summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine
diff options
context:
space:
mode:
author Angelo Salese <angelosa@users.noreply.github.com>2022-05-23 17:27:33 +0200
committer GitHub <noreply@github.com>2022-05-23 17:27:33 +0200
commite6180e3510bef6ea8ffb6abddf64d7d3006829ed (patch)
treebc5df1fb8bc2eaf5cff54675f7f60fadc59087a8 /src/devices/machine
parent09062b24a3be57139beaf823af3d7c5532ab256f (diff)
Initial work towards a sis630 based chipset driver (#9635)
- Add more or less complete implementations of sis630_host, sis950_lpc, sis630_gui, sis5513_ide, sis7001_usb, sis7018_audio, sis900_eth devices; - Removed gamecstl.cpp in favour to the new sis630.cpp driver; - i386.cpp: enable CMOV feature for Pentium III and 4; - i386.cpp: add PSN stub feature for Pentium III; New machines marked as NOT_WORKING ---------------------------------- Shuttle MS-11 [Angelo Salese, archive.org]
Diffstat (limited to 'src/devices/machine')
-rw-r--r--src/devices/machine/pci.cpp22
-rw-r--r--src/devices/machine/pci.h2
-rw-r--r--src/devices/machine/sis5513_ide.cpp363
-rw-r--r--src/devices/machine/sis5513_ide.h89
-rw-r--r--src/devices/machine/sis630_gui.cpp709
-rw-r--r--src/devices/machine/sis630_gui.h144
-rw-r--r--src/devices/machine/sis630_host.cpp441
-rw-r--r--src/devices/machine/sis630_host.h98
-rw-r--r--src/devices/machine/sis7001_usb.cpp93
-rw-r--r--src/devices/machine/sis7001_usb.h53
-rw-r--r--src/devices/machine/sis7018_audio.cpp126
-rw-r--r--src/devices/machine/sis7018_audio.h46
-rw-r--r--src/devices/machine/sis900_eth.cpp161
-rw-r--r--src/devices/machine/sis900_eth.h53
-rw-r--r--src/devices/machine/sis950_lpc.cpp764
-rw-r--r--src/devices/machine/sis950_lpc.h176
-rw-r--r--src/devices/machine/sis950_smbus.cpp108
-rw-r--r--src/devices/machine/sis950_smbus.h32
18 files changed, 3479 insertions, 1 deletions
diff --git a/src/devices/machine/pci.cpp b/src/devices/machine/pci.cpp
index 2620f1fc642..993d45efed4 100644
--- a/src/devices/machine/pci.cpp
+++ b/src/devices/machine/pci.cpp
@@ -1,5 +1,10 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
+/*
+ * References:
+ * - PCI local bus (rev 2.x)
+ * - https://wiki.osdev.org/PCI
+ */
#include "emu.h"
#include "pci.h"
@@ -87,6 +92,12 @@ pci_device::pci_device(const machine_config &mconfig, device_type type, const ch
}
}
+// main_id << 16 = vendor ID ($00-$01)
+// main_id & 0xffff = device ID ($02-$03)
+// revision = board versioning ($08)
+// pclass = programming interface/sub class code/base class code ($09-$0b)
+// subsystem_id << 16 = sub vendor ID ($2c-$2d) - NB: not all cards have these
+// subsystem_id & 0xffff = sub device ID ($2e-$2f) /
void pci_device::set_ids(uint32_t _main_id, uint8_t _revision, uint32_t _pclass, uint32_t _subsystem_id)
{
main_id = _main_id;
@@ -257,6 +268,17 @@ void pci_device::expansion_base_w(offs_t offset, uint32_t data, uint32_t mem_mas
remap_cb();
}
+// if non-zero a CAPability PoinTeR marks an offset in PCI config space where a standard extension is located
+// For example if capptr_r is 0xc0 then [offset+0xc0] has a capability identifier that is set with:
+// bits 31-16 <capability dependant, usually revision and supported sub-features>
+// bits 15-8 next capptr offset, 0x00 to determine the given item as last
+// bits 7-0 capability ID:
+// - 0x01 PMI Power Management Interface
+// - 0x02 AGP Accelerated Graphics Port
+// - 0x03 VPD Vital Product Data
+// - 0x04 Slot Identification
+// - 0x05 MSI Message Signaled Interrupts
+// - 0x06 CompactPCI Hot Swap
uint8_t pci_device::capptr_r()
{
return 0x00;
diff --git a/src/devices/machine/pci.h b/src/devices/machine/pci.h
index 0a0a570b451..c8ed2e84c18 100644
--- a/src/devices/machine/pci.h
+++ b/src/devices/machine/pci.h
@@ -193,7 +193,7 @@ public:
uint16_t iolimitu_r();
void iolimitu_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
uint16_t bridge_control_r();
- void bridge_control_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
+ virtual void bridge_control_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0);
protected:
enum
diff --git a/src/devices/machine/sis5513_ide.cpp b/src/devices/machine/sis5513_ide.cpp
new file mode 100644
index 00000000000..503751c6388
--- /dev/null
+++ b/src/devices/machine/sis5513_ide.cpp
@@ -0,0 +1,363 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+/**************************************************************************************************
+
+ SiS 5513 IDE controller
+
+ TODO:
+ - Derive from common pci-ide.cpp interface
+ (what flavour that emulates tho? PCI regs 0x40-0x52 don't match)
+
+**************************************************************************************************/
+
+#include "emu.h"
+#include "sis5513_ide.h"
+
+#define LOG_IO (1U << 1) // log PCI register accesses
+#define LOG_TODO (1U << 2) // log unimplemented registers
+#define LOG_MAP (1U << 3) // log full remaps
+
+#define VERBOSE (LOG_GENERAL | LOG_IO | LOG_TODO | LOG_MAP)
+//#define LOG_OUTPUT_FUNC osd_printf_warning
+
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMAP(...) LOGMASKED(LOG_MAP, __VA_ARGS__)
+#define LOGTODO(...) LOGMASKED(LOG_TODO, __VA_ARGS__)
+
+DEFINE_DEVICE_TYPE(SIS5513_IDE, sis5513_ide_device, "sis5513_ide", "SiS 5513 IDE Controller")
+
+sis5513_ide_device::sis5513_ide_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : pci_device(mconfig, SIS5513_IDE, tag, owner, clock)
+ , m_ide1(*this, "ide1")
+ , m_ide2(*this, "ide2")
+ , m_irq_pri_callback(*this)
+ , m_irq_sec_callback(*this)
+ , m_bus_master_space(*this, finder_base::DUMMY_TAG, AS_PROGRAM)
+{
+}
+
+void sis5513_ide_device::device_add_mconfig(machine_config &config)
+{
+ BUS_MASTER_IDE_CONTROLLER(config, m_ide1).options(ata_devices, "hdd", nullptr, false);
+ m_ide1->irq_handler().set([this](int state) { m_irq_pri_callback(state); });
+ m_ide1->set_bus_master_space(m_bus_master_space);
+
+ BUS_MASTER_IDE_CONTROLLER(config, m_ide2).options(ata_devices, "cdrom", nullptr, false);
+ m_ide2->irq_handler().set([this](int state) { m_irq_sec_callback(state); });
+ m_ide2->set_bus_master_space(m_bus_master_space);
+}
+
+void sis5513_ide_device::config_map(address_map &map)
+{
+ pci_device::config_map(map);
+// map(0x10, 0x4f).unmaprw();
+ map(0x09, 0x09).w(FUNC(sis5513_ide_device::prog_if_w));
+ // base I/O relocation (effective only when native mode is on)
+// map(0x10, 0x13) Primary channel command base
+// map(0x14, 0x17) Primary channel control base
+// map(0x18, 0x1b) Secondary channel command base
+// map(0x1c, 0x1f) Secondary channel control base
+
+// map(0x20, 0x23) Bus master IDE control register base (0x10 I/O regs)
+ map(0x10, 0x23).rw(FUNC(sis5513_ide_device::bar_r), FUNC(sis5513_ide_device::bar_w));
+ map(0x24, 0x27).unmaprw();
+ map(0x40, 0x52).rw(FUNC(sis5513_ide_device::unmap_log_r), FUNC(sis5513_ide_device::unmap_log_w));
+ map(0x4a, 0x4a).rw(FUNC(sis5513_ide_device::ide_ctrl_0_r), FUNC(sis5513_ide_device::ide_ctrl_0_w));
+ map(0x52, 0x52).rw(FUNC(sis5513_ide_device::ide_misc_ctrl_r), FUNC(sis5513_ide_device::ide_misc_ctrl_w));
+
+
+// map(0x2c, 0x2f) subsystem ID (written once)
+
+// map(0x3c, 0x3d) interrupt line/pin
+
+// map(0x40, 0x40) IDE Primary channel master data recovery time
+// map(0x41, 0x41) IDE Primary channel master data active time (Ultra DMA)
+// map(0x42, 0x42) IDE Primary channel slave data recovery time
+// map(0x43, 0x43) IDE Primary channel slave data active time (Ultra DMA)
+// map(0x44, 0x47) ^ Same for IDE Secondary
+
+// map(0x48, 0x48) IDE status
+// map(0x4a, 0x4b) IDE general control regs
+
+// map(0x4c, 0x4d) prefetch count of primary channel
+// map(0x4e, 0x4f) prefetch count of secondary channel
+
+// map(0x52, 0x52) IDE misc control regs
+}
+
+#if 0
+void sis5513_ide_device::compatible_io_map(address_map &map)
+{
+ map(0x0170, 0x0177).rw(FUNC(sis5513_ide_device::ide2_read32_cs0_r), FUNC(sis5513_ide_device::ide2_write32_cs0_w));
+ map(0x01f0, 0x01f7).rw(FUNC(sis5513_ide_device::ide1_read32_cs0_r), FUNC(sis5513_ide_device::ide1_write32_cs0_w));
+ map(0x0376, 0x0376).rw(FUNC(sis5513_ide_device::ide2_read_cs1_r), FUNC(sis5513_ide_device::ide2_write_cs1_w));
+ map(0x03f6, 0x03f6).rw(FUNC(sis5513_ide_device::ide1_read_cs1_r), FUNC(sis5513_ide_device::ide1_write_cs1_w));
+}
+#endif
+
+// $1f0
+void sis5513_ide_device::ide1_command_map(address_map &map)
+{
+ map(0, 7).rw(FUNC(sis5513_ide_device::ide1_read32_cs0_r), FUNC(sis5513_ide_device::ide1_write32_cs0_w));
+}
+
+// $3f4
+void sis5513_ide_device::ide1_control_map(address_map &map)
+{
+ map(2, 2).rw(FUNC(sis5513_ide_device::ide1_read_cs1_r), FUNC(sis5513_ide_device::ide1_write_cs1_w));
+}
+
+// $170
+void sis5513_ide_device::ide2_command_map(address_map &map)
+{
+ map(0, 7).rw(FUNC(sis5513_ide_device::ide2_read32_cs0_r), FUNC(sis5513_ide_device::ide2_write32_cs0_w));
+}
+
+// $374
+void sis5513_ide_device::ide2_control_map(address_map &map)
+{
+ map(2, 2).rw(FUNC(sis5513_ide_device::ide2_read_cs1_r), FUNC(sis5513_ide_device::ide2_write_cs1_w));
+}
+
+void sis5513_ide_device::bus_master_ide_control_map(address_map &map)
+{
+ map(0x0, 0x7).rw(m_ide1, FUNC(bus_master_ide_controller_device::bmdma_r), FUNC(bus_master_ide_controller_device::bmdma_w));
+ map(0x8, 0xf).rw(m_ide2, FUNC(bus_master_ide_controller_device::bmdma_r), FUNC(bus_master_ide_controller_device::bmdma_w));
+}
+
+void sis5513_ide_device::device_start()
+{
+ pci_device::device_start();
+
+ m_irq_pri_callback.resolve();
+ m_irq_sec_callback.resolve();
+
+ add_map(8, M_IO, FUNC(sis5513_ide_device::ide1_command_map));
+ add_map(4, M_IO, FUNC(sis5513_ide_device::ide1_control_map));
+ add_map(8, M_IO, FUNC(sis5513_ide_device::ide2_command_map));
+ add_map(4, M_IO, FUNC(sis5513_ide_device::ide2_control_map));
+ add_map(16, M_IO, FUNC(sis5513_ide_device::bus_master_ide_control_map));
+}
+
+
+void sis5513_ide_device::device_reset()
+{
+ pci_device::device_reset();
+
+ command = 0x0000;
+ status = 0x0000;
+ pclass = 0x01018a;
+ m_ide_ctrl0 = 0;
+ m_ide_misc = 0;
+ m_bar[0] = 0x1f0;
+ m_bar[1] = 0x3f4;
+ m_bar[2] = 0x170;
+ m_bar[3] = 0x374;
+ m_bar[4] = 0xf00;
+ for (int i = 0; i < 5; i++)
+ bank_infos[i].adr = m_bar[i];
+ remap_cb();
+}
+
+inline bool sis5513_ide_device::ide1_mode()
+{
+ return (pclass & 0x3) == 3;
+}
+
+inline bool sis5513_ide_device::ide2_mode()
+{
+ return (pclass & 0xc) == 0xc;
+}
+
+// In compatible mode BARs with legacy addresses but can values written can still be readout.
+// In practice we need to override writes and make sure we flush remapping accordingly
+inline void sis5513_ide_device::flush_ide_mode()
+{
+ // Map Primary IDE Channel
+ if (ide1_mode())
+ {
+ // PCI Mode
+ pci_device::address_base_w(0, m_bar[0]);
+ pci_device::address_base_w(1, m_bar[1]);
+ }
+ else
+ {
+ // Legacy Mode
+ pci_device::address_base_w(0, 0x1f0);
+ pci_device::address_base_w(1, 0x3f4);
+ }
+
+ // Map Secondary IDE Channel
+ if (ide2_mode())
+ {
+ // PCI Mode
+ pci_device::address_base_w(2, m_bar[2]);
+ pci_device::address_base_w(3, m_bar[3]);
+ }
+ else
+ {
+ // Legacy Mode
+ pci_device::address_base_w(2, 0x170);
+ pci_device::address_base_w(3, 0x374);
+ }
+}
+
+void sis5513_ide_device::prog_if_w(u8 data)
+{
+ uint32_t oldVal = pclass;
+ pclass = (pclass & ~(0xff)) | (data & 0xff);
+ // Check for switch to/from compatibility (legacy) mode from/to pci mode
+ if ((oldVal ^ pclass) & 0xf)
+ flush_ide_mode();
+}
+
+u32 sis5513_ide_device::bar_r(offs_t offset)
+{
+ if (bank_reg_infos[offset].bank == -1)
+ return 0;
+ int bid = bank_reg_infos[offset].bank;
+ if (bank_reg_infos[offset].hi)
+ return bank_infos[bid].adr >> 32;
+ int flags = bank_infos[bid].flags;
+ return (m_bar[offset] & ~(bank_infos[bid].size - 1)) | (flags & M_IO ? 1 : 0) | (flags & M_64A ? 4 : 0) | (flags & M_PREF ? 8 : 0);
+}
+
+void sis5513_ide_device::bar_w(offs_t offset, u32 data)
+{
+ m_bar[offset] = data;
+ // Bits 0 (primary) and 2 (secondary) control if the mapping is legacy or BAR
+ switch (offset) {
+ case 0 ... 1:
+ if (ide1_mode())
+ pci_device::address_base_w(offset, data);
+ break;
+ case 2 ... 3:
+ if (ide2_mode())
+ pci_device::address_base_w(offset, data);
+ break;
+ default:
+ // Only the first 4 bars are controlled by pif
+ pci_device::address_base_w(offset, data);
+ }
+ logerror("Mapping bar[%i] = %08x\n", offset, data);
+}
+
+u8 sis5513_ide_device::ide_ctrl_0_r()
+{
+ LOGIO("IDE ctrl 0 read [$4a] %02x\n", m_ide_ctrl0);
+ return m_ide_ctrl0;
+}
+
+void sis5513_ide_device::ide_ctrl_0_w(u8 data)
+{
+ LOGIO("IDE ctrl 0 write [$4a] %02x\n", data);
+ m_ide_ctrl0 = data;
+ // TODO: bit 1 disables IDE ch. 0, bit 2 ch. 1
+// remap_cb();
+}
+
+u8 sis5513_ide_device::ide_misc_ctrl_r()
+{
+ LOGIO("IDE misc ctrl read [$52] %02x\n", m_ide_misc);
+ return m_ide_misc;
+}
+
+void sis5513_ide_device::ide_misc_ctrl_w(u8 data)
+{
+ LOGIO("IDE misc ctrl write [$52] %02x\n", data);
+ m_ide_misc = data;
+
+ const bool compatible_mode = BIT(m_ide_misc, 2);
+ pclass &= 0xffff85;
+
+ if (compatible_mode)
+ {
+ //LOGMAP("- Compatible Mode\n");
+ intr_pin = 0;
+ }
+ else
+ {
+ // Native Mode
+ pclass |= 0xa;
+ intr_pin = 1;
+ }
+
+ flush_ide_mode();
+}
+
+/*
+ * Debugging
+ */
+
+u8 sis5513_ide_device::unmap_log_r(offs_t offset)
+{
+ LOGTODO("IDE Unemulated [%02x] R\n", offset + 0x40);
+ return 0;
+}
+
+void sis5513_ide_device::unmap_log_w(offs_t offset, u8 data)
+{
+ LOGTODO("IDE Unemulated [%02x] %02x W\n", offset + 0x40, data);
+}
+
+/*
+ * Start of legacy handling, to be moved out
+ */
+
+uint32_t sis5513_ide_device::ide1_read32_cs0_r(offs_t offset, uint32_t mem_mask)
+{
+ if (!(command & 1))
+ return 0xffffffff;
+ return m_ide1->read_cs0(offset, mem_mask);
+}
+
+void sis5513_ide_device::ide1_write32_cs0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ if (!(command & 1))
+ return;
+ m_ide1->write_cs0(offset, data, mem_mask);
+}
+
+uint32_t sis5513_ide_device::ide2_read32_cs0_r(offs_t offset, uint32_t mem_mask)
+{
+ if (!(command & 1))
+ return 0xffffffff;
+ return m_ide2->read_cs0(offset, mem_mask);
+}
+
+void sis5513_ide_device::ide2_write32_cs0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ if (!(command & 1))
+ return;
+ m_ide2->write_cs0(offset, data, mem_mask);
+}
+
+uint8_t sis5513_ide_device::ide1_read_cs1_r()
+{
+ if (!(command & 1))
+ return 0xff;
+ return m_ide1->read_cs1(1, 0xff0000) >> 16;
+}
+
+void sis5513_ide_device::ide1_write_cs1_w(uint8_t data)
+{
+ if (!(command & 1))
+ return;
+ m_ide1->write_cs1(1, data << 16, 0xff0000);
+}
+
+uint8_t sis5513_ide_device::ide2_read_cs1_r()
+{
+ if (!(command & 1))
+ return 0xff;
+ return m_ide2->read_cs1(1, 0xff0000) >> 16;
+}
+
+void sis5513_ide_device::ide2_write_cs1_w(uint8_t data)
+{
+ if (!(command & 1))
+ return;
+ m_ide2->write_cs1(1, data << 16, 0xff0000);
+}
diff --git a/src/devices/machine/sis5513_ide.h b/src/devices/machine/sis5513_ide.h
new file mode 100644
index 00000000000..89d4916c47b
--- /dev/null
+++ b/src/devices/machine/sis5513_ide.h
@@ -0,0 +1,89 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+
+#ifndef MAME_MACHINE_SIS5513_IDE_H
+#define MAME_MACHINE_SIS5513_IDE_H
+
+#pragma once
+
+#include "pci.h"
+#include "machine/pci-ide.h"
+
+class sis5513_ide_device : public pci_device
+{
+public:
+ template <typename T> sis5513_ide_device(
+ const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock,
+ T &&host_tag, uint32_t bmspace = AS_PROGRAM
+ ) : sis5513_ide_device(mconfig, tag, owner, clock)
+ {
+ // IDE controller with 0xd0 as programming i/f "ATA Host Adapters standard"
+ // pclass bits 1-3 are actually 1 when controller is in native mode
+ // pclass bits 0-2 can be r/w from $09
+ set_ids(0x10395513, 0xd0, 0x010180, 0x00);
+ m_bus_master_space.set_tag(host_tag, bmspace);
+ }
+ sis5513_ide_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ auto irq_pri() { return m_irq_pri_callback.bind(); }
+ auto irq_sec() { return m_irq_sec_callback.bind(); }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+// virtual void reset_all_mappings() override;
+
+// virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+// uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;
+
+ virtual void config_map(address_map &map) override;
+
+ void ide1_command_map(address_map &map);
+ void ide1_control_map(address_map &map);
+ void ide2_command_map(address_map &map);
+ void ide2_control_map(address_map &map);
+ void bus_master_ide_control_map(address_map &map);
+private:
+ required_device<bus_master_ide_controller_device> m_ide1;
+ required_device<bus_master_ide_controller_device> m_ide2;
+ devcb_write_line m_irq_pri_callback;
+ devcb_write_line m_irq_sec_callback;
+ required_address_space m_bus_master_space;
+
+ bool ide1_mode();
+ bool ide2_mode();
+
+ u32 bar_r(offs_t offset);
+ void bar_w(offs_t offset, u32 data);
+ u32 m_bar[5]{};
+
+ void prog_if_w(u8 data);
+ u8 ide_ctrl_0_r();
+ void ide_ctrl_0_w(u8 data);
+ u8 ide_misc_ctrl_r();
+ void ide_misc_ctrl_w(u8 data);
+
+ u8 m_ide_ctrl0 = 0;
+ u8 m_ide_misc = 0;
+
+ uint32_t ide1_read32_cs0_r(offs_t offset, uint32_t mem_mask = ~0);
+ void ide1_write32_cs0_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+ uint32_t ide2_read32_cs0_r(offs_t offset, uint32_t mem_mask = ~0);
+ void ide2_write32_cs0_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+ uint8_t ide1_read_cs1_r();
+ void ide1_write_cs1_w(uint8_t data);
+ uint8_t ide2_read_cs1_r();
+ void ide2_write_cs1_w(uint8_t data);
+
+// void compatible_io_map(address_map &map);
+ void flush_ide_mode();
+
+ u8 unmap_log_r(offs_t offset);
+ void unmap_log_w(offs_t offset, u8 data);
+};
+
+DECLARE_DEVICE_TYPE(SIS5513_IDE, sis5513_ide_device)
+
+#endif
diff --git a/src/devices/machine/sis630_gui.cpp b/src/devices/machine/sis630_gui.cpp
new file mode 100644
index 00000000000..be5d6030612
--- /dev/null
+++ b/src/devices/machine/sis630_gui.cpp
@@ -0,0 +1,709 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+/**************************************************************************************************
+
+ SiS 630 Video GUI portion (SVGA-based) & 301 video bridge
+
+ - 630 core is SVGA based:
+ \- has two sets of extended CRTC ($3c4) regs;
+ \- a dedicated MPEG-2 video playback interface;
+ \- a digital video interface to 301;
+ - 301 draws to a separate monitor, and it was originally tied to a SiS300 AGP card,
+ (which we don't have a dump of at the time of this writing):
+ \- can select VGA, NTSC, PAL or LCD sources;
+ \- Has separate set of VGA and RAMDAC regs;
+ \- Has TV encoder;
+ \- Has macrovision regs;
+ - GUI is the 630 PCI/AGP i/f
+ \- it's actually internal to the rest of 630;
+ \- 301 is external but closely tied to it: the digital i/f ports (RIO+$4) selects where it
+ should start drawing/sync etc. while the "VGA2 regs" (RIO+$14) seems to be a custom set
+ rather than be related at all (i.e. it most likely be just capable to have VGA-like
+ resolutions).
+ - sis_main.c portions refers to the correlated Linux driver at
+ https://github.com/torvalds/linux/blob/master/drivers/video/fbdev/sis/sis_main.c
+
+ TODO:
+ - Backward port '630 GUI/PCI implementation to '300 and other flavours
+ (needs VGA mods to do this properly);
+ - 2d acceleration;
+ - Turbo queue stuff;
+ - AGP;
+ - interlace (cfr. xubuntu 6.10 splash screen on 1024x768x32);
+ - xubuntu 6.10 splash screen is decentered (zooming?)
+ - xubuntu 6.10 splash screen text is unreadable or not visible depending on res selected;
+ - Interface with '301 bridge (a lovely can of worms);
+
+**************************************************************************************************/
+
+#include "emu.h"
+#include "sis630_gui.h"
+
+#define LOG_IO (1U << 1) // log PCI register accesses
+#define LOG_TODO (1U << 2) // log unimplemented registers
+#define LOG_MAP (1U << 3) // log full remaps
+#define LOG_AGP (1U << 4) // log AGP
+#define LOG_SVGA (1U << 5) // log SVGA
+
+#define VERBOSE (LOG_GENERAL | LOG_IO | LOG_TODO | LOG_MAP | LOG_AGP | LOG_SVGA)
+//#define LOG_OUTPUT_FUNC osd_printf_warning
+
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMAP(...) LOGMASKED(LOG_MAP, __VA_ARGS__)
+#define LOGTODO(...) LOGMASKED(LOG_TODO, __VA_ARGS__)
+#define LOGAGP(...) LOGMASKED(LOG_AGP, __VA_ARGS__)
+#define LOGSVGA(...) LOGMASKED(LOG_SVGA, __VA_ARGS__)
+
+/**************************
+ *
+ * SVGA implementation
+ *
+ *************************/
+
+// TODO: later variant of 5598
+// (definitely doesn't have dual segment mode for instance)
+DEFINE_DEVICE_TYPE(SIS630_SVGA, sis630_svga_device, "sis630_svga", "SiS 630 SVGA")
+
+sis630_svga_device::sis630_svga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : svga_device(mconfig, SIS630_SVGA, tag, owner, clock)
+{
+
+}
+
+void sis630_svga_device::device_start()
+{
+ svga_device::device_start();
+ zero();
+
+ // Avoid an infinite loop when displaying. 0 is not possible anyway.
+ vga.crtc.maximum_scan_line = 1;
+
+ // copy over interfaces
+ vga.read_dipswitch.set(nullptr); //read_dipswitch;
+ vga.svga_intf.seq_regcount = 0x05;
+ vga.svga_intf.crtc_regcount = 0x27;
+ vga.svga_intf.vram_size = 64*1024*1024;
+ //vga.memory = std::make_unique<uint8_t []>(vga.svga_intf.vram_size);
+}
+
+void sis630_svga_device::device_reset()
+{
+ svga_device::device_reset();
+
+ m_svga_bank_reg_w = m_svga_bank_reg_r = 0;
+ m_unlock_reg = false;
+ //m_dual_seg_mode = false;
+}
+
+// Page 144
+uint8_t sis630_svga_device::crtc_reg_read(uint8_t index)
+{
+ if (index < 0x19)
+ return svga_device::crtc_reg_read(index);
+
+ // make sure '301 CRT2 is not enabled
+ if (index == 0x30)
+ return 0;
+
+ if (index == 0x31)
+ return 0x60;
+
+ if (index == 0x32)
+ return 0x20;
+
+ // TODO: if one of these is 0xff then it enables a single port transfer to $b8000
+ return m_crtc_ext_regs[index];
+}
+
+void sis630_svga_device::crtc_reg_write(uint8_t index, uint8_t data)
+{
+ if (index < 0x19)
+ svga_device::crtc_reg_write(index, data);
+ else
+ {
+ m_crtc_ext_regs[index] = data;
+ }
+}
+
+uint8_t sis630_svga_device::seq_reg_read(uint8_t index)
+{
+ switch (index)
+ {
+ // extended id register
+ case 0x05:
+ return m_unlock_reg ? 0xa1 : 0x21;
+ case 0x06:
+ return m_ramdac_mode;
+ case 0x07:
+ return m_ext_misc_ctrl_0;
+ case 0x0a:
+ return m_ext_vert_overflow;
+ case 0x0b ... 0x0c:
+ return m_ext_horz_overflow[index - 0xb];
+ case 0x0d:
+ return vga.crtc.start_addr_latch >> 16;
+ case 0x14:
+ // sis_main.c calculates VRAM size in two ways:
+ // 1. the legacy way ('300), by probing this register
+ // 2. by reading '630 PCI host register $63 (as shared DRAM?)
+ // Method 1 seems enough to enforce "64MB" message at POST,
+ // 2 is probably more correct but unsure about how to change the shared area in BIOS
+ // (shutms11 will always write a "0x41" on fresh CMOS then a "0x47"
+ // on successive boots no matter what)
+ const u8 bus_width = m_seq_ext_regs[index] & 0xc0;
+ return (bus_width) | ((vga.svga_intf.vram_size / (1024 * 1024) - 1) & 0x3f);
+ }
+
+ //LOGSVGA("Unemulated index R [%02x]\n", index);
+ return m_seq_ext_regs[index];
+}
+
+std::tuple<u8, u8> sis630_svga_device::flush_true_color_mode()
+{
+ // punt if extended or true color is off
+ if ((m_ramdac_mode & 0x12) != 0x12)
+ return std::make_tuple(0, 0);
+
+ const u8 res = (m_ext_misc_ctrl_0 & 4) >> 2;
+
+ return std::make_tuple(res, res ^ 1);
+}
+
+void sis630_svga_device::recompute_params()
+{
+ // TODO: ext clock
+ recompute_params_clock(1, XTAL(25'174'800).value());
+}
+
+
+void sis630_svga_device::seq_reg_write(uint8_t index, uint8_t data)
+{
+ if (index < vga.svga_intf.seq_regcount)
+ svga_device::seq_reg_write(index, data);
+ else
+ {
+ if (index == 0x05)
+ {
+ m_unlock_reg = (data == 0x86);
+ LOGSVGA("Unlock register write %02x (%s)\n", data, m_unlock_reg ? "unlocked" : "locked");
+ }
+ else
+ {
+ if (!m_unlock_reg)
+ {
+ LOGSVGA("Attempt to write to extended SVGA while locked [$%02x] -> %02x\n", index, data);
+ return;
+ }
+
+ m_seq_ext_regs[index] = data;
+
+ switch(index)
+ {
+ /*
+ * x--- ---- GFX mode linear addressing enable
+ * -x-- ---- GFX hardware cursor display
+ * --x- ---- GFX mode interlace
+ * ---x ---- True Color enable (ties with index 0x07 bit 2)
+ * ---- x--- RGB16 enable
+ * ---- -x-- RGB15 enable
+ * ---- --x- enhanced GFX mode enable
+ * ---- ---x enhanced text mode enable
+ */
+ case 0x06:
+ m_ramdac_mode = data;
+ LOGSVGA("RAMDAC mode %02x\n", data);
+
+ if (!BIT(data, 1))
+ {
+ svga.rgb8_en = svga.rgb15_en = svga.rgb16_en = svga.rgb24_en = svga.rgb32_en = 0;
+ }
+ else
+ {
+ if (BIT(data, 2))
+ svga.rgb15_en = 1;
+ if (BIT(data, 3))
+ svga.rgb16_en = 1;
+ std::tie(svga.rgb24_en, svga.rgb32_en) = flush_true_color_mode();
+ }
+ break;
+ case 0x07:
+ LOGSVGA("Extended Misc. Control register 0 (%02x) %02x\n", index, data);
+ m_ext_misc_ctrl_0 = data;
+ std::tie(svga.rgb24_en, svga.rgb32_en) = flush_true_color_mode();
+ break;
+ case 0x0a:
+ LOGSVGA("Extended vertical Overflow register (%02x) %02x\n", index, data);
+ m_ext_vert_overflow = data;
+ vga.crtc.vert_retrace_end = (vga.crtc.vert_retrace_end & 0xf) | ((data & 0x20) >> 1);
+ vga.crtc.vert_blank_end = (vga.crtc.vert_blank_end & 0x00ff) | ((data & 0x10) << 4);
+ vga.crtc.vert_retrace_start = (vga.crtc.vert_retrace_start & 0x03ff) | ((data & 0x08) << 7);
+ vga.crtc.vert_blank_start = (vga.crtc.vert_blank_start & 0x03ff) | ((data & 0x04) << 8);
+ vga.crtc.vert_disp_end = (vga.crtc.vert_disp_end & 0x03ff) | ((data & 0x02) << 9);
+ vga.crtc.vert_total = (vga.crtc.vert_total & 0x03ff) | ((data & 0x01) << 10);
+ recompute_params();
+ break;
+ case 0x0b:
+ //m_dual_seg_mode = bool(BIT(data, 3));
+ LOGSVGA("Extended horizontal Overflow 1 (%02x) %02x\n", index, data);
+ m_ext_horz_overflow[0] = data;
+
+ vga.crtc.horz_retrace_start = (vga.crtc.horz_retrace_start & 0x00ff) | ((data & 0xc0) << 2);
+ vga.crtc.horz_blank_start = (vga.crtc.horz_blank_start & 0x00ff) | ((data & 0x30) << 4);
+ vga.crtc.horz_disp_end = (vga.crtc.horz_disp_end & 0x00ff) | ((data & 0x0c) << 6);
+ vga.crtc.horz_total = (vga.crtc.horz_total & 0x00ff) | ((data & 0x03) << 8);
+
+ recompute_params();
+ break;
+ case 0x0c:
+ LOGSVGA("Extended horizontal Overflow 2 (%02x) %02x\n", index, data);
+ m_ext_horz_overflow[1] = data;
+
+ vga.crtc.horz_retrace_end = (vga.crtc.horz_retrace_end & 0x001f) | ((data & 0x04) << 3);
+ vga.crtc.horz_blank_end = (vga.crtc.horz_blank_end & 0x003f) | ((data & 0x03) << 6);
+ recompute_params();
+ break;
+ case 0x0d:
+ LOGSVGA("Extended starting address register (%02x) %02x\n", index, data);
+ vga.crtc.start_addr_latch &= ~0xff0000;
+ vga.crtc.start_addr_latch |= data << 16;
+ break;
+ case 0x0e:
+ LOGSVGA("Extended pitch register (%02x) %02x\n", index, data);
+ // sis_main.c implicitly sets this with bits 0-3 granularity, assume being right
+ vga.crtc.offset = (vga.crtc.offset & 0x00ff) | ((data & 0x0f) << 8);
+ break;
+ case 0x1e:
+ if (data & 0x40)
+ popmessage("Warning: enable 2d engine");
+ break;
+ case 0x20:
+ if (data & 0x81)
+ popmessage("Warning: %s %s", BIT(data, 7) ? "PCI address enabled" : "", BIT(data, 0) ? "memory map I/O enable" : "");
+ break;
+ default:
+ LOGSVGA("Extended write %02x %02x\n", index, data);
+ break;
+ }
+ }
+ }
+}
+
+uint16_t sis630_svga_device::offset()
+{
+ if (svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en || svga.rgb32_en)
+ return vga.crtc.offset << 3;
+ return svga_device::offset();
+}
+
+// read by gamecstl Kontron BIOS
+u8 sis630_svga_device::port_03c0_r(offs_t offset)
+{
+ if (offset == 0xd)
+ return m_svga_bank_reg_w;
+ if (offset == 0xb)
+ return m_svga_bank_reg_r;
+
+ return svga_device::port_03c0_r(offset);
+}
+
+void sis630_svga_device::port_03c0_w(offs_t offset, uint8_t data)
+{
+ // TODO: for '630 it's always with dual segment enabled?
+
+ if (offset == 0xd)
+ {
+ //if (m_dual_seg_mode)
+ m_svga_bank_reg_w = (data & 0x3f) * 0x10000;
+ //else
+ {
+ // m_svga_bank_reg_w = (data >> 4) * 0x10000;
+ // m_svga_bank_reg_r = (data & 0xf) * 0x10000;
+ }
+ return;
+ }
+
+ if (offset == 0xb)
+ {
+ //if (m_dual_seg_mode)
+ m_svga_bank_reg_r = (data & 0x3f) * 0x10000;
+ // otherwise ignored if dual segment mode disabled
+ return;
+ }
+
+ svga_device::port_03c0_w(offset, data);
+}
+
+uint8_t sis630_svga_device::mem_r(offs_t offset)
+{
+ if (svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en || svga.rgb32_en)
+ return svga_device::mem_linear_r(offset + m_svga_bank_reg_r);
+ return svga_device::mem_r(offset);
+}
+
+void sis630_svga_device::mem_w(offs_t offset, uint8_t data)
+{
+ if (svga.rgb8_en || svga.rgb15_en || svga.rgb16_en || svga.rgb24_en || svga.rgb32_en)
+ {
+ svga_device::mem_linear_w(offset + m_svga_bank_reg_w, data);
+ return;
+ }
+ svga_device::mem_w(offset, data);
+}
+
+/*****************************
+ *
+ * 630 GUI PCI implementation
+ *
+ ****************************/
+
+DEFINE_DEVICE_TYPE(SIS630_GUI, sis630_gui_device, "sis630_gui", "SiS 630 GUI")
+
+sis630_gui_device::sis630_gui_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : pci_device(mconfig, SIS630_GUI, tag, owner, clock)
+ , m_svga(*this, "svga")
+ , m_gui_rom(*this, "gui_rom")
+{
+ set_ids(0x10396300, 0x00, 0x030000, 0x00);
+}
+
+ROM_START( sis630gui )
+ ROM_REGION32_LE( 0xc000, "gui_rom", ROMREGION_ERASEFF )
+ // TODO: why the OEM ROM is 0xc000 in size?
+ // 0x8000-0xbfff mostly contains a charset, may be either programmable via a dedicated interface
+ // or the dump above is half size.
+ // gamecstl dump ver. 2.06.50
+ // (which actually writes to VRAM with the actual expansion ROM enabled, uh?)
+ ROM_SYSTEM_BIOS( 0, "2.06.50", "Ver. 2.06.50 OEM" )
+ ROMX_LOAD( "oemrom.bin", 0x0000, 0xc000, BAD_DUMP CRC(03d8df9d) SHA1(8fb80a2bf4067d9bebc90fb498448869ae795b2b), ROM_BIOS(0) )
+
+ // "SiS 630 (Ver. 2.02.1c) [AGP VGA] (Silicon Integrated Systems Corp.).bin"
+ ROM_SYSTEM_BIOS( 1, "2.02.1c", "Ver. 2.02.1c" )
+ ROMX_LOAD( "sis630.bin", 0x0000, 0x8000, BAD_DUMP CRC(f04ef9b0) SHA1(2396a79cd4045362bfc511090b146daa85902b4d), ROM_BIOS(1) )
+ROM_END
+
+const tiny_rom_entry *sis630_gui_device::device_rom_region() const
+{
+ return ROM_NAME(sis630gui);
+}
+
+void sis630_gui_device::device_add_mconfig(machine_config &config)
+{
+ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
+ screen.set_raw(XTAL(25'174'800), 900, 0, 640, 526, 0, 480);
+ screen.set_screen_update(m_svga, FUNC(sis630_svga_device::screen_update));
+
+ SIS630_SVGA(config, m_svga, 0);
+ m_svga->set_screen("screen");
+ // 64MB according to POST
+ // documentation claims 128MB, assume being wrong
+ m_svga->set_vram_size(64*1024*1024);
+}
+
+void sis630_gui_device::config_map(address_map &map)
+{
+ pci_device::config_map(map);
+ map(0x2c, 0x2d).r(FUNC(sis630_gui_device::subvendor_r));
+ map(0x2e, 0x2f).r(FUNC(sis630_gui_device::subsystem_r));
+ map(0x2c, 0x2f).w(FUNC(sis630_gui_device::subvendor_w));
+
+// map(0x3c, 0x3d) irq line/pin
+
+ map(0x34, 0x34).r(FUNC(sis630_gui_device::capptr_r));
+
+ map(0x50, 0x53).r(FUNC(sis630_gui_device::agp_id_r));
+ map(0x54, 0x57).r(FUNC(sis630_gui_device::agp_status_r));
+ map(0x58, 0x5b).rw(FUNC(sis630_gui_device::agp_command_r), FUNC(sis630_gui_device::agp_command_w));
+ map(0x5c, 0x5c).lr8(NAME([] () { return 0; })); // NULL terminator
+}
+
+u8 sis630_gui_device::capptr_r()
+{
+ return 0x50;
+}
+
+// TODO: move to specific interface
+u32 sis630_gui_device::agp_id_r()
+{
+ LOGAGP("Read AGP ID [$50]\n");
+ // bits 23-16 AGP v1.0
+ // bits 15-8 0x5c NEXT_PTR (which goes to NULL terminator, heh)
+ // bits 7-0 CAP_ID (0x02 for AGP)
+ return 0x00105c02;
+}
+
+u32 sis630_gui_device::agp_status_r()
+{
+ LOGAGP("Read AGP status [$54]\n");
+ // RQ (1 + 1), 2X and 1X capable
+ return 0x01000003;
+}
+
+u32 sis630_gui_device::agp_command_r(offs_t offset, uint32_t mem_mask)
+{
+ LOGAGP("Read AGP command [$58] %d %d %08x\n", m_agp.enable, m_agp.data_rate, mem_mask);
+ // TODO: enable gets cleared by AGP_RESET, or even from PCI RST#
+ return m_agp.enable << 8 | (m_agp.data_rate & 7);
+}
+
+void sis630_gui_device::agp_command_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ LOGAGP("Write AGP command [$c8] %08x & %08x\n", data, mem_mask);
+
+ if (ACCESSING_BITS_8_15)
+ {
+ m_agp.enable = bool(BIT(m_agp.enable, 8));
+ LOGAGP("- AGP_ENABLE = %d\n", m_agp.enable);
+ }
+
+ if (ACCESSING_BITS_0_7)
+ {
+ // quick checker, to be translated into an AGP interface
+ std::map<u8, std::string> agp_transfer_rates = {
+ { 0, "(illegal 0)" },
+ { 1, "1X" },
+ { 2, "2X" },
+ { 3, "(illegal 3)" }
+ };
+
+ // make sure the AGP DATA_RATE specs are honored
+ const u8 data_rate = data & 3;
+ LOGAGP("- DATA_RATE = %s enabled=%d\n", agp_transfer_rates.at(data_rate), m_agp.enable);
+ m_agp.data_rate = data_rate;
+ }
+}
+
+// TODO: may be common to PCI base interface, verify
+void sis630_gui_device::subvendor_w(offs_t offset, u32 data, u32 mem_mask)
+{
+ // write once
+ if (m_subsystem_logger_mask & mem_mask)
+ {
+ LOG("Warning: subvendor ID possible rewrite! old=%08x & %08x data=%08x & %08x\n"
+ , subsystem_id
+ , m_subsystem_logger_mask
+ , data
+ , mem_mask
+ );
+ }
+ m_subsystem_logger_mask |= mem_mask;
+
+ COMBINE_DATA(&subsystem_id);
+ LOGIO("subsystem ID write [$2c] %08x & %08x (%08x)\n", data, mem_mask, subsystem_id);
+}
+
+void sis630_gui_device::memory_map(address_map &map)
+{
+ map(0x0000000, 0x3ffffff).rw(m_svga, FUNC(sis630_svga_device::mem_linear_r), FUNC(sis630_svga_device::mem_linear_w)).umask32(0xffffffff);
+}
+
+void sis630_gui_device::io_map(address_map &map)
+{
+
+}
+
+// "Relocate I/O" -> RIO
+void sis630_gui_device::space_io_map(address_map &map)
+{
+ // RIO + 0x00: video capture regs on '300, omitted or missing on '630
+ // RIO + 0x02: MPEG-2 video playback
+ // RIO + 0x04: digital video interface (to '301 only?)
+ // RIO + 0x10: 301 TV encoder
+ // RIO + 0x12: 301 macrovision regs
+ // RIO + 0x14: 301 VGA2 regs
+ // RIO + 0x16: 301 RAMDAC
+ // RIO + 0x30/+0x40/+0x50: omitted, legacy '300/'630 VGA regs?
+ // (gamecstl definitely tries to access 0x44 index 5 for readback extension ID)
+ map(0x30, 0x3f).rw(FUNC(sis630_gui_device::vga_3b0_r), FUNC(sis630_gui_device::vga_3b0_w));
+ map(0x40, 0x4f).rw(FUNC(sis630_gui_device::vga_3c0_r), FUNC(sis630_gui_device::vga_3c0_w));
+ map(0x50, 0x5f).rw(FUNC(sis630_gui_device::vga_3d0_r), FUNC(sis630_gui_device::vga_3d0_w));
+}
+
+void sis630_gui_device::legacy_memory_map(address_map &map)
+{
+ map(0xa0000, 0xbffff).rw(FUNC(sis630_gui_device::vram_r), FUNC(sis630_gui_device::vram_w));
+}
+
+void sis630_gui_device::legacy_io_map(address_map &map)
+{
+ map(0x03b0, 0x03bf).rw(FUNC(sis630_gui_device::vga_3b0_r), FUNC(sis630_gui_device::vga_3b0_w));
+ map(0x03c0, 0x03cf).rw(FUNC(sis630_gui_device::vga_3c0_r), FUNC(sis630_gui_device::vga_3c0_w));
+ map(0x03d0, 0x03df).rw(FUNC(sis630_gui_device::vga_3d0_r), FUNC(sis630_gui_device::vga_3d0_w));
+}
+
+void sis630_gui_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
+{
+
+}
+
+void sis630_gui_device::device_start()
+{
+ pci_device::device_start();
+
+ add_map(64*1024*1024, M_MEM, FUNC(sis630_gui_device::memory_map));
+ // claims 128KB, which goes outside the pentium range.
+ // Assume memory mapped, given the size should be yet another VGA memory compatible window.
+ add_map(128*1024, M_MEM, FUNC(sis630_gui_device::io_map));
+ add_map(128, M_IO, FUNC(sis630_gui_device::space_io_map));
+
+ add_rom((u8 *)m_gui_rom->base(), m_gui_rom->bytes());
+
+ // INTA#
+ intr_pin = 1;
+}
+
+void sis630_gui_device::device_reset()
+{
+ pci_device::device_reset();
+
+ command = 0x0004;
+ status = 0x0220;
+
+ m_subsystem_logger_mask = 0;
+}
+
+// TODO: remove these trampolines
+uint8_t sis630_gui_device::vram_r(offs_t offset)
+{
+ return downcast<sis630_svga_device *>(m_svga.target())->mem_r(offset);
+}
+
+void sis630_gui_device::vram_w(offs_t offset, uint8_t data)
+{
+ downcast<sis630_svga_device *>(m_svga.target())->mem_w(offset, data);
+}
+
+u32 sis630_gui_device::vga_3b0_r(offs_t offset, uint32_t mem_mask)
+{
+ uint32_t result = 0;
+ if (ACCESSING_BITS_0_7)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03b0_r(offset * 4 + 0) << 0;
+ if (ACCESSING_BITS_8_15)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03b0_r(offset * 4 + 1) << 8;
+ if (ACCESSING_BITS_16_23)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03b0_r(offset * 4 + 2) << 16;
+ if (ACCESSING_BITS_24_31)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03b0_r(offset * 4 + 3) << 24;
+ return result;
+}
+
+void sis630_gui_device::vga_3b0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ if (ACCESSING_BITS_0_7)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03b0_w(offset * 4 + 0, data >> 0);
+ if (ACCESSING_BITS_8_15)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03b0_w(offset * 4 + 1, data >> 8);
+ if (ACCESSING_BITS_16_23)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03b0_w(offset * 4 + 2, data >> 16);
+ if (ACCESSING_BITS_24_31)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03b0_w(offset * 4 + 3, data >> 24);
+}
+
+
+u32 sis630_gui_device::vga_3c0_r(offs_t offset, uint32_t mem_mask)
+{
+ uint32_t result = 0;
+ if (ACCESSING_BITS_0_7)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03c0_r(offset * 4 + 0) << 0;
+ if (ACCESSING_BITS_8_15)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03c0_r(offset * 4 + 1) << 8;
+ if (ACCESSING_BITS_16_23)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03c0_r(offset * 4 + 2) << 16;
+ if (ACCESSING_BITS_24_31)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03c0_r(offset * 4 + 3) << 24;
+ return result;
+}
+
+void sis630_gui_device::vga_3c0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ if (ACCESSING_BITS_0_7)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03c0_w(offset * 4 + 0, data >> 0);
+ if (ACCESSING_BITS_8_15)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03c0_w(offset * 4 + 1, data >> 8);
+ if (ACCESSING_BITS_16_23)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03c0_w(offset * 4 + 2, data >> 16);
+ if (ACCESSING_BITS_24_31)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03c0_w(offset * 4 + 3, data >> 24);
+}
+
+u32 sis630_gui_device::vga_3d0_r(offs_t offset, uint32_t mem_mask)
+{
+ uint32_t result = 0;
+ if (ACCESSING_BITS_0_7)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03d0_r(offset * 4 + 0) << 0;
+ if (ACCESSING_BITS_8_15)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03d0_r(offset * 4 + 1) << 8;
+ if (ACCESSING_BITS_16_23)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03d0_r(offset * 4 + 2) << 16;
+ if (ACCESSING_BITS_24_31)
+ result |= downcast<sis630_svga_device *>(m_svga.target())->port_03d0_r(offset * 4 + 3) << 24;
+ return result;
+}
+
+void sis630_gui_device::vga_3d0_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ if (ACCESSING_BITS_0_7)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03d0_w(offset * 4 + 0, data >> 0);
+ if (ACCESSING_BITS_8_15)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03d0_w(offset * 4 + 1, data >> 8);
+ if (ACCESSING_BITS_16_23)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03d0_w(offset * 4 + 2, data >> 16);
+ if (ACCESSING_BITS_24_31)
+ downcast<sis630_svga_device *>(m_svga.target())->port_03d0_w(offset * 4 + 3, data >> 24);
+}
+
+/*****************************
+ *
+ * 630 bridge PCI implementation
+ *
+ ****************************/
+
+DEFINE_DEVICE_TYPE(SIS630_BRIDGE, sis630_bridge_device, "sis630_bridge", "SiS 630 Virtual PCI-to-PCI bridge")
+
+sis630_bridge_device::sis630_bridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : pci_bridge_device(mconfig, SIS630_BRIDGE, tag, owner, clock)
+ , m_vga(*this, finder_base::DUMMY_TAG)
+{
+
+}
+
+void sis630_bridge_device::map_extra(
+ uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space
+)
+{
+ // command extensions
+ // VGA control - forward legacy VGA addresses to AGP
+ // TODO: doc implies that is unaffected by base and limit?
+ if (BIT(bridge_control, 3))
+ {
+ memory_space->install_device(0, 0xfffff, *m_vga, &sis630_gui_device::legacy_memory_map);
+ io_space->install_device(0, 0x0fff, *m_vga, &sis630_gui_device::legacy_io_map);
+ }
+
+ // TODO: ISA control
+ // forward to "primary PCI" (host & LPC?) for A8 or A9 blocks for each 1KB blocks in I/O spaces,
+ // (i.e. $100-$3ff, $500-$7ff, $900-$bff etc.)
+ // even if I/O range is inside base and limits
+// if (BIT(bridge_control, 2))
+ // ...
+}
+
+void sis630_bridge_device::bridge_control_w(offs_t offset, uint16_t data, uint16_t mem_mask)
+{
+ pci_bridge_device::bridge_control_w(offset, data, mem_mask);
+ LOGMAP("- %s VGA control\n", bridge_control & 8 ? "Enable" : "Disable");
+ remap_cb();
+}
+
+void sis630_bridge_device::device_start()
+{
+ pci_bridge_device::device_start();
+}
+
+void sis630_bridge_device::device_reset()
+{
+ pci_bridge_device::device_reset();
+}
diff --git a/src/devices/machine/sis630_gui.h b/src/devices/machine/sis630_gui.h
new file mode 100644
index 00000000000..79690f81575
--- /dev/null
+++ b/src/devices/machine/sis630_gui.h
@@ -0,0 +1,144 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+
+#ifndef MAME_MACHINE_SIS630_VGA_H
+#define MAME_MACHINE_SIS630_VGA_H
+
+#pragma once
+
+#include "pci.h"
+#include "video/pc_vga.h"
+
+class sis630_svga_device : public svga_device
+{
+public:
+ sis630_svga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ virtual uint8_t mem_r(offs_t offset) override;
+ virtual void mem_w(offs_t offset, uint8_t data) override;
+
+ virtual u8 port_03c0_r(offs_t offset) override;
+ virtual void port_03c0_w(offs_t offset, uint8_t data) override;
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual uint8_t crtc_reg_read(uint8_t index) override;
+ virtual void crtc_reg_write(uint8_t index, uint8_t data) override;
+ virtual uint8_t seq_reg_read(uint8_t index) override;
+ virtual void seq_reg_write(uint8_t index, uint8_t data) override;
+ virtual uint16_t offset() override;
+ virtual void recompute_params() override;
+
+ u8 m_crtc_ext_regs[0x100]{};
+ u8 m_seq_ext_regs[0x100]{};
+ u8 m_ramdac_mode = 0;
+ u8 m_ext_misc_ctrl_0 = 0;
+ u8 m_ext_vert_overflow = 0;
+ u8 m_ext_horz_overflow[2]{};
+ u32 m_svga_bank_reg_w = 0;
+ u32 m_svga_bank_reg_r = 0;
+ bool m_unlock_reg = false;
+
+ std::tuple<u8, u8> flush_true_color_mode();
+// bool m_dual_seg_mode = false;
+};
+
+DECLARE_DEVICE_TYPE(SIS630_SVGA, sis630_svga_device)
+
+
+class sis630_gui_device : public pci_device
+{
+public:
+ sis630_gui_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ void legacy_memory_map(address_map &map);
+ void legacy_io_map(address_map &map);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+ virtual const tiny_rom_entry *device_rom_region() const override;
+
+// virtual void reset_all_mappings() override;
+
+ virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;
+
+ virtual void config_map(address_map &map) override;
+
+ void memory_map(address_map &map);
+ void io_map(address_map &map);
+ void space_io_map(address_map &map);
+
+private:
+ required_device<sis630_svga_device> m_svga;
+ required_memory_region m_gui_rom;
+
+ u8 vram_r(offs_t offset);
+ void vram_w(offs_t offset, uint8_t data);
+ u32 vga_3b0_r(offs_t offset, uint32_t mem_mask = ~0);
+ void vga_3b0_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+ u32 vga_3c0_r(offs_t offset, uint32_t mem_mask = ~0);
+ void vga_3c0_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+ u32 vga_3d0_r(offs_t offset, uint32_t mem_mask = ~0);
+ void vga_3d0_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+
+ void subvendor_w(offs_t offset, u32 data, u32 mem_mask = ~0);
+
+ virtual u8 capptr_r() override;
+ u32 agp_id_r();
+ u32 agp_status_r();
+ u32 agp_command_r(offs_t offset, uint32_t mem_mask);
+ void agp_command_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+
+ struct {
+ bool enable = false;
+ u8 data_rate = 0;
+ } m_agp;
+
+ u32 m_subsystem_logger_mask = 0;
+
+ u8 unmap_log_r(offs_t offset);
+ void unmap_log_w(offs_t offset, u8 data);
+};
+
+DECLARE_DEVICE_TYPE(SIS630_GUI, sis630_gui_device)
+
+class sis630_bridge_device : public pci_bridge_device
+{
+public:
+ template <typename T> sis630_bridge_device(
+ const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock,
+ T &&gui_tag
+ ) : sis630_bridge_device(mconfig, tag, owner, clock)
+ {
+ // either 0001 or 6001 as device ID
+ set_ids_bridge(0x10396001, 0x00);
+ //set_multifunction_device(true);
+ m_vga.set_tag(std::forward<T>(gui_tag));
+ }
+
+ sis630_bridge_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+ virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;
+
+private:
+ required_device<sis630_gui_device> m_vga;
+
+ virtual void bridge_control_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0) override;
+
+
+};
+
+DECLARE_DEVICE_TYPE(SIS630_BRIDGE, sis630_bridge_device)
+
+
+#endif
diff --git a/src/devices/machine/sis630_host.cpp b/src/devices/machine/sis630_host.cpp
new file mode 100644
index 00000000000..5088585aa5f
--- /dev/null
+++ b/src/devices/machine/sis630_host.cpp
@@ -0,0 +1,441 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+/**************************************************************************************************
+
+ SiS630 host implementation (northbridge)
+
+ TODO:
+ - AGP and VGA interfaces;
+ - Is ACPI declared here shared with LPC or a different one?
+ \- shutms11 maps it to the exact same place (I/O $5000), may be interleaved?
+ - HW trap control;
+ - PCI-Hole;
+ - Convert RAM to device;
+ - Integrated VGA control;
+
+**************************************************************************************************/
+
+#include "emu.h"
+#include "sis630_host.h"
+
+
+#define LOG_IO (1U << 1) // log PCI register accesses
+#define LOG_TODO (1U << 2) // log unimplemented registers
+#define LOG_MAP (1U << 3) // log full remaps
+#define LOG_AGP (1U << 4) // log AGP
+
+#define VERBOSE (LOG_GENERAL | LOG_IO | LOG_TODO | LOG_AGP)
+//#define LOG_OUTPUT_FUNC osd_printf_warning
+
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMAP(...) LOGMASKED(LOG_MAP, __VA_ARGS__)
+#define LOGTODO(...) LOGMASKED(LOG_TODO, __VA_ARGS__)
+#define LOGAGP(...) LOGMASKED(LOG_AGP, __VA_ARGS__)
+
+DEFINE_DEVICE_TYPE(SIS630_HOST, sis630_host_device, "sis630_host", "SiS 630 Host-to-PCI Bridge")
+
+sis630_host_device::sis630_host_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : pci_host_device(mconfig, SIS630_HOST, tag, owner, clock)
+ , m_host_cpu(*this, finder_base::DUMMY_TAG)
+{
+}
+
+void sis630_host_device::device_start()
+{
+ pci_host_device::device_start();
+
+ memory_window_start = 0;
+ memory_window_end = 0xffffffff;
+ memory_offset = 0;
+ io_window_start = 0;
+ io_window_end = 0xffff;
+ io_offset = 0;
+
+ memory_space = &m_host_cpu->space(AS_PROGRAM);
+ io_space = &m_host_cpu->space(AS_IO);
+ add_map(8*1024*1024, M_MEM, FUNC(sis630_host_device::memory_map));
+
+ m_ram.resize(m_ram_size/4);
+}
+
+void sis630_host_device::device_reset()
+{
+ pci_host_device::device_reset();
+
+ command = 0x0005;
+ status = 0x0210;
+
+ m_shadow_ram_ctrl = 0;
+ m_vga_control = 0;
+ std::fill(std::begin(m_agp_mailbox), std::end(m_agp_mailbox), 0);
+
+ remap_cb();
+}
+
+void sis630_host_device::device_add_mconfig(machine_config &config)
+{
+
+}
+
+void sis630_host_device::config_map(address_map &map)
+{
+ pci_host_device::config_map(map);
+ // override header type, needed for actual IDE detection
+ map(0x0e, 0x0e).lr8(NAME([] () { return 0x80; }));
+
+ // override first BAR slot for gfx window base address
+ map(0x10, 0x13).rw(FUNC(pci_device::address_base_r), FUNC(pci_device::address_base_w));
+
+ map(0x34, 0x34).r(FUNC(sis630_host_device::capptr_r));
+
+ // host & DRAM regs
+// map(0x50, 0x51) host interface control
+// map(0x52, 0x53) DRAM misc control 1 & 2
+// map(0x54, 0x55) DRAM timing control 1 & 2
+// map(0x56, 0x56) DRAM misc control 3
+// map(0x57, 0x57) SDRAM/VCM init control
+// map(0x58, 0x58) DRAM buffer slew rating
+// map(0x59, 0x5a) DRAM buffer strength and current rating
+// map(0x5b, 0x5b) PCI buffer strength and current rating
+// map(0x60, 0x62) DRAMx type register (x = 0, 1 or 2)
+ map(0x63, 0x63).rw(FUNC(sis630_host_device::dram_status_r), FUNC(sis630_host_device::dram_status_w));
+// map(0x64, 0x64) FBC control register
+// map(0x65, 0x65) DIMM switch control
+// map(0x68, 0x69) ACPI I/O base
+ map(0x6a, 0x6a).rw(FUNC(sis630_host_device::smram_r), FUNC(sis630_host_device::smram_w));
+// map(0x6b, 0x6b) self refresh command output timing control
+// map(0x6c, 0x6c) power management DRAM self refresh control
+
+// Shadow RAM & PCI-Hole area
+ map(0x70, 0x73).rw(FUNC(sis630_host_device::shadow_ram_ctrl_r), FUNC(sis630_host_device::shadow_ram_ctrl_w));
+ map(0x77, 0x77).rw(FUNC(sis630_host_device::pci_hole_r), FUNC(sis630_host_device::pci_hole_w));
+// map(0x78, 0x79) PCI-Hole #1 allocation
+// map(0x7a, 0x7b) PCI-Hole #2 allocation
+
+// HW Trap control
+// map(0x7c, 0x7c) VGA
+// map(0x7d, 0x7d) Southbridge
+// map(0x7e, 0x7f) Northbridge
+
+// Host Bridge & PCI arbiter characteristics
+// map(0x80, 0x80) Target bridge DRAM characteristics
+// map(0x81, 0x81) PCI discard timer for delay transaction
+// map(0x82, 0x82) PCI target bridge bus characteristics
+// map(0x83, 0x83) CPU to PCI characteristics
+// map(0x84, 0x85) PCI grant timer
+// map(0x86, 0x86) CPU idle timer for PCI
+// map(0x87, 0x87) Host bridge & PCI master priority timer
+// map(0x88, 0x89) PCI discard timer for PCI hold
+
+// Clock Control
+// map(0x8c, 0x8c) SDRCLK/SDWCLK
+// map(0x8d, 0x8d) SDWCLK
+// map(0x8e, 0x8e) CPU & SDRAM clock relationship
+// map(0x8f, 0x8f) FBCRCLK/FBCWCLK control
+
+// GART and page table regs
+// map(0x90, 0x93) GART base address
+// map(0x94, 0x94) Graphic window control
+// map(0x97, 0x97) Page table cache control
+// map(0x98, 0x98) Page table cache invalidation control
+
+ // Integrated VGA control
+ map(0x9c, 0x9c).rw(FUNC(sis630_host_device::vga_control_r), FUNC(sis630_host_device::vga_control_w));
+
+// AGP
+// map(0xa0, 0xa3) DRAM priority timer control
+ map(0xa0, 0xa3).rw(FUNC(sis630_host_device::agp_priority_timer_r), FUNC(sis630_host_device::agp_priority_timer_w));
+// map(0xa4, 0xaf) General purpose register (generic mailboxes?)
+ map(0xa4, 0xaf).rw(FUNC(sis630_host_device::agp_mailbox_r), FUNC(sis630_host_device::agp_mailbox_w));
+// map(0xc0, 0xc3) AGP capability identifier
+ map(0xc0, 0xc3).r(FUNC(sis630_host_device::agp_id_r));
+ map(0xc4, 0xc7).r(FUNC(sis630_host_device::agp_status_r));
+ map(0xc8, 0xcb).rw(FUNC(sis630_host_device::agp_command_r), FUNC(sis630_host_device::agp_command_w));
+}
+
+// TODO: verify if we need these trampolines
+void sis630_host_device::memory_map(address_map &map)
+{
+}
+
+void sis630_host_device::map_shadowram(address_space *memory_space, uint32_t start_offs, uint32_t end_offs, bool read_enable, bool write_enable)
+{
+ LOGMAP("- 0x%08x-0x%08x ", start_offs, end_offs);
+
+ switch(write_enable << 1 | read_enable)
+ {
+ case 0:
+ LOGMAP("shadow RAM off\n");
+ //memory_space->unmap_write(start_offs, end_offs);
+ break;
+ case 1:
+ LOGMAP("shadow RAM r/o\n");
+ memory_space->install_rom(start_offs, end_offs, &m_ram[start_offs/4]);
+ break;
+ case 2:
+ LOGMAP("shadow RAM w/o\n");
+ //memory_space->install_rom(start_offs, end_offs, m_region->base() + bios_rom_offset);
+ memory_space->install_writeonly(start_offs, end_offs, &m_ram[start_offs/4]);
+ break;
+ case 3:
+ LOGMAP("shadow RAM r/w\n");
+ memory_space->install_ram(start_offs, end_offs, &m_ram[start_offs/4]);
+ break;
+ }
+}
+
+
+void sis630_host_device::map_extra(
+ uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space
+) {
+ io_space->install_device(0, 0xffff, *static_cast<pci_host_device *>(this), &pci_host_device::io_configuration_access_map);
+
+ regenerate_config_mapping();
+
+ memory_space->install_ram(0x00000000, 0x0009ffff, &m_ram[0x00000000/4]);
+// memory_space->install_ram(0x000a0000, 0x000bffff, &m_ram[0x000a0000/4]);
+
+ LOGMAP("Host Remapping table (shadow: %08x smram: %02x):\n", m_shadow_ram_ctrl, m_smram);
+
+ for (int i = 0; i < 12; i ++)
+ {
+ u32 start_offs = 0x000c0000 + i * 0x4000;
+ u32 end_offs = start_offs + 0x3fff;
+
+ map_shadowram(
+ memory_space,
+ start_offs, end_offs,
+ bool(BIT(m_shadow_ram_ctrl, i)), bool(BIT(m_shadow_ram_ctrl, i + 16))
+ );
+ }
+
+ map_shadowram(
+ memory_space,
+ 0xf0000, 0xfffff,
+ bool(BIT(m_shadow_ram_ctrl, 12)), bool(BIT(m_shadow_ram_ctrl, 28))
+ );
+
+ // System Management Memory Region handling
+ // Potentially overrides VGA VRAM if on
+ if (BIT(m_smram, 4))
+ {
+ u8 smram_config = m_smram >> 5;
+
+ // POST checks for config 6 only, other settings aren't tested
+ // TODO: setting 3 and 5 are undocumented, verify if mirror logic is correct
+ if (smram_config == 3 || smram_config == 5)
+ throw emu_fatalerror("SMRAM config = %d!", smram_config);
+
+ const u32 host_addresses[8] = {
+ 0xe0000, 0xb0000, 0xe0000, 0xb0000,
+ 0xe0000, 0xe0000, 0xa0000, 0xa0000
+ };
+ const u32 smram_sizes[8] = {
+ 0x07fff, 0xffff, 0x7fff, 0xffff,
+ 0x07fff, 0x7fff, 0xffff, 0x1ffff
+ };
+ const u32 system_memory_addresses[8] = {
+ 0xe0000, 0xb0000, 0xa0000, 0xb0000,
+ 0xb0000, 0xb0000, 0xa0000, 0xa0000
+ };
+ const u32 host_address_start = host_addresses[smram_config];
+ const u32 host_address_end = host_address_start + smram_sizes[smram_config];
+ const u32 system_memory_address = system_memory_addresses[smram_config];
+ LOGMAP("- SMRAM %02x relocation %08x-%08x to %08x\n"
+ , m_smram
+ , host_address_start
+ , host_address_end
+ , system_memory_address
+ );
+ memory_space->install_ram(host_address_start, host_address_end, &m_ram[system_memory_address/4]);
+ }
+
+ // TODO: shadow RAM bit 15?
+ // Always on after POST, should give shared access to PCI cards on the bus,
+ // BIOS mentions 8M of "shared memory", unknown how this works out.
+ // TODO: undocumented shadow RAM configs bits 7 and 23 after POST IDE check on shutms11 (programmer errors?)
+
+ memory_space->install_ram(0x00100000, m_ram_size - 1, &m_ram[0x00100000/4]);
+}
+
+
+
+
+
+/*
+ *
+ * I/O implemtation
+ *
+ */
+
+u8 sis630_host_device::capptr_r()
+{
+ LOGIO("Read capptr_r [$34]\n");
+ return 0xc0;
+}
+
+u8 sis630_host_device::dram_status_r()
+{
+ LOGIO("Read DRAM status [$63] (%02x)\n", m_dram_status);
+ return m_dram_status;
+}
+
+void sis630_host_device::dram_status_w(u8 data)
+{
+ LOGIO("Write DRAM status [$63] %02x\n", data);
+
+ m_dram_status = data;
+ // TODO: bit 7 is shared memory control
+}
+
+
+u8 sis630_host_device::smram_r()
+{
+ LOGIO("Read SMRAM [$6a] (%02x)\n", m_smram);
+ return m_smram;
+}
+
+void sis630_host_device::smram_w(u8 data)
+{
+ LOGIO("Write SMRAM [$6a] %02x\n", data);
+ m_smram = data;
+ remap_cb();
+}
+
+u32 sis630_host_device::shadow_ram_ctrl_r(offs_t offset, uint32_t mem_mask)
+{
+ LOGIO("Read shadow RAM setting [$70] %08x (%08x)\n", mem_mask, m_shadow_ram_ctrl);
+ return m_shadow_ram_ctrl;
+}
+
+void sis630_host_device::shadow_ram_ctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ COMBINE_DATA(&m_shadow_ram_ctrl);
+ LOGMAP("Write shadow RAM setting [$70] %08x & %08x (%08x)\n", data, mem_mask, m_shadow_ram_ctrl);
+ remap_cb();
+}
+
+u8 sis630_host_device::pci_hole_r()
+{
+ LOGIO("Read PCI hole [$77]\n");
+ return 0;
+}
+
+void sis630_host_device::pci_hole_w(u8 data)
+{
+ LOGIO("Write PCI hole [$77] %02x\n", data);
+ if (data)
+ LOG("Warning: PCI hole area enabled! %02x\n", data);
+}
+
+u8 sis630_host_device::vga_control_r()
+{
+ LOGIO("Read integrated VGA control data [$9c] %02x\n", m_vga_control);
+ return m_vga_control;
+}
+
+void sis630_host_device::vga_control_w(u8 data)
+{
+ LOGIO("Write integrated VGA control data [$9c] %02x\n", data);
+ // TODO: "integrated VGA control" (?)
+ m_vga_control = data;
+// remap_cb();
+}
+
+u32 sis630_host_device::agp_priority_timer_r(offs_t offset, uint32_t mem_mask)
+{
+ LOGIO("Read AGP priority timer [$a0] %08x (%08x)\n", mem_mask, m_agp_priority_timer);
+ return m_agp_priority_timer;
+}
+
+void sis630_host_device::agp_priority_timer_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ COMBINE_DATA(&m_agp_priority_timer);
+ LOGIO("Write AGP priority timer [$a0] %08x & %08x (%08x)\n", data, mem_mask, m_agp_priority_timer);
+}
+
+u8 sis630_host_device::agp_mailbox_r(offs_t offset)
+{
+ LOGIO("Read AGP mailbox [$%02x] (%02x)\n", offset + 0xa4, m_agp_mailbox[offset]);
+ return m_agp_mailbox[offset];
+}
+
+void sis630_host_device::agp_mailbox_w(offs_t offset, u8 data)
+{
+ LOGIO("Write AGP mailbox [$%02x] %02x\n", offset + 0xa4, data);
+ m_agp_mailbox[offset] = data;
+}
+
+// TODO: move to generic interface
+u32 sis630_host_device::agp_id_r()
+{
+ LOGAGP("Read AGP ID [$c0]\n");
+ // bits 23-16 AGP v2.0
+ // bits 15-8 0x00 no NEXT_PTR (NULL terminates here)
+ // bits 7-0 CAP_ID (0x02 for AGP)
+ return 0x00200002;
+}
+
+u32 sis630_host_device::agp_status_r()
+{
+ LOGAGP("Read AGP status [$c4]\n");
+ // bits 31-24 RQ max number of AGP command requests (0x1f + 1 = 32)
+ // bit 9: SBA, side band addressing enabled
+ // ---- -xxx RATE
+ // ---- -1-- 4X transfer capable
+ // ---- --1- 2X transfer capable
+ // ---- ---1 1X transfer capable
+ // NB: documentation claims a RATE of 0x03 then contradicts with "111b" value, do the math
+ // It gets setup with a 4X at POST, assume 0x07 is right
+
+ // Stuff that isn't enabled here:
+ // bit 5: 4G support address greater than 4 GB
+ // bit 4: FW transfer support
+
+ return 0x1f000207;
+}
+
+u32 sis630_host_device::agp_command_r(offs_t offset, uint32_t mem_mask)
+{
+ LOGAGP("Read AGP command [$c8] %d %d %02x\n", m_agp.sba_enable, m_agp.enable, m_agp.data_rate);
+ // TODO: enable gets cleared by AGP_RESET, or even from PCI RST#
+ return m_agp.sba_enable << 9 | m_agp.enable << 8 | (m_agp.data_rate & 7);
+}
+
+void sis630_host_device::agp_command_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ LOGAGP("Write AGP command [$c8] %08x & %08x\n", data, mem_mask);
+ if (ACCESSING_BITS_8_15)
+ {
+ m_agp.sba_enable = bool(BIT(m_agp.sba_enable, 9));
+ m_agp.enable = bool(BIT(m_agp.enable, 8));
+ LOGAGP("- SBA_ENABLE = %d AGP_ENABLE = %d\n", m_agp.sba_enable, m_agp.enable);
+ }
+
+ if (ACCESSING_BITS_0_7)
+ {
+ // quick checker, to be translated into an AGP interface
+ std::map<u8, std::string> agp_transfer_rates = {
+ { 0, "(illegal 0)" },
+ { 1, "1X" },
+ { 2, "2X" },
+ { 3, "(illegal 3)" },
+ { 4, "4X" },
+ { 5, "(illegal 5)" },
+ { 6, "(illegal 6)" },
+ { 7, "(illegal 7)" }
+ };
+
+ // make sure the AGP DATA_RATE specs are honored
+ const u8 data_rate = data & 7;
+ LOGAGP("- DATA_RATE = %s enabled=%d\n", agp_transfer_rates.at(data_rate), m_agp.enable);
+ m_agp.data_rate = data_rate;
+
+ // should probably never be enabled since it reads out from the ID
+ if (data & 0x30)
+ LOG("Warning: AGP unsupported i/f set 4G=%d FW_Enable=%d\n", bool(BIT(data, 5)), bool(BIT(data, 4)));
+ }
+}
diff --git a/src/devices/machine/sis630_host.h b/src/devices/machine/sis630_host.h
new file mode 100644
index 00000000000..46efc0ed803
--- /dev/null
+++ b/src/devices/machine/sis630_host.h
@@ -0,0 +1,98 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+
+#ifndef MAME_MACHINE_SIS630_HOST_H
+#define MAME_MACHINE_SIS630_HOST_H
+
+#pragma once
+
+#include "pci.h"
+#include "sis630_gui.h"
+
+class sis630_host_device : public pci_host_device
+{
+public:
+ template <typename T> sis630_host_device(
+ const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock,
+ T &&cpu_tag, int ram_size
+ ) : sis630_host_device(mconfig, tag, owner, clock)
+ {
+ // Revision 1 -> A1
+ set_ids(0x10390630, 0x01, 0x060000, 0x00);
+ set_multifunction_device(true);
+ m_host_cpu.set_tag(std::forward<T>(cpu_tag));
+ set_ram_size(ram_size);
+ }
+
+ sis630_host_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+ void set_ram_size(int ram_size) { m_ram_size = ram_size; }
+
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+ virtual bool map_first() const override { return true; }
+
+// virtual void reset_all_mappings() override;
+
+ virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;
+
+ virtual void config_map(address_map &map) override;
+
+ void memory_map(address_map &map);
+
+private:
+ required_device<cpu_device> m_host_cpu;
+ std::vector<uint32_t> m_ram;
+
+ void map_shadowram(address_space *memory_space, offs_t start_offs, offs_t end_offs, bool read_enable, bool write_enable);
+
+ int m_ram_size = 0;
+ u8 m_dram_status = 0;
+ u32 m_shadow_ram_ctrl = 0;
+ u8 m_vga_control = 0;
+ u8 m_agp_mailbox[12]{};
+ u8 m_smram = 0;
+ u32 m_agp_priority_timer = 0;
+
+ u8 dram_status_r();
+ void dram_status_w(u8 data);
+
+ u8 pci_hole_r();
+ void pci_hole_w(u8 data);
+
+ u8 vga_control_r();
+ void vga_control_w(u8 data);
+
+ u32 shadow_ram_ctrl_r(offs_t offset, uint32_t mem_mask = ~0);
+ void shadow_ram_ctrl_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+
+ u8 smram_r();
+ void smram_w(u8 data);
+
+ u32 agp_priority_timer_r(offs_t offset, uint32_t mem_mask = ~0);
+ void agp_priority_timer_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+
+ u8 agp_mailbox_r(offs_t offset);
+ void agp_mailbox_w(offs_t offset, u8 data);
+
+ virtual uint8_t capptr_r() override;
+ u32 agp_id_r();
+ u32 agp_status_r();
+ u32 agp_command_r(offs_t offset, uint32_t mem_mask);
+ void agp_command_w(offs_t offset, uint32_t data, uint32_t mem_mask = ~0);
+
+ struct {
+ bool sba_enable = false;
+ bool enable = false;
+ u8 data_rate = 0;
+ } m_agp;
+};
+
+DECLARE_DEVICE_TYPE(SIS630_HOST, sis630_host_device)
+
+
+#endif
diff --git a/src/devices/machine/sis7001_usb.cpp b/src/devices/machine/sis7001_usb.cpp
new file mode 100644
index 00000000000..7703c777888
--- /dev/null
+++ b/src/devices/machine/sis7001_usb.cpp
@@ -0,0 +1,93 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+/**************************************************************************************************
+
+ SiS 7001 USB Host controller
+
+ TODO:
+ - Stub interface, to be improved;
+ - PCI values omitted from docs, assumes same as OpenHCI;
+
+**************************************************************************************************/
+
+#include "emu.h"
+#include "sis7001_usb.h"
+
+#define LOG_IO (1U << 1) // log PCI register accesses
+#define LOG_MAP (1U << 2) // log full remaps
+
+#define VERBOSE (LOG_GENERAL | LOG_IO | LOG_MAP)
+//#define LOG_OUTPUT_FUNC osd_printf_warning
+
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMAP(...) LOGMASKED(LOG_MAP, __VA_ARGS__)
+
+DEFINE_DEVICE_TYPE(SIS7001_USB, sis7001_usb_device, "sis7001_usb", "SiS 7001 USB Host Controller")
+
+sis7001_usb_device::sis7001_usb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : pci_device(mconfig, SIS7001_USB, tag, owner, clock)
+
+{
+
+}
+
+void sis7001_usb_device::device_add_mconfig(machine_config &config)
+{
+
+}
+
+void sis7001_usb_device::config_map(address_map &map)
+{
+ pci_device::config_map(map);
+}
+
+void sis7001_usb_device::io_map(address_map &map)
+{
+ // operational mode
+ map(0x000, 0x003).lr32(NAME([]() { return 0x00000110; }));
+ // ...
+ // HcFmInterval (Windows OSes fails if this isn't r/w)
+ map(0x034, 0x037).lrw32(
+ NAME([this]() { return m_HcFmInterval; } ),
+ NAME([this](offs_t offset, u32 data, u32 mem_mask) { COMBINE_DATA(&m_HcFmInterval); LOG("Write HcFmInterval %08x & %08x\n", data, mem_mask); })
+ );
+ // ...
+ // HcRhDescriptorA, writeable except for 0x4ff
+ map(0x048, 0x04b).lr32(NAME([this]() { return 0x01000000 | m_downstream_ports; }));
+ // ...
+// map(0x05c, 0x05c) last item for function 2, missing on function 3
+
+ // legacy support mode (8-bit each)
+// map(0x100, 0x100) control, bit 0 enables emulation mode
+// map(0x104, 0x104) input
+// map(0x108, 0x108) output
+// map(0x10c, 0x10f) status
+}
+
+void sis7001_usb_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
+{
+ // TODO: overrides I/O ports $0060-$0064 (emulation mode)
+}
+
+void sis7001_usb_device::device_start()
+{
+ pci_device::device_start();
+
+ add_map(512, M_MEM, FUNC(sis7001_usb_device::io_map));
+
+ // INTD#
+ intr_pin = 4;
+}
+
+
+void sis7001_usb_device::device_reset()
+{
+ pci_device::device_reset();
+
+ command = 0x0000;
+ status = 0x0000;
+ m_HcFmInterval = 0;
+}
diff --git a/src/devices/machine/sis7001_usb.h b/src/devices/machine/sis7001_usb.h
new file mode 100644
index 00000000000..e85a5285bdf
--- /dev/null
+++ b/src/devices/machine/sis7001_usb.h
@@ -0,0 +1,53 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+
+#ifndef MAME_MACHINE_SIS7001_USB_H
+#define MAME_MACHINE_SIS7001_USB_H
+
+#pragma once
+
+#include "pci.h"
+
+class sis7001_usb_device : public pci_device
+{
+public:
+ sis7001_usb_device(
+ const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock,
+ int num_ports
+ ) : sis7001_usb_device(mconfig, tag, owner, clock)
+ {
+ // 0x0c0310 - Serial Bus Controller, USB, OpenHCI Host
+ // Assume no rev.
+ set_ids(0x10397001, 0x00, 0x0c0310, 0x00);
+ // TODO: should really read from a std::list interface
+ m_downstream_ports = num_ports;
+ }
+ sis7001_usb_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ static constexpr feature_type unemulated_features() { return feature::MEDIA; }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+// virtual void reset_all_mappings() override;
+
+ virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;
+
+ virtual void config_map(address_map &map) override;
+
+ void io_map(address_map &map);
+
+private:
+ u8 m_downstream_ports;
+ u32 m_HcFmInterval = 0;
+
+ u8 unmap_log_r(offs_t offset);
+ void unmap_log_w(offs_t offset, u8 data);
+};
+
+DECLARE_DEVICE_TYPE(SIS7001_USB, sis7001_usb_device)
+
+#endif
diff --git a/src/devices/machine/sis7018_audio.cpp b/src/devices/machine/sis7018_audio.cpp
new file mode 100644
index 00000000000..a9eb4416188
--- /dev/null
+++ b/src/devices/machine/sis7018_audio.cpp
@@ -0,0 +1,126 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+/**************************************************************************************************
+
+ SiS 7018 Audio device (AC97 complaint)
+
+ TODO:
+ - Stub interface, to be improved;
+ - Should be easy to at least inherit SB16/MIDI/game port devices;
+
+**************************************************************************************************/
+
+#include "emu.h"
+#include "sis7018_audio.h"
+
+#define LOG_IO (1U << 1) // log PCI register accesses
+#define LOG_TODO (1U << 2) // log unimplemented registers
+#define LOG_MAP (1U << 3) // log full remaps
+#define LOG_PMC (1U << 4) // log PMC access
+
+#define VERBOSE (LOG_GENERAL | LOG_IO | LOG_TODO | LOG_MAP | LOG_PMC)
+//#define LOG_OUTPUT_FUNC osd_printf_warning
+
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMAP(...) LOGMASKED(LOG_MAP, __VA_ARGS__)
+#define LOGTODO(...) LOGMASKED(LOG_TODO, __VA_ARGS__)
+#define LOGPMC(...) LOGMASKED(LOG_PMC, __VA_ARGS__)
+
+DEFINE_DEVICE_TYPE(SIS7018_AUDIO, sis7018_audio_device, "sis7018_audio", "SiS 7018 Audio AC97")
+
+sis7018_audio_device::sis7018_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : pci_device(mconfig, SIS7018_AUDIO, tag, owner, clock)
+
+{
+ // 0x040100 - Multimedia device, audio device (vendor specific i/f)
+ // 0x01 - Rev 1
+ set_ids(0x10397018, 0x01, 0x040100, 0x00);
+}
+
+void sis7018_audio_device::device_add_mconfig(machine_config &config)
+{
+
+}
+
+void sis7018_audio_device::config_map(address_map &map)
+{
+ pci_device::config_map(map);
+ // map(0x003e, 0x003f) max latency min=0x02, max=0x18
+
+ map(0x40, 0xe3).rw(FUNC(sis7018_audio_device::unmap_log_r), FUNC(sis7018_audio_device::unmap_log_w));
+
+ // PMC capability identifier
+ map(0xdc, 0xdf).r(FUNC(sis7018_audio_device::pmc_id_r));
+// map(0xe0, 0xe3).r(FUNC(sis7018_audio_device::pmc_status_r), FUNC(sis7018_audio_device::pmc_control_w));
+}
+
+u8 sis7018_audio_device::capptr_r()
+{
+ LOGIO("AUDIO Read capptr_r [$34]\n");
+ return 0xdc;
+}
+
+// TODO: move to specific interface
+u32 sis7018_audio_device::pmc_id_r()
+{
+ LOGPMC("Read PMC ID [$dc]\n");
+ // bits 31-16 PPMI v1.0, D1 / D2 supported, no PCI clock for PME#
+ // bits 15-8 0x00 no NEXT_PTR (NULL terminates here)
+ // bits 7-0 PM_CAP_ID (0x01 for PMC)
+ return 0xe6110001;
+}
+
+void sis7018_audio_device::memory_map(address_map &map)
+{
+
+}
+
+void sis7018_audio_device::io_map(address_map &map)
+{
+
+}
+
+void sis7018_audio_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
+{
+// io_space->install_device(0, 0x03ff, *this, &sis7018_audio_device::io_map);
+ // TODO: legacy handling, including game port
+}
+
+void sis7018_audio_device::device_start()
+{
+ pci_device::device_start();
+ add_map(256, M_IO, FUNC(sis7018_audio_device::io_map));
+ // not explicitly stated, assume 4096 size from the MEM decoding
+ add_map(4096, M_MEM, FUNC(sis7018_audio_device::memory_map));
+}
+
+
+void sis7018_audio_device::device_reset()
+{
+ pci_device::device_reset();
+
+ command = 0x0000;
+ status = 0x0000;
+ // INTB#
+ intr_pin = 2;
+ // TODO: can be written to with $46=1
+ subsystem_id = 0x10397018;
+}
+
+/*
+ * Debugging
+ */
+
+u8 sis7018_audio_device::unmap_log_r(offs_t offset)
+{
+ LOGTODO("AUDIO Unemulated [%02x] R\n", offset + 0x40);
+ return 0;
+}
+
+void sis7018_audio_device::unmap_log_w(offs_t offset, u8 data)
+{
+ LOGTODO("AUDIO Unemulated [%02x] %02x W\n", offset + 0x40, data);
+}
diff --git a/src/devices/machine/sis7018_audio.h b/src/devices/machine/sis7018_audio.h
new file mode 100644
index 00000000000..c9e9a6918b5
--- /dev/null
+++ b/src/devices/machine/sis7018_audio.h
@@ -0,0 +1,46 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+
+#ifndef MAME_MACHINE_SIS7018_AUDIO_H
+#define MAME_MACHINE_SIS7018_AUDIO_H
+
+#pragma once
+
+#include "pci.h"
+
+class sis7018_audio_device : public pci_device
+{
+public:
+ sis7018_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ static constexpr feature_type unemulated_features() { return feature::SOUND; }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+// virtual void reset_all_mappings() override;
+
+ virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;
+
+ virtual void config_map(address_map &map) override;
+
+ void memory_map(address_map &map);
+ void io_map(address_map &map);
+
+private:
+ virtual u8 capptr_r() override;
+
+ u32 pmc_id_r();
+// void pmc_status_r();
+// u32 pmc_control(offs_t offset, u32 data, u32 mem_mask = ~0);
+
+ u8 unmap_log_r(offs_t offset);
+ void unmap_log_w(offs_t offset, u8 data);
+};
+
+DECLARE_DEVICE_TYPE(SIS7018_AUDIO, sis7018_audio_device)
+
+#endif
diff --git a/src/devices/machine/sis900_eth.cpp b/src/devices/machine/sis900_eth.cpp
new file mode 100644
index 00000000000..d960c4b0293
--- /dev/null
+++ b/src/devices/machine/sis900_eth.cpp
@@ -0,0 +1,161 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+/**************************************************************************************************
+
+ SiS 900 Fast Ethernet Controller / Adapter
+
+ TODO:
+ - Stub interface, to be improved;
+ - Sensible defaults for EEPROM;
+
+**************************************************************************************************/
+
+#include "emu.h"
+#include "sis900_eth.h"
+
+#define LOG_IO (1U << 1) // log PCI register accesses
+#define LOG_TODO (1U << 2) // log unimplemented registers
+#define LOG_MAP (1U << 3) // log full remaps
+#define LOG_PMC (1U << 4) // log PMC access
+
+#define VERBOSE (LOG_GENERAL | LOG_IO | LOG_TODO | LOG_MAP | LOG_PMC)
+//#define LOG_OUTPUT_FUNC osd_printf_warning
+
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMAP(...) LOGMASKED(LOG_MAP, __VA_ARGS__)
+#define LOGTODO(...) LOGMASKED(LOG_TODO, __VA_ARGS__)
+#define LOGPMC(...) LOGMASKED(LOG_PMC, __VA_ARGS__)
+
+DEFINE_DEVICE_TYPE(SIS900_ETH, sis900_eth_device, "sis900_eth", "SiS 900 Fast Ethernet Adapter")
+
+sis900_eth_device::sis900_eth_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : pci_device(mconfig, SIS900_ETH, tag, owner, clock)
+ , m_eeprom(*this, "eeprom")
+ , m_eth_rom(*this, "eth_rom")
+{
+ // 0x020000 - Network Ethernet controller
+ // 0x80 - "Silicon revision" (sic)
+ set_ids(0x10390900, 0x80, 0x020000, 0x00);
+}
+
+void sis900_eth_device::device_add_mconfig(machine_config &config)
+{
+ EEPROM_93C66_16BIT(config, m_eeprom); // NM93Cxx
+}
+
+void sis900_eth_device::config_map(address_map &map)
+{
+ pci_device::config_map(map);
+ map(0x40, 0x43).r(FUNC(sis900_eth_device::pmc_id_r));
+// map(0x44, 0x47).r(FUNC(sis900_eth_device::pmc_status_r), FUNC(sis900_eth_device::pmc_control_w));
+}
+
+void sis900_eth_device::memory_map(address_map &map)
+{
+
+}
+
+void sis900_eth_device::io_map(address_map &map)
+{
+ map(0x08, 0x08).rw(FUNC(sis900_eth_device::eromar_r), FUNC(sis900_eth_device::eromar_w));
+}
+
+void sis900_eth_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
+{
+// io_space->install_device(0, 0x03ff, *this, &sis900_eth_device::io_map);
+}
+
+ROM_START( sis900eth )
+ ROM_REGION32_LE( 0x10000, "eth_rom", ROMREGION_ERASEFF )
+ ROM_LOAD( "pxe_m.19", 0x0000, 0xa000, BAD_DUMP CRC(c8da34a6) SHA1(f11b4f5398176b7d924c63c77a1951cb83020e48) )
+ // structure:
+ // [0x00] signature -> 0x55aa
+ // [0x08] MAC address
+ // [0x0b] checksum
+ // PCIR signature+[0x00] vendor ID -> 0x1039
+ // PCIR signature+[0x02] device ID -> 0x0900
+ROM_END
+
+const tiny_rom_entry *sis900_eth_device::device_rom_region() const
+{
+ return ROM_NAME(sis900eth);
+}
+
+void sis900_eth_device::device_start()
+{
+ pci_device::device_start();
+
+ add_map(256, M_IO, FUNC(sis900_eth_device::io_map));
+ add_map(4096, M_MEM, FUNC(sis900_eth_device::memory_map));
+ add_rom((u8 *)m_eth_rom->base(), m_eth_rom->bytes());
+
+ // INTC#
+ intr_pin = 3;
+ // TODO: "if auto load is enabled it is set by subvendor ID stored in EEPROM" (?)
+ subsystem_id = 0x10390900;
+}
+
+void sis900_eth_device::device_reset()
+{
+ pci_device::device_reset();
+
+ command = 0x0000;
+ status = 0x0290;
+
+ m_eromar_mode = false;
+}
+
+uint8_t sis900_eth_device::capptr_r()
+{
+ return 0x40;
+}
+
+// TODO: move to specific interface
+u32 sis900_eth_device::pmc_id_r()
+{
+ LOGPMC("Read PMC ID [$40]\n");
+ // bits 31-16 PPMI v1.0 / v1.0a, D1 / D2 supported, PME# D0/D1/D2/D3hot & optional D3cold
+ // bits 15-8 0x00 no NEXT_PTR (NULL terminates here)
+ // bits 7-0 PM_CAP_ID (0x01 for PMC)
+ // TODO: versioning may depend on ROM installed
+ return 0xfe010001;
+}
+
+/*
+ * I/O space
+ */
+
+u8 sis900_eth_device::eromar_r()
+{
+ // TODO: doc claims all bits are readable, looks unlikely?
+ u8 res = m_eromar_mode << 7;
+ if (m_eromar_mode)
+ {
+ // ...
+ }
+ else
+ res |= m_eeprom->do_read() << 1;
+
+ return res;
+}
+
+void sis900_eth_device::eromar_w(u8 data)
+{
+ m_eromar_mode = bool(BIT(data, 7));
+
+ LOGIO("eromar_w %s selected %02x\n", m_eromar_mode ? "HomePHY" : "eeprom", data);
+
+ if (m_eromar_mode)
+ {
+ // ...
+ }
+ else
+ {
+ m_eeprom->cs_write(BIT(data, 3));
+ m_eeprom->clk_write(BIT(data, 2));
+ m_eeprom->di_write(BIT(data, 0));
+ }
+}
diff --git a/src/devices/machine/sis900_eth.h b/src/devices/machine/sis900_eth.h
new file mode 100644
index 00000000000..4d5fb46f2bd
--- /dev/null
+++ b/src/devices/machine/sis900_eth.h
@@ -0,0 +1,53 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+
+#ifndef MAME_MACHINE_SIS900_ETH_H
+#define MAME_MACHINE_SIS900_ETH_H
+
+#pragma once
+
+#include "pci.h"
+#include "machine/eepromser.h"
+
+class sis900_eth_device : public pci_device
+{
+public:
+ sis900_eth_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ static constexpr feature_type unemulated_features() { return feature::LAN; }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+ virtual const tiny_rom_entry *device_rom_region() const override;
+
+// virtual void reset_all_mappings() override;
+
+ virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;
+
+ virtual void config_map(address_map &map) override;
+
+ void memory_map(address_map &map);
+ void io_map(address_map &map);
+
+private:
+ required_device<eeprom_serial_93cxx_device> m_eeprom;
+ required_memory_region m_eth_rom;
+
+ bool m_eromar_mode;
+
+ virtual uint8_t capptr_r() override;
+ u32 pmc_id_r();
+// void pmc_status_r();
+// u32 pmc_control(offs_t offset, u32 data, u32 mem_mask = ~0);
+
+ u8 eromar_r();
+ void eromar_w(u8 data);
+};
+
+DECLARE_DEVICE_TYPE(SIS900_ETH, sis900_eth_device)
+
+#endif
diff --git a/src/devices/machine/sis950_lpc.cpp b/src/devices/machine/sis950_lpc.cpp
new file mode 100644
index 00000000000..3a75f35156d
--- /dev/null
+++ b/src/devices/machine/sis950_lpc.cpp
@@ -0,0 +1,764 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+/**************************************************************************************************
+
+ SiS950 LPC implementation (Super I/O & southbridge)
+
+ TODO:
+ - Convert most stuff declared here to generic interfaces;
+ - Flash ROM handling
+ \- Doesn't survive a soft reset;
+ - Fix EISA;
+ - INIT register (reset & A20 control + fast gates + fast reset timing control);
+ - Override PS/2 ports if USB legacy mode is enabled;
+ - NMI & SMI handling;
+ - SMBus handling;
+ - RTC extended bank enable;
+ \- Doesn't survive a CMOS write after fast reset;
+ - Shadow registers for PIC and PIT;
+ - IRQ remaps
+ \- INTA GUI
+ \- INTB AUDIO and MODEM
+ \- INTC ethernet
+ \- INTD USB
+ - IRQ software traps ($6e-$6f);
+ \- Documentation mentions that those can be read-back too, huh?
+ - Understand what's the caveat of "changing device ID number" via BIOS control $40 bit 6;
+
+**************************************************************************************************/
+
+#include "emu.h"
+#include "sis950_lpc.h"
+#include "bus/pc_kbd/keyboards.h"
+#include "speaker.h"
+
+#define LOG_IO (1U << 1) // log PCI register accesses
+#define LOG_TODO (1U << 2) // log unimplemented registers
+#define LOG_MAP (1U << 3) // log full remaps
+#define LOG_LPC (1U << 4) // log LPC legacy regs
+#define LOG_IRQ (1U << 5) // log IRQ remaps
+
+#define VERBOSE (LOG_GENERAL | LOG_IO | LOG_TODO | LOG_IRQ)
+//#define LOG_OUTPUT_FUNC osd_printf_warning
+
+#include "logmacro.h"
+
+#define LOGIO(...) LOGMASKED(LOG_IO, __VA_ARGS__)
+#define LOGMAP(...) LOGMASKED(LOG_MAP, __VA_ARGS__)
+#define LOGTODO(...) LOGMASKED(LOG_TODO, __VA_ARGS__)
+#define LOGLPC(...) LOGMASKED(LOG_LPC, __VA_ARGS__)
+#define LOGIRQ(...) LOGMASKED(LOG_IRQ, __VA_ARGS__)
+
+DEFINE_DEVICE_TYPE(SIS950_LPC, sis950_lpc_device, "sis950_lpc", "SiS 950 LPC Super-South Bridge")
+
+sis950_lpc_device::sis950_lpc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : pci_device(mconfig, SIS950_LPC, tag, owner, clock)
+ , m_host_cpu(*this, finder_base::DUMMY_TAG)
+ , m_flash_rom(*this, finder_base::DUMMY_TAG)
+ , m_pic_master(*this, "pic_master")
+ , m_pic_slave(*this, "pic_slave")
+ , m_dmac_master(*this, "dmac_master")
+ , m_dmac_slave(*this, "dmac_slave")
+ , m_pit(*this, "pit")
+ , m_keybc(*this, "keybc")
+ , m_speaker(*this, "speaker")
+ , m_rtc(*this, "rtc")
+ , m_ps2_con(*this, "ps2_con")
+ , m_aux_con(*this, "aux_con")
+ , m_uart(*this, "uart")
+ , m_acpi(*this, "acpi")
+ , m_smbus(*this, "smbus")
+ , m_fast_reset_cb(*this)
+{
+}
+
+void sis950_lpc_device::device_start()
+{
+ pci_device::device_start();
+
+ m_fast_reset_cb.resolve_safe();
+}
+
+void sis950_lpc_device::device_reset()
+{
+ pci_device::device_reset();
+
+ command = 0x000c;
+ status = 0x0200;
+
+ m_bios_control = 1;
+ m_acpi_base = 0;
+ m_flash_control = 0x40;
+ m_keybc_reg = 0x51;
+ m_dma_channel = -1;
+// m_cur_eop = false;
+ m_dma_high_byte = 0;
+ m_init_reg = 0;
+ m_rtc_reg = 0x10;
+ // remapping is disabled for all as default
+ std::fill(std::begin(m_irq_remap), std::end(m_irq_remap), 0x80);
+
+ m_lpc_legacy.fast_init = 0;
+ remap_cb();
+}
+
+WRITE_LINE_MEMBER(sis950_lpc_device::cpu_a20_w)
+{
+ // TODO: confirm "A20M# being always high"
+// if (BIT(m_init_reg, 1))
+// state = ASSERT_LINE;
+ m_host_cpu->set_input_line(INPUT_LINE_A20, state);
+}
+
+WRITE_LINE_MEMBER(sis950_lpc_device::cpu_reset_w)
+{
+ // TODO: masked via INIT $46 bit 0
+ m_host_cpu->set_input_line(INPUT_LINE_RESET, state);
+}
+
+void sis950_lpc_device::device_add_mconfig(machine_config &config)
+{
+ constexpr XTAL lpc_pit_clock = XTAL(14'318'181);
+
+ // confirmed 82C54
+ PIT8254(config, m_pit, 0);
+ // heartbeat IRQ
+ m_pit->set_clk<0>(lpc_pit_clock / 12);
+ m_pit->out_handler<0>().set(FUNC(sis950_lpc_device::pit_out0));
+ // DRAM refresh
+ m_pit->set_clk<1>(lpc_pit_clock / 12);
+ m_pit->out_handler<1>().set(FUNC(sis950_lpc_device::pit_out1));
+ // PIO port C pin 4, and speaker polling enough
+ m_pit->set_clk<2>(lpc_pit_clock / 12);
+ m_pit->out_handler<2>().set(FUNC(sis950_lpc_device::pit_out2));
+
+ // TODO: unknown part #
+ AM9517A(config, m_dmac_master, lpc_pit_clock / 3);
+ m_dmac_master->out_hreq_callback().set(m_dmac_slave, FUNC(am9517a_device::dreq0_w));
+// m_dmac_master->out_eop_callback().set(FUNC(sis950_lpc_device::at_dma8237_out_eop));
+ m_dmac_master->in_memr_callback().set(FUNC(sis950_lpc_device::pc_dma_read_byte));
+ m_dmac_master->out_memw_callback().set(FUNC(sis950_lpc_device::pc_dma_write_byte));
+ // TODO: ior/iow/dack/eop callbacks
+
+ AM9517A(config, m_dmac_slave, lpc_pit_clock / 3);
+ m_dmac_slave->out_hreq_callback().set(FUNC(sis950_lpc_device::pc_dma_hrq_changed));
+ m_dmac_slave->in_memr_callback().set(FUNC(sis950_lpc_device::pc_dma_read_word));
+ m_dmac_slave->out_memw_callback().set(FUNC(sis950_lpc_device::pc_dma_write_word));
+ // TODO: ior/iow/dack callbacks
+
+ // Confirmed 82C59s
+ PIC8259(config, m_pic_master, 0);
+ m_pic_master->out_int_callback().set_inputline(m_host_cpu, 0);
+ m_pic_master->in_sp_callback().set_constant(1);
+ m_pic_master->read_slave_ack_callback().set(
+ [this](offs_t offset) -> u8
+ {
+ if (offset == 2)
+ return m_pic_slave->acknowledge();
+
+ return 0;
+ });
+
+ PIC8259(config, m_pic_slave, 0);
+ m_pic_slave->out_int_callback().set(m_pic_master, FUNC(pic8259_device::ir2_w));
+ m_pic_slave->in_sp_callback().set_constant(0);
+
+ // TODO: EISA, from virtual bridge
+
+ PC_KBDC(config, m_ps2_con, pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL);
+ m_ps2_con->out_clock_cb().set(m_keybc, FUNC(ps2_keyboard_controller_device::kbd_clk_w));
+ m_ps2_con->out_data_cb().set(m_keybc, FUNC(ps2_keyboard_controller_device::kbd_data_w));
+
+ PC_KBDC(config, m_aux_con, ps2_mice, STR_HLE_PS2_MOUSE);
+ m_aux_con->out_clock_cb().set(m_keybc, FUNC(ps2_keyboard_controller_device::aux_clk_w));
+ m_aux_con->out_data_cb().set(m_keybc, FUNC(ps2_keyboard_controller_device::aux_data_w));
+
+ // TODO: selectable between PCI clock / 4 (33 MHz) or 7.159 MHz, via reg $47 bit 5
+ PS2_KEYBOARD_CONTROLLER(config, m_keybc, DERIVED_CLOCK(1, 4));
+ // TODO: default ibm BIOS doesn't cope with this too well
+ m_keybc->set_default_bios_tag("compaq");
+ m_keybc->hot_res().set(FUNC(sis950_lpc_device::cpu_reset_w));
+ m_keybc->gate_a20().set(FUNC(sis950_lpc_device::cpu_a20_w));
+ m_keybc->kbd_irq().set(m_pic_master, FUNC(pic8259_device::ir1_w));
+ m_keybc->kbd_clk().set(m_ps2_con, FUNC(pc_kbdc_device::clock_write_from_mb));
+ m_keybc->kbd_data().set(m_ps2_con, FUNC(pc_kbdc_device::data_write_from_mb));
+ m_keybc->aux_irq().set(m_pic_slave, FUNC(pic8259_device::ir4_w));
+ m_keybc->aux_clk().set(m_aux_con, FUNC(pc_kbdc_device::clock_write_from_mb));
+ m_keybc->aux_data().set(m_aux_con, FUNC(pc_kbdc_device::data_write_from_mb));
+
+ // TODO: unknown RTC type
+ // Has external RTC bank select at $48, using this one as convenience
+ DS12885EXT(config, m_rtc, XTAL(32'768));
+ m_rtc->irq().set(m_pic_slave, FUNC(pic8259_device::ir0_w));
+ m_rtc->set_century_index(0x32);
+
+ // serial fragment
+ // TODO: unconfirmed type / clock
+ INS8250(config, m_uart, XTAL(18'432'000) / 10);
+ m_uart->out_tx_callback().set("com1", FUNC(rs232_port_device::write_txd));
+ m_uart->out_dtr_callback().set("com1", FUNC(rs232_port_device::write_dtr));
+ m_uart->out_rts_callback().set("com1", FUNC(rs232_port_device::write_rts));
+// m_uart->out_int_callback().set()
+// m_uart->out_baudout_callback().set([this](int state){ if (m_8251dtr_state) m_uart->rclk_w(state); }); // TODO: Fix INS8250 BAUDOUT pin support
+
+ rs232_port_device &rs232(RS232_PORT(config, "com1", default_rs232_devices, nullptr));
+ rs232.rxd_handler().set(m_uart, FUNC(ins8250_uart_device::rx_w));
+ rs232.dcd_handler().set(m_uart, FUNC(ins8250_uart_device::dcd_w));
+ rs232.dsr_handler().set(m_uart, FUNC(ins8250_uart_device::dsr_w));
+ rs232.ri_handler().set(m_uart, FUNC(ins8250_uart_device::ri_w));
+ rs232.cts_handler().set(m_uart, FUNC(ins8250_uart_device::cts_w));
+
+ // TODO: left/right speaker connection
+ SPEAKER(config, "mono").front_center();
+ SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.50);
+}
+
+void sis950_lpc_device::config_map(address_map &map)
+{
+ pci_device::config_map(map);
+ map(0x10, 0x4f).unmaprw();
+ map(0x10, 0x75).rw(FUNC(sis950_lpc_device::unmap_log_r), FUNC(sis950_lpc_device::unmap_log_w));
+
+ // LPC control regs
+ map(0x40, 0x40).rw(FUNC(sis950_lpc_device::bios_control_r), FUNC(sis950_lpc_device::bios_control_w));
+ map(0x41, 0x41).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_INTA>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_INTA>));
+ map(0x42, 0x42).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_INTB>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_INTB>));
+ map(0x43, 0x43).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_INTC>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_INTC>));
+ map(0x44, 0x44).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_INTD>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_INTD>));
+
+ map(0x45, 0x45).rw(FUNC(sis950_lpc_device::flash_ctrl_r), FUNC(sis950_lpc_device::flash_ctrl_w));
+ map(0x46, 0x46).rw(FUNC(sis950_lpc_device::init_enable_r), FUNC(sis950_lpc_device::init_enable_w));
+ map(0x47, 0x47).rw(FUNC(sis950_lpc_device::keybc_reg_r), FUNC(sis950_lpc_device::keybc_reg_w));
+ map(0x48, 0x48).rw(FUNC(sis950_lpc_device::rtc_reg_r), FUNC(sis950_lpc_device::rtc_reg_w));
+
+ // DMA control regs
+// map(0x49, 0x49) Distributed DMA channel enable
+// map(0x4a, 0x4b) Distributed DMA Master config
+
+ // Shadow regs (r/o)
+// map(0x4c, 0x4f) PIC master ICW*
+// map(0x50, 0x53) PIC slave ICW*
+// map(0x54, 0x55) PIC master OCW2-3
+// map(0x56, 0x57) PIC slave OCW2-3 (NB: assume documentation 0x54-0x55 to be a typo)
+// map(0x58, 0x5f) PIT counters 0-1-2 low/high, $5e -> control 43h, $5f -> read count pointer statuses
+// map(0x60, 0x60) EISA port $70
+
+ map(0x61, 0x61).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_IDE>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_IDE>));
+// map(0x62, 0x62) <reserved>, hardwired to 0x80 (PIT irq remap?)
+ map(0x62, 0x62).lr8(NAME([] () { return 0x80; }));
+ map(0x63, 0x63).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_GPE>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_GPE>));
+
+// map(0x64, 0x64) PCI bus priority timer
+// map(0x65, 0x65) PHOLD# timer
+
+// map(0x66, 0x66) <reserved>
+// map(0x67, 0x67) Serial IRQ 1 & 12 latch control (FDC super I/O)
+// map(0x68, 0x69) <reserved>
+
+ map(0x63, 0x63).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_ACPI>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_ACPI>));
+// map(0x6b, 0x6b) <reserved>
+ map(0x6c, 0x6c).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_SMBUS>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_SMBUS>));
+ map(0x6d, 0x6d).rw(FUNC(sis950_lpc_device::irq_remap_r<IRQ_SWDOG>), FUNC(sis950_lpc_device::irq_remap_w<IRQ_SWDOG>));
+
+// map(0x6e, 0x6f) SW irq triggers
+// map(0x70, 0x70) Serial irq control
+// map(0x71, 0x73) Serial irq enable
+
+ // $74 should be ACPI lower base bank, but marked as <reserved> regardless
+ // (by logic that should go to NOP too)
+ map(0x74, 0x74).lr8(NAME([] () { return 0; }));
+ map(0x75, 0x75).rw(FUNC(sis950_lpc_device::acpi_base_r), FUNC(sis950_lpc_device::acpi_base_w));
+}
+
+u8 sis950_lpc_device::bios_control_r()
+{
+ LOGIO("Read BIOS control [$40] %02x\n", m_bios_control);
+ return m_bios_control;
+}
+
+void sis950_lpc_device::bios_control_w(u8 data)
+{
+ // Mostly 0x9a or 0x9b
+ LOGIO("Write BIOS control [$40] %02x\n", data);
+ m_bios_control = data;
+ remap_cb();
+}
+
+template <unsigned N> u8 sis950_lpc_device::irq_remap_r()
+{
+ return m_irq_remap[N];
+}
+
+template <unsigned N> void sis950_lpc_device::irq_remap_w(u8 data)
+{
+ m_irq_remap[N] = data;
+ LOGIRQ("%s IRQ remap write %02x (%s)\n", std::array<char const *, 9> {{
+ "INTA",
+ "INTB",
+ "INTC",
+ "INTD",
+ "IDEIRQ",
+ "GPEIRQ",
+ "ACPI/SCI",
+ "SMBus",
+ "Software Watchdog"
+ }}[N], data, BIT(data, 7) ? "disable" : "enable");
+}
+
+u8 sis950_lpc_device::flash_ctrl_r()
+{
+ LOGIO("Read Flash control [$45] %02x\n", m_flash_control);
+ return m_flash_control;
+}
+
+void sis950_lpc_device::flash_ctrl_w(u8 data)
+{
+ LOGIO("Write Flash control [$45] %02x\n", data);
+ m_flash_control = data;
+ remap_cb();
+}
+
+u8 sis950_lpc_device::init_enable_r()
+{
+ LOGIO("Read INIT enable [$46] %02x\n", m_init_reg);
+ return m_init_reg;
+}
+
+/*
+ * 11-- ---- HW (fast?) reset
+ * --x- ---- INIT enable
+ * ---x ---- Fast Gate A20 emulation
+ * ---- x--- Fast Reset latency control
+ * ---- -x-- Fast Reset emulation
+ * ---- --x- (0) enable A20M# (1) A20M# always high (?)
+ * ---- ---x Keyboard HW reset
+ */
+void sis950_lpc_device::init_enable_w(u8 data)
+{
+ LOGIO("Write INIT enable [$46] %02x\n", data);
+ // HW fast reset
+ // TODO: is 0->1 implementation correct?
+ // it will otherwise keep resetting itself, which may be a side effect of something else
+ // (perhaps PS/2 controller can intercept this? Or it's a full on LPC reset like using an actual MAME soft reset implies?)
+ if ((data & 0xc0) == 0xc0)// && (m_init_reg & 0xc0) == 0)
+ {
+ //const int fast_reset_time = BIT(data, 3) ? 6 : 2;
+ LOGIO("Fast reset issued\n");
+ //m_host_cpu->pulse_input_line(INPUT_LINE_RESET, attotime::from_usec(fast_reset_time));
+ m_fast_reset_cb(1);
+ }
+
+ m_init_reg = data;
+}
+
+u8 sis950_lpc_device::keybc_reg_r()
+{
+ LOGIO("Read keyboard register [$47] %02x\n", m_keybc_reg);
+ return m_keybc_reg;
+}
+
+/*
+ * x--- ---- legacy USB (ports $60-$64 overrides)
+ * -x-- ---- PS/2 mouse lock enable
+ * --x- ---- Keyboard controller clock select (0) PCI Clock / 4 (1) 7.159 MHz
+ * ---x ---- Keyboard lock enable
+ * ---- x--- Integrated keyboard controller enable
+ * ---- -x-- Integrated PS/2 mouse enable (needs bit 3 enabled)
+ * ---- --x- Keyboard hot key status (needs bit 3 enabled, ctrl+alt+backspace)
+ * ---- ---x Keyboard hot key control
+ */
+void sis950_lpc_device::keybc_reg_w(u8 data)
+{
+ LOGIO("Write keyboard register [$47] %02x\n", data);
+ m_keybc_reg = data;
+}
+
+u8 sis950_lpc_device::rtc_reg_r()
+{
+ LOGIO("Read RTC register [$48] %02x\n", m_keybc_reg);
+ return m_rtc_reg | 0x10;
+}
+
+/*
+ * x--- ---- RTC extended bank enable
+ * -x-- ---- APC enable
+ * --x- ---- Instant Power-Off enable (thru APC)
+ * ---x ---- Internal RTC status (r/o, always on?)
+ * ---- xxxx <reserved>
+ */
+void sis950_lpc_device::rtc_reg_w(u8 data)
+{
+ LOGIO("Write RTC register [$48] %02x\n", data);
+ m_rtc_reg = data;
+}
+
+u8 sis950_lpc_device::acpi_base_r()
+{
+ LOGIO("Read ACPI base [$75] %04x\n", m_acpi_base);
+ return m_acpi_base >> 8;
+}
+
+void sis950_lpc_device::acpi_base_w(u8 data)
+{
+ u16 new_value = data << 8;
+ LOGIO("Write ACPI base [$75] %04x\n", new_value);
+ m_acpi_base = new_value;
+ remap_cb();
+}
+
+template <unsigned N> void sis950_lpc_device::memory_map(address_map &map)
+{
+ map(0x00000000, 0x0001ffff).lrw8(
+ NAME([this] (offs_t offset) { return m_flash_rom->read(offset | N * 0x20000); }),
+ NAME([this] (offs_t offset, u8 data) { m_flash_rom->write(offset | N * 0x20000, data); })
+ );
+}
+
+void sis950_lpc_device::io_map(address_map &map)
+{
+ // Legacy ISA (page 177)
+ // map(0x0000, 0x000f) DMA1
+ map(0x0000, 0x001f).rw(m_dmac_master, FUNC(am9517a_device::read), FUNC(am9517a_device::write));
+ // map(0x0020, 0x0021) INT1
+ map(0x0020, 0x003f).rw(m_pic_master, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
+ // map(0x0040, 0x0043) PIT
+ map(0x0040, 0x0043).rw(m_pit, FUNC(pit8254_device::read), FUNC(pit8254_device::write));
+ map(0x0060, 0x0060).rw(m_keybc, FUNC(ps2_keyboard_controller_device::data_r), FUNC(ps2_keyboard_controller_device::data_w));
+ // map(0x0061, 0x0061) NMI Status Register
+ map(0x0061, 0x0061).rw(FUNC(sis950_lpc_device::nmi_status_r), FUNC(sis950_lpc_device::nmi_control_w));
+ // undocumented but read, assume LPC complaint
+ map(0x0064, 0x0064).rw(m_keybc, FUNC(ps2_keyboard_controller_device::status_r), FUNC(ps2_keyboard_controller_device::command_w));
+ // map(0x0070, 0x0070) CMOS and NMI Mask
+ map(0x0070, 0x0070).w(FUNC(sis950_lpc_device::rtc_index_w));
+ map(0x0071, 0x0071).rw(FUNC(sis950_lpc_device::rtc_data_r), FUNC(sis950_lpc_device::rtc_data_w));
+ // map(0x0080, 0x008f) DMA low page registers
+ map(0x0080, 0x008f).rw(FUNC(sis950_lpc_device::at_page8_r), FUNC(sis950_lpc_device::at_page8_w));
+ // map(0x0092, 0x0092) INIT and A20
+ map(0x0092, 0x0092).rw(FUNC(sis950_lpc_device::lpc_fast_init_r), FUNC(sis950_lpc_device::lpc_fast_init_w));
+ // map(0x00a0, 0x00a1) INT2
+ map(0x00a0, 0x00bf).rw(m_pic_slave, FUNC(pic8259_device::read), FUNC(pic8259_device::write));
+ // map(0x00c0, 0x00df) DMA2
+ map(0x00c0, 0x00df).lrw8(
+ NAME([this] (offs_t offset) { return m_dmac_slave->read( offset / 2 ); }),
+ NAME([this] (offs_t offset, u8 data) { m_dmac_slave->write( offset / 2, data ); })
+ );
+
+ map(0x00e0, 0x00ef).noprw();
+ // map(0x00f0, 0x00f0) COPRO error
+ // map(0x0480, 0x048f) DMA high page registers
+ // map(0x04d0, 0x04d1) IRQ edge/level control registers
+
+ // http://bxr.su/DragonFly/share/man/man4/it.4
+ // 0x0290, 0xc00, 0xd00, 0x228: Motherboard Super I/O HW monitoring
+
+ // map(0x0295, 0x0295) - index register, $d-$e lower/upper RPM readback for fans (alternating on read)
+ // map(0x0296, 0x0296) - data ^
+
+ // Intel LPC interface specs (legacy host decode ranges, not necessarily present on '950)
+
+ // map(0x002e, 0x002f) Super I/O config (config-index & data ports)
+ // map(0x004e, 0x004f) alt Super I/O config (ISA converter index-data ports)
+ // map(0x0062, 0x0062) - ACPI embedded controller
+ // map(0x0066, 0x0066) /
+
+ // map(0x0200, 0x020f) game ports ($201 as shutms11 default)
+
+ // map(0x0220, 0x0227) serial 2
+ // map(0x0228, 0x022f) serial 2
+ // map(0x0220, 0x0233) sb compatible i/f 1
+ // map(0x0240, 0x0253) sb compatible i/f 2
+ // map(0x0260, 0x0273) sb compatible i/f 3
+ // map(0x0280, 0x0293) sb compatible i/f 4
+
+ // map(0x0238, 0x023f) serial 3
+
+ // map(0x0278, 0x027f) parallel port 2 & PnP
+ // map(0x02e8, 0x02ef) serial 3
+ // map(0x02f8, 0x02ff) serial 1
+
+ // map(0x0300, 0x0301) - MIDI ($330 as shutms11 default)
+ // map(0x0310, 0x0311) /
+ // map(0x0320, 0x0321) /
+ // map(0x0330, 0x0331) /
+
+ // map(0x0338, 0x033f) serial 4
+ // map(0x0370, 0x0377) FDC 2
+
+ // map(0x0378, 0x037f) parallel port 1
+ // map(0x0388, 0x0389) ADLIB
+ // map(0x03bc, 0x03bf) parallel port 3
+ // map(0x03e8, 0x03ef) serial 4
+ // map(0x03f0, 0x03f7) FDC 1
+ map(0x03f8, 0x03ff).rw(m_uart, FUNC(ins8250_device::ins8250_r), FUNC(ins8250_device::ins8250_w)); // COM1
+
+ // map(0x0530, 0x0537) - MSS (TCP Maximum Segment Size?)
+ // map(0x0604, 0x060b) /
+ // map(0x0e80, 0x0e87) /
+ // map(0x0f40, 0x0f47) /
+
+ // map(0x0678, 0x067f) ECP parallel port 1
+ // map(0x0778, 0x0779) ECP parallel port 2 & PnP
+ // map(0x07bc, 0x07bf) ECP parallel port 3
+}
+
+void sis950_lpc_device::map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space)
+{
+ io_space->install_device(0, 0x07ff, *this, &sis950_lpc_device::io_map);
+
+ LOGMAP("LPC Remapping table (BIOS: %02x, flash: %02x)\n", m_bios_control, m_flash_control);
+
+ // ACPI enable
+ if (BIT(m_bios_control, 7))
+ {
+ LOGMAP("- ACPI enable (%02x) %04x-%04x\n", m_bios_control, m_acpi_base, m_acpi_base + 0xff);
+ // shutms11 BIOS POST maps this at $5000
+ m_acpi->map_device(memory_window_start, memory_window_end, 0, memory_space, io_window_start, io_window_end, m_acpi_base, io_space);
+ io_space->install_device(m_acpi_base | 0x80, m_acpi_base | 0xff, *m_smbus, &sis950_smbus_device::map);
+ }
+
+ // TODO: disable flash access write thru reg $45
+ // TODO: BIOS positive decode
+ // (bios_control bit 1), which should disable BIOS mapping to E and F segment
+
+ memory_space->install_device(0x000e0000, 0x000fffff, *this, &sis950_lpc_device::memory_map<3>);
+ // extended BIOS enable
+ if (m_bios_control & 1)
+ {
+ LOGMAP("- Extend BIOS on\n");
+ memory_space->install_device(0xfff80000, 0xfff9ffff, *this, &sis950_lpc_device::memory_map<0>);
+ memory_space->install_device(0xfffa0000, 0xfffbffff, *this, &sis950_lpc_device::memory_map<1>);
+ memory_space->install_device(0xfffc0000, 0xfffdffff, *this, &sis950_lpc_device::memory_map<2>);
+ }
+ memory_space->install_device(0xfffe0000, 0xffffffff, *this, &sis950_lpc_device::memory_map<3>);
+}
+
+u8 sis950_lpc_device::lpc_fast_init_r()
+{
+ LOGLPC("LPC fast init read [$92]\n");
+ return m_lpc_legacy.fast_init;
+}
+
+void sis950_lpc_device::lpc_fast_init_w(offs_t offset, u8 data)
+{
+ LOGLPC("LPC fast init write [$92] %02x\n", data);
+ if (data & 0xfd)
+ LOG("Warning: unemulated LPC fast init type %02x", data);
+
+ // TODO: pinpoint exact disable INIT condition and if that will be reflected on reading reg too
+ m_host_cpu->set_input_line(INPUT_LINE_A20, BIT(data, 1));
+
+ m_lpc_legacy.fast_init = data;
+}
+
+/*
+ * Debugging
+ */
+u8 sis950_lpc_device::unmap_log_r(offs_t offset)
+{
+ LOGTODO("LPC Unemulated [%02x] R\n", offset + 0x10);
+ return 0;
+}
+
+void sis950_lpc_device::unmap_log_w(offs_t offset, u8 data)
+{
+ LOGTODO("LPC Unemulated [%02x] %02x W\n", offset + 0x10, data);
+}
+
+/*
+ * Start of legacy handling, to be moved out
+ */
+
+WRITE_LINE_MEMBER( sis950_lpc_device::pit_out0 )
+{
+ m_pic_master->ir0_w(state);
+}
+
+WRITE_LINE_MEMBER( sis950_lpc_device::pit_out1 )
+{
+ if(state)
+ m_refresh = !m_refresh;
+}
+
+WRITE_LINE_MEMBER( sis950_lpc_device::pit_out2 )
+{
+ m_pit_out2 = state ? 1 : 0;
+ m_speaker->level_w(m_at_spkrdata & m_pit_out2);
+}
+
+/*
+ * x--- ---- Set if a PCI device or main memory module asserts PERR#/SERR# line
+ * -x-- ---- Set when IOCHK# is asserted on ISA bus
+ * --x- ---- Counter 2 out signal state (PIT ch. 2)
+ * ---x ---- Refresh bit toggle (PIT ch. 1)
+ * ---- xxxx <reads back NMI control enable>
+ */
+uint8_t sis950_lpc_device::nmi_status_r()
+{
+ uint8_t data = m_at_speaker;
+ data &= ~0xd0; // AT BIOS don't likes this being set
+
+ data |= m_refresh ? 0x10 : 0;
+
+ if (m_pit_out2)
+ data |= 0x20;
+ else
+ data &= ~0x20; // ps2m30 wants this
+
+ return data;
+}
+
+void sis950_lpc_device::at_speaker_set_spkrdata(uint8_t data)
+{
+ m_at_spkrdata = data ? 1 : 0;
+ m_speaker->level_w(m_at_spkrdata & m_pit_out2);
+}
+
+/*
+ * xxxx ---- <must be zero when writing to this port>
+ * ---- x--- (0) enable IOCHK# NMI (1) clear and disable IOCHK# NMI
+ * ---- -x-- (0) enable PCI SERR# (1) clear and disable PCI SERR#
+ * ---- --x- Speaker output enable
+ * ---- ---x Timer counter 2 enable
+ */
+void sis950_lpc_device::nmi_control_w(uint8_t data)
+{
+ m_at_speaker = data;
+ m_pit->write_gate2(BIT(data, 0));
+ at_speaker_set_spkrdata(BIT(data, 1));
+ m_channel_check = BIT(data, 3);
+ if (m_channel_check)
+ m_host_cpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
+}
+
+void sis950_lpc_device::rtc_index_w(uint8_t data)
+{
+ m_rtc_index = data & 0x7f;
+ // bit 7: NMI enable
+}
+
+u8 sis950_lpc_device::rtc_data_r()
+{
+ const u8 rtc_address = m_rtc_index | (m_rtc_reg & 0x80);
+ return m_rtc->read_direct(rtc_address);
+}
+
+void sis950_lpc_device::rtc_data_w(u8 data)
+{
+ const u8 rtc_address = m_rtc_index | (m_rtc_reg & 0x80);
+ m_rtc->write_direct(rtc_address, data);
+}
+
+uint8_t sis950_lpc_device::at_page8_r(offs_t offset)
+{
+ uint8_t data = m_at_pages[offset % 0x10];
+
+ switch(offset % 8)
+ {
+ case 1:
+ data = m_dma_offset[BIT(offset, 3)][2];
+ break;
+ case 2:
+ data = m_dma_offset[BIT(offset, 3)][3];
+ break;
+ case 3:
+ data = m_dma_offset[BIT(offset, 3)][1];
+ break;
+ case 7:
+ data = m_dma_offset[BIT(offset, 3)][0];
+ break;
+ }
+ return data;
+}
+
+
+void sis950_lpc_device::at_page8_w(offs_t offset, uint8_t data)
+{
+ m_at_pages[offset % 0x10] = data;
+
+ switch(offset % 8)
+ {
+ case 0:
+ // matches boot_state_infos_phoenix
+ //m_boot_state_hook((offs_t)0, data);
+ break;
+ case 1:
+ m_dma_offset[BIT(offset, 3)][2] = data;
+ break;
+ case 2:
+ m_dma_offset[BIT(offset, 3)][3] = data;
+ break;
+ case 3:
+ m_dma_offset[BIT(offset, 3)][1] = data;
+ break;
+ case 7:
+ m_dma_offset[BIT(offset, 3)][0] = data;
+ break;
+ }
+}
+
+WRITE_LINE_MEMBER( sis950_lpc_device::pc_dma_hrq_changed )
+{
+ m_host_cpu->set_input_line(INPUT_LINE_HALT, state ? ASSERT_LINE : CLEAR_LINE);
+
+ // Assert HLDA
+ m_dmac_slave->hack_w( state );
+}
+
+#if 0
+WRITE_LINE_MEMBER( sis950_lpc_device::iochck_w )
+{
+// if (!state && !m_channel_check && m_nmi_enabled)
+ if (!state && !m_channel_check)
+ m_host_cpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
+}
+#endif
+
+uint8_t sis950_lpc_device::pc_dma_read_byte(offs_t offset)
+{
+ address_space& prog_space = m_host_cpu->space(AS_PROGRAM); // get the right address space
+ if(m_dma_channel == -1)
+ return 0xff;
+ uint8_t result;
+ offs_t page_offset = ((offs_t) m_dma_offset[0][m_dma_channel]) << 16;
+
+ result = prog_space.read_byte(page_offset + offset);
+ return result;
+}
+
+
+void sis950_lpc_device::pc_dma_write_byte(offs_t offset, uint8_t data)
+{
+ address_space& prog_space = m_host_cpu->space(AS_PROGRAM); // get the right address space
+ if(m_dma_channel == -1)
+ return;
+ offs_t page_offset = ((offs_t) m_dma_offset[0][m_dma_channel]) << 16;
+
+ prog_space.write_byte(page_offset + offset, data);
+}
+
+
+uint8_t sis950_lpc_device::pc_dma_read_word(offs_t offset)
+{
+ address_space& prog_space = m_host_cpu->space(AS_PROGRAM); // get the right address space
+ if(m_dma_channel == -1)
+ return 0xff;
+ uint16_t result;
+ offs_t page_offset = ((offs_t) m_dma_offset[1][m_dma_channel & 3]) << 16;
+
+ result = prog_space.read_word((page_offset & 0xfe0000) | (offset << 1));
+ m_dma_high_byte = result & 0xFF00;
+
+ return result & 0xFF;
+}
+
+
+void sis950_lpc_device::pc_dma_write_word(offs_t offset, uint8_t data)
+{
+ address_space& prog_space = m_host_cpu->space(AS_PROGRAM); // get the right address space
+ if(m_dma_channel == -1)
+ return;
+ offs_t page_offset = ((offs_t) m_dma_offset[1][m_dma_channel & 3]) << 16;
+
+ prog_space.write_word((page_offset & 0xfe0000) | (offset << 1), m_dma_high_byte | data);
+}
diff --git a/src/devices/machine/sis950_lpc.h b/src/devices/machine/sis950_lpc.h
new file mode 100644
index 00000000000..4e994110b9b
--- /dev/null
+++ b/src/devices/machine/sis950_lpc.h
@@ -0,0 +1,176 @@
+// license: BSD-3-Clause
+// copyright-holders: Angelo Salese
+
+#ifndef MAME_MACHINE_SIS950_LPC_H
+#define MAME_MACHINE_SIS950_LPC_H
+
+#pragma once
+
+#include "pci.h"
+
+#include "bus/ata/ataintf.h"
+#include "bus/isa/isa.h"
+#include "bus/pc_kbd/pc_kbdc.h"
+#include "bus/rs232/rs232.h"
+#include "lpc-acpi.h"
+#include "sis950_smbus.h"
+
+#include "cpu/i386/i386.h"
+
+#include "machine/am9517a.h"
+#include "machine/at.h"
+#include "machine/at_keybc.h"
+#include "machine/ds128x.h"
+#include "machine/ins8250.h"
+#include "machine/intelfsh.h"
+#include "machine/pc_lpt.h"
+#include "machine/pic8259.h"
+#include "machine/pit8253.h"
+#include "machine/ram.h"
+#include "machine/nvram.h"
+
+#include "sound/spkrdev.h"
+
+
+class sis950_lpc_device : public pci_device
+{
+public:
+ template <typename T, typename U> sis950_lpc_device(
+ const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock,
+ T &&cpu_tag, U &&flash_tag
+ ) : sis950_lpc_device(mconfig, tag, owner, clock)
+ {
+ // Revision 0 -> A0
+ set_ids(0x10390008, 0x00, 0x060100, 0x00);
+ //set_multifunction_device(true);
+ m_host_cpu.set_tag(std::forward<T>(cpu_tag));
+ m_flash_rom.set_tag(std::forward<U>(flash_tag));
+ }
+
+ sis950_lpc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ static constexpr feature_type unemulated_features() { return feature::MOUSE; }
+
+ auto fast_reset_cb() { return m_fast_reset_cb.bind(); }
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_add_mconfig(machine_config &config) override;
+
+// virtual void reset_all_mappings() override;
+
+ virtual void map_extra(uint64_t memory_window_start, uint64_t memory_window_end, uint64_t memory_offset, address_space *memory_space,
+ uint64_t io_window_start, uint64_t io_window_end, uint64_t io_offset, address_space *io_space) override;
+
+ virtual void config_map(address_map &map) override;
+
+ template <unsigned N> void memory_map(address_map &map);
+ void io_map(address_map &map);
+
+private:
+ required_device<cpu_device> m_host_cpu;
+ required_device<intelfsh8_device> m_flash_rom;
+ required_device<pic8259_device> m_pic_master;
+ required_device<pic8259_device> m_pic_slave;
+ required_device<am9517a_device> m_dmac_master;
+ required_device<am9517a_device> m_dmac_slave;
+ required_device<pit8254_device> m_pit;
+ required_device<ps2_keyboard_controller_device> m_keybc;
+ required_device<speaker_sound_device> m_speaker;
+ required_device<ds12885ext_device> m_rtc;
+ required_device<pc_kbdc_device> m_ps2_con;
+ required_device<pc_kbdc_device> m_aux_con;
+ required_device<ins8250_device> m_uart;
+ required_device<lpc_acpi_device> m_acpi;
+ required_device<sis950_smbus_device> m_smbus;
+
+ devcb_write_line m_fast_reset_cb;
+
+ // PCI interface
+ u8 bios_control_r();
+ void bios_control_w(u8 data);
+ u8 flash_ctrl_r();
+ void flash_ctrl_w(u8 data);
+ u8 acpi_base_r();
+ void acpi_base_w(u8 data);
+ u8 init_enable_r();
+ void init_enable_w(u8 data);
+ u8 keybc_reg_r();
+ void keybc_reg_w(u8 data);
+ u8 rtc_reg_r();
+ void rtc_reg_w(u8 data);
+ void rtc_index_w(u8 data);
+ u8 rtc_data_r();
+ void rtc_data_w(u8 data);
+ template <unsigned N> u8 irq_remap_r();
+ template <unsigned N> void irq_remap_w(u8 data);
+ u8 unmap_log_r(offs_t offset);
+ void unmap_log_w(offs_t offset, u8 data);
+
+ u8 m_bios_control = 0;
+ u8 m_flash_control = 0;
+ u16 m_acpi_base = 0x0000;
+ u8 m_init_reg = 0;
+ u8 m_keybc_reg = 0;
+ u8 m_rtc_reg = 0;
+ u8 m_rtc_index = 0;
+
+ enum {
+ IRQ_INTA = 0,
+ IRQ_INTB,
+ IRQ_INTC,
+ IRQ_INTD,
+ IRQ_IDE,
+ IRQ_GPE,
+ // or SCI
+ IRQ_ACPI,
+ IRQ_SMBUS,
+ IRQ_SWDOG
+ };
+ u8 m_irq_remap[9]{};
+
+ // LPC vendor specific, verify if it's common for all
+ u8 lpc_fast_init_r();
+ void lpc_fast_init_w(offs_t offset, u8 data);
+ struct {
+ u8 fast_init;
+ } m_lpc_legacy;
+
+ // SB implementation, to be moved out
+ DECLARE_WRITE_LINE_MEMBER(pit_out0);
+ DECLARE_WRITE_LINE_MEMBER(pit_out1);
+ DECLARE_WRITE_LINE_MEMBER(pit_out2);
+ uint8_t pc_dma_read_byte(offs_t offset);
+ void pc_dma_write_byte(offs_t offset, uint8_t data);
+ uint8_t pc_dma_read_word(offs_t offset);
+ void pc_dma_write_word(offs_t offset, uint8_t data);
+ DECLARE_WRITE_LINE_MEMBER(pc_dma_hrq_changed);
+ void pc_select_dma_channel(int channel, bool state);
+
+ uint8_t m_at_pages[0x10]{};
+ uint8_t m_dma_offset[2][4]{};
+ uint8_t m_at_speaker = 0;
+ uint8_t m_refresh = 0;
+ bool m_pit_out2 = 0;
+ bool m_at_spkrdata = 0;
+ uint8_t m_channel_check = 0;
+ int m_dma_channel = -1;
+// bool m_cur_eop = false;
+ uint16_t m_dma_high_byte = 0;
+
+ DECLARE_WRITE_LINE_MEMBER(cpu_a20_w);
+ DECLARE_WRITE_LINE_MEMBER(cpu_reset_w);
+
+ uint8_t at_page8_r(offs_t offset);
+ void at_page8_w(offs_t offset, uint8_t data);
+ u8 nmi_status_r();
+ void nmi_control_w(uint8_t data);
+
+ void at_speaker_set_spkrdata(uint8_t data);
+};
+
+DECLARE_DEVICE_TYPE(SIS950_LPC, sis950_lpc_device)
+
+
+#endif
diff --git a/src/devices/machine/sis950_smbus.cpp b/src/devices/machine/sis950_smbus.cpp
new file mode 100644
index 00000000000..b96898cbbe0
--- /dev/null
+++ b/src/devices/machine/sis950_smbus.cpp
@@ -0,0 +1,108 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese
+/**************************************************************************************************
+
+ SiS 950 SMBus implementation
+
+ TODO:
+ - Stub, needs an smbus_interface, bare minimum to make shutms11 happy;
+
+**************************************************************************************************/
+#include "emu.h"
+#include "sis950_smbus.h"
+
+#define LOG_CMD (1U << 1) // log commands
+
+#define VERBOSE (LOG_GENERAL | LOG_CMD)
+//#define LOG_OUTPUT_FUNC osd_printf_warning
+
+#include "logmacro.h"
+
+#define LOGCMD(...) LOGMASKED(LOG_CMD, __VA_ARGS__)
+
+DEFINE_DEVICE_TYPE(SIS950_SMBUS, sis950_smbus_device, "sis950_smbus", "SiS950 SMBus interface")
+
+void sis950_smbus_device::map(address_map &map)
+{
+ map(0x00, 0x00).rw(FUNC(sis950_smbus_device::smb_sts_r), FUNC(sis950_smbus_device::smb_sts_w));
+
+ map(0x03, 0x03).rw(FUNC(sis950_smbus_device::smbhost_cnt_r), FUNC(sis950_smbus_device::smbhost_cnt_w));
+
+#if 0
+ map(0x02, 0x02).rw(FUNC(sis950_smbus_device::hst_cnt_r), FUNC(sis950_smbus_device::hst_cnt_w));
+ map(0x03, 0x03).rw(FUNC(sis950_smbus_device::hst_cmd_r), FUNC(sis950_smbus_device::hst_cmd_w));
+ map(0x04, 0x04).rw(FUNC(sis950_smbus_device::xmit_slva_r), FUNC(sis950_smbus_device::xmit_slva_w));
+ map(0x05, 0x05).rw(FUNC(sis950_smbus_device::hst_d0_r), FUNC(sis950_smbus_device::hst_d0_w));
+ map(0x06, 0x06).rw(FUNC(sis950_smbus_device::hst_d1_r), FUNC(sis950_smbus_device::hst_d1_w));
+ map(0x07, 0x07).rw(FUNC(sis950_smbus_device::host_block_db_r), FUNC(sis950_smbus_device::host_block_db_w));
+ map(0x08, 0x08).rw(FUNC(sis950_smbus_device::pec_r), FUNC(sis950_smbus_device::pec_w));
+ map(0x09, 0x09).rw(FUNC(sis950_smbus_device::rcv_slva_r), FUNC(sis950_smbus_device::rcv_slva_w));
+ map(0x0a, 0x0b).rw(FUNC(sis950_smbus_device::slv_data_r), FUNC(sis950_smbus_device::slv_data_w));
+ map(0x0c, 0x0c).rw(FUNC(sis950_smbus_device::aux_sts_r), FUNC(sis950_smbus_device::aux_sts_w));
+ map(0x0d, 0x0d).rw(FUNC(sis950_smbus_device::aux_ctl_r), FUNC(sis950_smbus_device::aux_ctl_w));
+ map(0x0e, 0x0e).rw(FUNC(sis950_smbus_device::smlink_pin_ctl_r), FUNC(sis950_smbus_device::smlink_pin_ctl_w));
+ map(0x0f, 0x0f).rw(FUNC(sis950_smbus_device::smbus_pin_ctl_r), FUNC(sis950_smbus_device::smbus_pin_ctl_w));
+ map(0x10, 0x10).rw(FUNC(sis950_smbus_device::slv_sts_r), FUNC(sis950_smbus_device::slv_sts_w));
+ map(0x11, 0x11).rw(FUNC(sis950_smbus_device::slv_cmd_r), FUNC(sis950_smbus_device::slv_cmd_w));
+ map(0x14, 0x14).r(FUNC(sis950_smbus_device::notify_daddr_r));
+ map(0x16, 0x16).r(FUNC(sis950_smbus_device::notify_dlow_r));
+ map(0x17, 0x17).r(FUNC(sis950_smbus_device::notify_dhigh_r));
+#endif
+}
+
+sis950_smbus_device::sis950_smbus_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, SIS950_SMBUS, tag, owner, clock)
+{
+}
+
+void sis950_smbus_device::device_start()
+{
+
+}
+
+void sis950_smbus_device::device_reset()
+{
+
+}
+
+u8 sis950_smbus_device::smb_sts_r()
+{
+ LOGCMD("%s: smb_sts_r (%02x)\n", tag(), m_status);
+
+ return m_status;
+}
+
+void sis950_smbus_device::smb_sts_w(u8 data)
+{
+ m_status &= ~data;
+ LOGCMD("%s: smb_sts_w = %02x\n", tag(), data);
+}
+
+// reads back bits 0-2 only
+u8 sis950_smbus_device::smbhost_cnt_r()
+{
+ return m_cmd;
+}
+
+/*
+ * --x- ---- kill (reset state, TODO)
+ * ---x ---- start transaction
+ * ---- -xxx command protocol
+ * ---- -000 quick command
+ * ---- -001 send/receive byte
+ * ---- -010 read/write byte data
+ * ---- -011 read/write word data
+ * ---- -100 process call
+ * ---- -101 read/write block data
+ * ---- -11x <reserved>
+ */
+void sis950_smbus_device::smbhost_cnt_w(u8 data)
+{
+ m_cmd = data & 7;
+ LOGCMD("%s: smbhost_cnt_w = %02x\n", tag(), data);
+
+ // TODO: process commands properly
+ // For now we just reflect shutms11 wanting bit 3 on after each transaction
+ if (BIT(data, 4))
+ m_status |= 8;
+}
diff --git a/src/devices/machine/sis950_smbus.h b/src/devices/machine/sis950_smbus.h
new file mode 100644
index 00000000000..a474fe10914
--- /dev/null
+++ b/src/devices/machine/sis950_smbus.h
@@ -0,0 +1,32 @@
+// license:BSD-3-Clause
+// copyright-holders:Angelo Salese
+
+#ifndef MAME_MACHINE_SIS950_SMBUS_H
+#define MAME_MACHINE_SIS950_SMBUS_H
+
+#include "emu.h"
+
+class sis950_smbus_device : public device_t {
+public:
+
+ sis950_smbus_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ void map(address_map &map);
+
+protected:
+ virtual void device_start() override;
+ virtual void device_reset() override;
+
+private:
+ u8 smb_sts_r();
+ void smb_sts_w(u8 data);
+ u8 smbhost_cnt_r();
+ void smbhost_cnt_w(u8 data);
+
+ u8 m_status = 0;
+ u8 m_cmd = 0;
+};
+
+DECLARE_DEVICE_TYPE(SIS950_SMBUS, sis950_smbus_device)
+
+#endif // MAME_MACHINE_PCI_SMBUS_H