diff options
author | Patrick Mackinlay <pmackinlay@hotmail.com> | 2019-07-03 20:50:27 +0700 |
---|---|---|
committer | Patrick Mackinlay <pmackinlay@hotmail.com> | 2019-07-03 20:50:27 +0700 |
commit | bad674aca2c06981c8984015ed6583b02188515d (patch) | |
tree | f0800040071206a022aed8df765c29b3f11d965e | |
parent | f8c1f01694571cb8ab369d60b15977d1ac6912ea (diff) |
4dpi: wip (nw)
* lance and scsi working
* monitor, fx and sash run (latter two from cdrom or network)
* irix boots from miniroot but hangs
-rw-r--r-- | src/mame/drivers/4dpi.cpp | 318 |
1 files changed, 261 insertions, 57 deletions
diff --git a/src/mame/drivers/4dpi.cpp b/src/mame/drivers/4dpi.cpp index 5416bdf9d50..801eea75c7f 100644 --- a/src/mame/drivers/4dpi.cpp +++ b/src/mame/drivers/4dpi.cpp @@ -22,18 +22,22 @@ ****************************************************************************/ /* * Sources: + * - http://www.bitsavers.org/pdf/sgi/personal_iris/SGI_IP-6_Schematic.pdf * - http://www.futuretech.blinkenlights.nl/pitechrep.html * - https://hardware.majix.org/computers/sgi.pi/index.shtml * - http://archive.irix.cc/sgistuff/hardware/systems/personal.html + * - http://archive.irix.cc/developerforum95/Silicon_Graphics_Developer_Forum_95_The_CD_Volume_2/documents/hw_handbook_html/handbook.html * - https://github.com/NetBSD/src/tree/trunk/sys/arch/sgimips/ * * TODO: * - IOC1 and CTL1 - * - graphics, keyboard, mouse, audio + * - graphics, audio * * Status: - * - hangs waiting for wd33c93 reset interrupt - * - if bypassed, hangs waiting on a timer(?) at bfa40004 + * - eeprom is glitchy/unreliable + * - parity and cache diagnostics fail + * - boots monitor and fx/sash from cdrom or network + * - hangs after booting irix from miniroot */ #include "emu.h" @@ -55,6 +59,8 @@ #include "machine/nscsi_hd.h" #include "machine/nscsi_cd.h" #include "bus/rs232/rs232.h" +#include "bus/sgikbd/sgikbd.h" +#include "bus/rs232/hlemouse.h" // video and audio #include "screen.h" @@ -75,9 +81,10 @@ public: , m_rtc(*this, "rtc") , m_pit(*this, "pit") , m_scsi(*this, "scsi:0:wd33c93") - , m_net(*this, "net") + , m_enet(*this, "enet") , m_duart(*this, "duart%u", 0U) - , m_serial(*this, "serial%u", 0U) + , m_serial(*this, "serial%u", 1U) + , m_leds(*this, "led%u", 0U) { } @@ -92,30 +99,92 @@ private: required_device<dp8573_device> m_rtc; required_device<pit8254_device> m_pit; required_device<wd33c93_device> m_scsi; - required_device<am7990_device> m_net; + required_device<am7990_device> m_enet; required_device_array<scn2681_device, 2> m_duart; required_device_array<rs232_port_device, 2> m_serial; + enum leds : unsigned + { + LED_HBT = 0, // heartbeat (1Hz) + LED_CPU = 1, // cpu activity + LED_GFX = 2, // graphics + LED_FPU = 3, // fpu present + }; + output_finder<4> m_leds; + void map(address_map &map); + template <unsigned N> void lio_interrupt(int state) { lio_interrupt(N, state); } + void lio_interrupt(unsigned number, int state); + void scsi_drq(int state); + + enum sysid_mask : u8 + { + SID_SERDATA = 0x01, // serial memory data output state + SID_FPPRES = 0x02, // floating point processor present + SID_SERCLK = 0x04, // serial memory clock + SID_GDMAERR = 0x08, // error in graphics dma + SID_GDMAEN = 0x10, // graphics dma busy + SID_GDMARDY = 0x20, // asserted at end of graphics dma + SID_GDMARST = 0x40, // asserted in reset of graphics dma + SID_VMERMW = 0x80, // asserted in vme read-modify-write + }; + + enum lio_int_number : unsigned + { + LIO_D0 = 0, // duart 0 interrupt + LIO_D1 = 1, // duart 1 interrupt + LIO_VR = 2, // vertical retrace interrupt + LIO_CENTR = 3, // parallel port interrupt + LIO_SCSI = 4, // scsi interrupt + LIO_ENET = 5, // ethernet interrupt + LIO_GE = 6, // ge interrupt + LIO_FIFO = 7, // fifo full interrupt + LIO_AC = 8, // vme ac fail interrupt + LIO_VRSTAT = 9, // vert retrace status: no interrupt + }; + + enum mem_cfg_mask : u8 + { + MCF_4MRAM = 0x10, + MCF_MEMSIZE = 0x1f, + MCF_TIMERDIS = 0x20, // reduce peripheral r/w strobe + MCF_FMEM = 0x40, // reduce cas pulse on reads + MCF_REFDIS = 0x80, // disable memory refresh + }; + + u8 m_mem_cfg; u8 m_ctl_sysid; + u8 m_vme_isr; + u8 m_vme_imr; u8 m_aux_cpuctrl; u16 m_ctl_cpuctrl; + attotime m_refresh_timer; - u8 m_int_mask; + u16 m_lio_isr; + u8 m_lio_imr; + bool m_lio_int; + + u16 m_scsi_dmalo; + unsigned m_scsi_dmapage; + std::unique_ptr<u16 []> m_dma_map; }; void ip6_state::map(address_map &map) { - //map(0x10000000, 0x1bffffff); // a32 kernel vme bus, vme modifier 0x09 - //map(0x1c000000, 0x1cffffff); // a24 kernel vme bus, vme modifier 0x3d - //map(0x1e000000, 0x1effffff); // a24 kernel vme bus, vme modifier 0x39 + //map(0x10000000, 0x1bffffff); // vme a32 modifier 0x09 non-privileged + //map(0x1c000000, 0x1cffffff); // vme a24 modifier 0x3d privileged + //map(0x1e000000, 0x1effffff); // vme a24 modifier 0x39 non-privileged + //map(0x1d000000, 0x1d00ffff); // vme a16 modifier 0x2d privileged + //map(0x1d100000, 0x1d10ffff); // vme a16 modifier 0x29 non-privileged - //map(0x1f900000, 0x1f900000); // i/o controller (also lance and scsi) + //map(0x1df00000, 0x1df00003).umask32(0x0000ff00); // VME_IACK: vme interrupt acknowledge + map(0x1f800000, 0x1f800003).lw8("mem_cfg", [this](u8 data) { m_mem_cfg = data; }).umask32(0xff000000); map(0x1f800000, 0x1f800003).lr8("ctl_sysid", [this]() { return m_ctl_sysid; }).umask32(0x00ff0000); - map(0x1f840008, 0x1f84000b).ram().umask32(0x000000ff); // vme mask? + map(0x1f840000, 0x1f840003).lrw8("vme_isr", [this]() { return m_vme_isr; }, [this](u8 data) { m_vme_isr = data; }).umask32(0x000000ff); + map(0x1f840008, 0x1f84000b).lrw8("vme_imr", [this]() { return m_vme_imr; }, [this](u8 data) { m_vme_imr = data; }).umask32(0x000000ff); map(0x1f880000, 0x1f880003).lrw16("ctl_cpuctrl", [this]() @@ -129,10 +198,14 @@ void ip6_state::map(address_map &map) //BIT(data, 9); // reset system //BIT(data, 10); // enable parity checking //BIT(data, 11); // enable slave accesses + //BIT(data, 12); // enable vme arbiter + //BIT(data, 13); // write bad parity //BIT(data, 14); // watchdog enable + //BIT(data, 15); // unused/fast peripheral cycle? m_ctl_cpuctrl = data; }).umask32(0x0000ffff); + //map(0x1f8c0000, 0x1f8c0003); // lca readback trigger (b) map(0x1f8e0000, 0x1f8e0003).lrw8("aux_cpuctrl", [this]() { @@ -140,50 +213,103 @@ void ip6_state::map(address_map &map) }, [this](u8 data) { - //BIT(data, 1); // heartbeat - //BIT(data, 4); // enable console led(?)/eeprom program enable? + // cpu leds + m_leds[LED_HBT] = BIT(data, 0); + m_leds[LED_CPU] = BIT(data, 1); + m_leds[LED_GFX] = BIT(data, 2); + m_leds[LED_FPU] = BIT(data, 3); + + //BIT(data, 4); // console led(?) & eeprom program enable m_eeprom->cs_write(BIT(data, 5)); m_eeprom->clk_write(BIT(data, 6)); + //BIT(data, 7); // gfx_reset: reset graphics subsystem m_aux_cpuctrl = data; }).umask32(0xff000000); - map(0x1f950000, 0x1f9501ff).rw(m_net, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)).umask32(0xffff0000); + map(0x1f900000, 0x1f900003).lrw16("scsi_dmalo_addr", [this]() { return m_scsi_dmalo; }, [this](u16 data) { m_scsi_dmalo = data; m_scsi_dmapage = 0; }).umask32(0x0000ffff); + + /* + * DMA address mapping table is a pair of CY7C128-35PC 2048x8 SRAMs which + * read/write to data bus D27-12. A10 is tied high, giving 1024 entries. + */ + map(0x1f920000, 0x1f920fff).lrw16("dma_map", + [this](offs_t offset) + { + return m_dma_map[offset]; + }, + [this](offs_t offset, u16 data, u16 mem_mask) + { + mem_mask &= 0x0fff; + COMBINE_DATA(&m_dma_map[offset]); + }).umask32(0x0000ffff); + + //map(0x1f940000, 0x1f940003).umask32(?); // scsi_flush_addr + + map(0x1f950000, 0x1f9501ff).rw(m_enet, FUNC(am7990_device::regs_r), FUNC(am7990_device::regs_w)).umask32(0xffff0000); + map(0x1f960000, 0x1f960007).lr8("enet_reset", [this](offs_t offset) { m_enet->reset_w(!offset); return 0; }).umask32(0xff000000); - // local interrupt goes to cpu interrupt 1 - //map(0x1f980000, 0x1f980003).r().umask32(0x0000ffff); // int1_local_status - map(0x1f980008, 0x1f98000b).lrw8("int_mask", [this]() { return m_int_mask; }, [this](u8 data) { m_int_mask = data; }).umask32(0x000000ff); // int1_local_mask + map(0x1f980000, 0x1f980003).lr16("lio_isr", [this]() { return m_lio_isr; }).umask32(0x0000ffff); + map(0x1f980008, 0x1f98000b).lrw8("lio_imr", [this]() { return m_lio_imr; }, [this](u8 data) { m_lio_imr = data; }).umask32(0x000000ff); map(0x1fa00000, 0x1fa00003).lr8("timer1_ack", [this]() { m_cpu->set_input_line(INPUT_LINE_IRQ4, 0); return 0; }).umask32(0xff000000); map(0x1fa20000, 0x1fa20003).lr8("timer0_ack", [this]() { m_cpu->set_input_line(INPUT_LINE_IRQ2, 0); return 0; }).umask32(0xff000000); - //map(0x1fa40000, 0x1fa4000f).rw(m_pit, FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask32(0xff000000); // NetBSD says 0x1fb40000? + + //map(0x1fa40000, 0x1fa40000); // system bus error address + map(0x1fa40004, 0x1fa40007).lrw32("refresh_timer", + [this]() + { + return u32((machine().time() - m_refresh_timer).as_attoseconds() / ATTOSECONDS_PER_NANOSECOND); + }, + [this](u32 data) + { + m_refresh_timer = machine().time() - attotime::from_nsec(data); + }); + + //map(0x1fa40008, 0x1fa4000b); // GDMA_DABR_PHYS descriptor array base register + //map(0x1fa4000c, 0x1fa4000f); // GDMA_BUFADR_PHYS buffer address register + //map(0x1fa40010, 0x1fa400013).umask32(0xffff0000); // GDMA_BURST_PHYS burst/delay register + //map(0x1fa40010, 0x1fa400013).umask32(0x0000ffff); // GDMA_BUFLEN_PHYS buffer length register + + //map(0x1fa60000, 0x1fa600003); // VMA_RMW_ADDR map(0x1fa80000, 0x1fa80007).lr32("scsi_reset", [this](offs_t offset) { m_scsi->reset_w(!!offset); return 0; }); - // TODO: firmware resets wd33c93 and expects it to produce an interrupt - device emulation is currently wrong + //map(0x1fa80008, 0x1fa8000b); // IOC2 configuration register, bus error on IOC1 + + //map(0x1faa0000, 0x1faa0003).umask32(0xff000000); // clear lan access bit + //map(0x1faa0000, 0x1faa0003).umask32(0x00ff0000); // clear dma access bit + //map(0x1faa0000, 0x1faa0003).umask32(0x0000ff00); // clear cpu access bit + //map(0x1faa0000, 0x1faa0003).umask32(0x000000ff); // clear vme access bit + //map(0x1faa0004, 0x1faa0007).umask32(0x00ff0000); // parity error register + map(0x1fb00000, 0x1fb00003).rw(m_scsi, FUNC(wd33c93_device::indir_addr_r), FUNC(wd33c93_device::indir_addr_w)).umask32(0x00ff0000); map(0x1fb00100, 0x1fb00103).rw(m_scsi, FUNC(wd33c93_device::indir_reg_r), FUNC(wd33c93_device::indir_reg_w)).umask32(0x00ff0000); map(0x1fb40000, 0x1fb4000f).rw(m_pit, FUNC(pit8254_device::read), FUNC(pit8254_device::write)).umask32(0xff000000); - map(0x1fb80000, 0x1fb800ff).lrw8("duart0", + map(0x1fb80000, 0x1fb800ff).lrw8("duarts", [this](offs_t offset) { - return m_duart[0]->read(offset >> 2); + return m_duart[BIT(offset, 0)]->read(offset >> 2); }, [this](offs_t offset, u8 data) { - m_duart[0]->write(offset >> 2, data); + m_duart[BIT(offset, 0)]->write(offset >> 2, data); }).umask32(0xff000000); - map(0x1fbc0000, 0x1fbc00ff).rw(m_rtc, FUNC(dp8573_device::read), FUNC(dp8573_device::write)).umask32(0xff000000); + map(0x1fbc0000, 0x1fbc007f).rw(m_rtc, FUNC(dp8573_device::read), FUNC(dp8573_device::write)).umask32(0xff000000); map(0x1fc00000, 0x1fc3ffff).rom().region("prom", 0); } static void scsi_devices(device_slot_interface &device) { - device.option_add("cdrom", NSCSI_CDROM); + device.option_add("cdrom", NSCSI_CDROM_SGI).machine_config( + [](device_t *device) + { + downcast<nscsi_cdrom_device &>(*device).set_block_size(512); + }); device.option_add("harddisk", NSCSI_HARDDISK); } @@ -206,12 +332,12 @@ void ip6_state::configure(machine_config &config) [this](int state) { if (state) - m_ctl_sysid |= 0x01; + m_ctl_sysid |= SID_SERDATA; else - m_ctl_sysid &= ~0x01; + m_ctl_sysid &= ~SID_SERDATA; }); - DP8573(config, m_rtc); + DP8573(config, m_rtc); // DP8572AN PIT8254(config, m_pit); m_pit->set_clk<2>(3.6864_MHz_XTAL); @@ -222,11 +348,13 @@ void ip6_state::configure(machine_config &config) NSCSI_BUS(config, "scsi"); NSCSI_CONNECTOR(config, "scsi:0").option_set("wd33c93", WD33C93).machine_config( - [](device_t *device) + [this](device_t *device) { - device->set_clock(10000000); + wd33c93_device &wd33c93(downcast<wd33c93_device &>(*device)); - // TODO: connect irq and drq + wd33c93.set_clock(10000000); + wd33c93.irq_cb().set(*this, FUNC(ip6_state::lio_interrupt<LIO_SCSI>)).invert(); + wd33c93.drq_cb().set(*this, FUNC(ip6_state::scsi_drq)); }); NSCSI_CONNECTOR(config, "scsi:1", scsi_devices, "harddisk", false); NSCSI_CONNECTOR(config, "scsi:2", scsi_devices, nullptr, false); @@ -236,42 +364,72 @@ void ip6_state::configure(machine_config &config) NSCSI_CONNECTOR(config, "scsi:6", scsi_devices, "cdrom", false); NSCSI_CONNECTOR(config, "scsi:7", scsi_devices, nullptr, false); - AM7990(config, m_net); - // TODO: irq, drq, memory accessors (via ioc) + AM7990(config, m_enet); + m_enet->intr_out().set(FUNC(ip6_state::lio_interrupt<LIO_ENET>)); + m_enet->dma_in().set( + [this](offs_t offset) + { + unsigned const page = 0x200 + (offset >> 12); + u32 const address = (u32(m_dma_map[page]) << 12) | (offset & 0xfff); - // duarts - // NOTE: one of these is has 40 pins, while the other has only 24 pins; most - // likely the keyboard/mouse one is without and doesn't have any flow control - SCN2681(config, m_duart[0], 3.6864_MHz_XTAL); - SCN2681(config, m_duart[1], 3.6864_MHz_XTAL); // TODO: probably for the keyboard and mouse + return m_cpu->space(0).read_word(address); + }); + m_enet->dma_out().set( + [this](offs_t offset, u16 data, u16 mem_mask) + { + unsigned const page = 0x200 + (offset >> 12); + u32 const address = (u32(m_dma_map[page]) << 12) | (offset & 0xfff); + + m_cpu->space(0).write_word(address, data, mem_mask); + }); + + // duart 0 (keyboard/mouse) + SCN2681(config, m_duart[0], 3.6864_MHz_XTAL); // SCN2681AC1N24 + sgi_keyboard_port_device &keyboard_port(SGIKBD_PORT(config, "keyboard_port", default_sgi_keyboard_devices, "hlekbd")); + rs232_port_device &mouse_port(RS232_PORT(config, "mouse_port", + [](device_slot_interface &device) + { + device.option_add("mouse", SGI_HLE_SERIAL_MOUSE); + }, + "mouse")); + + // duart 0 outputs + m_duart[0]->irq_cb().set(FUNC(ip6_state::lio_interrupt<LIO_D0>)); + m_duart[0]->a_tx_cb().set(keyboard_port, FUNC(sgi_keyboard_port_device::write_txd)); + m_duart[0]->b_tx_cb().set(mouse_port, FUNC(rs232_port_device::write_txd)); + + // duart 0 inputs + keyboard_port.rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_a_w)); + mouse_port.rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_b_w)); + + // duart 1 (serial ports) + SCN2681(config, m_duart[1], 3.6864_MHz_XTAL); // SCN2681AC1N40 RS232_PORT(config, m_serial[0], default_rs232_devices, "terminal"); RS232_PORT(config, m_serial[1], default_rs232_devices, nullptr); - // duart 0 outputs - // TODO: irq - m_duart[0]->a_tx_cb().set(m_serial[0], FUNC(rs232_port_device::write_txd)); - m_duart[0]->b_tx_cb().set(m_serial[1], FUNC(rs232_port_device::write_txd)); - m_duart[0]->outport_cb().set( + // duart 1 outputs + m_duart[1]->irq_cb().set(FUNC(ip6_state::lio_interrupt<LIO_D1>)); + m_duart[1]->a_tx_cb().set(m_serial[0], FUNC(rs232_port_device::write_txd)); + m_duart[1]->b_tx_cb().set(m_serial[1], FUNC(rs232_port_device::write_txd)); + m_duart[1]->outport_cb().set( [this](u8 data) { m_serial[0]->write_rts(BIT(data, 0)); m_serial[1]->write_rts(BIT(data, 1)); - m_serial[0]->write_dtr(BIT(data, 2)); - m_serial[1]->write_dtr(BIT(data, 3)); - - // TODO: bit 4-7 + m_duart[1]->ip5_w(BIT(data, 3)); + m_duart[1]->ip6_w(BIT(data, 3)); + m_serial[0]->write_dtr(BIT(data, 4)); + m_serial[1]->write_dtr(BIT(data, 5)); }); - // duart inputs (guessed for now) - m_serial[0]->rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_a_w)); - m_serial[0]->cts_handler().set(m_duart[0], FUNC(scn2681_device::ip0_w)); - m_serial[0]->dsr_handler().set(m_duart[0], FUNC(scn2681_device::ip3_w)); - m_serial[0]->dcd_handler().set(m_duart[0], FUNC(scn2681_device::ip4_w)); + // duart 1 inputs + m_serial[0]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_a_w)); + m_serial[0]->cts_handler().set(m_duart[1], FUNC(scn2681_device::ip0_w)); + m_serial[0]->dcd_handler().set(m_duart[1], FUNC(scn2681_device::ip3_w)); - m_serial[1]->rxd_handler().set(m_duart[0], FUNC(scn2681_device::rx_b_w)); - m_serial[1]->cts_handler().set(m_duart[0], FUNC(scn2681_device::ip1_w)); - m_serial[1]->dsr_handler().set(m_duart[0], FUNC(scn2681_device::ip5_w)); - m_serial[1]->dcd_handler().set(m_duart[0], FUNC(scn2681_device::ip6_w)); + m_serial[1]->rxd_handler().set(m_duart[1], FUNC(scn2681_device::rx_b_w)); + m_serial[1]->cts_handler().set(m_duart[1], FUNC(scn2681_device::ip1_w)); + m_serial[1]->dcd_handler().set(m_duart[1], FUNC(scn2681_device::ip2_w)); } void ip6_state::initialize() @@ -279,7 +437,53 @@ void ip6_state::initialize() // map the configured ram m_cpu->space(0).install_ram(0x00000000, m_ram->mask(), m_ram->pointer()); - m_ctl_sysid = 0x02; // fpu present + m_ctl_sysid = SID_FPPRES; + + m_lio_isr = 0x3ff; + m_lio_imr = 0; + m_lio_int = false; + + m_refresh_timer = machine().time(); + + m_dma_map = make_unique_clear<u16 []>(2048); + + m_leds.resolve(); +} + +void ip6_state::lio_interrupt(unsigned number, int state) +{ + u16 const mask = 1 << number; + + // record interrupt state + if (!state) + m_lio_isr &= ~mask; + else + m_lio_isr |= mask; + + // update interrupt line + bool const lio_int = ~m_lio_isr & m_lio_imr; + if (m_lio_imr ^ lio_int) + { + m_lio_int = lio_int; + m_cpu->set_input_line(INPUT_LINE_IRQ1, m_lio_int); + } +} + +void ip6_state::scsi_drq(int state) +{ + if (state) + { + u32 const address = (u32(m_dma_map[m_scsi_dmapage]) << 12) | (m_scsi_dmalo & 0x0fff); + + if (m_scsi_dmalo & 0x8000) + m_cpu->space(0).write_byte(address, m_scsi->dma_r()); + else + m_scsi->dma_w(m_cpu->space(0).read_byte(address)); + + m_scsi_dmalo = (m_scsi_dmalo & 0xf000) | ((m_scsi_dmalo + 1) & 0x0fff); + if (!(m_scsi_dmalo & 0x0fff)) + m_scsi_dmapage++; + } } ROM_START(4d20) |