diff options
Diffstat (limited to 'src/devices/machine/sis950_lpc.cpp')
-rw-r--r-- | src/devices/machine/sis950_lpc.cpp | 764 |
1 files changed, 764 insertions, 0 deletions
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); +} |