mame/src/devices/machine/sis950_lpc.cpp
Angelo Salese e6180e3510
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]
2022-05-23 17:27:33 +02:00

765 lines
25 KiB
C++

// 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);
}