// license:BSD-3-Clause // copyright-holders:Ryan Holtz /********************************************************************** SGI IOC2 I/O Controller emulation **********************************************************************/ #include "emu.h" #include "bus/rs232/rs232.h" #include "machine/input_merger.h" #include "machine/ioc2.h" #define LOG_PI1 (1 << 0) #define LOG_SERIAL (1 << 1) #define LOG_MOUSEKBD (1 << 2) #define LOG_PANEL (1 << 3) #define LOG_SYSID (1 << 4) #define LOG_READ (1 << 5) #define LOG_DMA_SEL (1 << 6) #define LOG_RESET (1 << 7) #define LOG_WRITE (1 << 8) #define LOG_INT3 (1 << 9) #define LOG_PIT (1 << 10) #define LOG_IRQS (1 << 11) #define LOG_ALL (LOG_PI1 | LOG_SERIAL | LOG_MOUSEKBD | LOG_PANEL | LOG_SYSID | LOG_READ | LOG_DMA_SEL | LOG_RESET | LOG_WRITE | LOG_INT3 | LOG_PIT | LOG_IRQS) #define LOG_DEFAULT (LOG_ALL & ~(LOG_SYSID)) #define VERBOSE (0) #include "logmacro.h" /*static*/ char const *const ioc2_device::SCC_TAG = "scc"; /*static*/ char const *const ioc2_device::PI1_TAG = "pi1"; /*static*/ char const *const ioc2_device::KBDC_TAG = "kbdc"; /*static*/ char const *const ioc2_device::PIT_TAG = "pit"; /*static*/ char const *const ioc2_device::RS232A_TAG = "rs232a"; /*static*/ char const *const ioc2_device::RS232B_TAG = "rs232b"; /*static*/ const XTAL ioc2_device::SCC_PCLK = 10_MHz_XTAL; /*static*/ const XTAL ioc2_device::SCC_RXA_CLK = 3.6864_MHz_XTAL; // Needs verification /*static*/ const XTAL ioc2_device::SCC_TXA_CLK = XTAL(0); /*static*/ const XTAL ioc2_device::SCC_RXB_CLK = 3.6864_MHz_XTAL; // Needs verification /*static*/ const XTAL ioc2_device::SCC_TXB_CLK = XTAL(0); DEFINE_DEVICE_TYPE(SGI_IOC2_GUINNESS, ioc2_guinness_device, "ioc2g", "SGI IOC2 (Guinness)") DEFINE_DEVICE_TYPE(SGI_IOC2_FULL_HOUSE, ioc2_full_house_device, "ioc2f", "SGI IOC2 (Full House)") ioc2_guinness_device::ioc2_guinness_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ioc2_device(mconfig, SGI_IOC2_GUINNESS, tag, owner, clock) { } ioc2_full_house_device::ioc2_full_house_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : ioc2_device(mconfig, SGI_IOC2_FULL_HOUSE, tag, owner, clock) { } static INPUT_PORTS_START( front_panel ) PORT_START("panel_buttons") PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Power") PORT_CHANGED_MEMBER(DEVICE_SELF, ioc2_device, power_button, 0) PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Volume Down") PORT_CHANGED_MEMBER(DEVICE_SELF, ioc2_device, volume_down, 0) PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYPAD) PORT_NAME("Volume Up") PORT_CHANGED_MEMBER(DEVICE_SELF, ioc2_device, volume_up, 0) INPUT_PORTS_END ioport_constructor ioc2_device::device_input_ports() const { return INPUT_PORTS_NAME(front_panel); } void ioc2_device::device_add_mconfig(machine_config &config) { SCC85230(config, m_scc, SCC_PCLK); m_scc->configure_channels(SCC_RXA_CLK.value(), SCC_TXA_CLK.value(), SCC_RXB_CLK.value(), SCC_TXB_CLK.value()); m_scc->out_txda_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_txd)); m_scc->out_dtra_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_dtr)); m_scc->out_rtsa_callback().set(RS232A_TAG, FUNC(rs232_port_device::write_rts)); m_scc->out_txdb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_txd)); m_scc->out_dtrb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_dtr)); m_scc->out_rtsb_callback().set(RS232B_TAG, FUNC(rs232_port_device::write_rts)); m_scc->out_int_callback().set(FUNC(ioc2_device::duart_int_w)); rs232_port_device &rs232a(RS232_PORT(config, RS232A_TAG, default_rs232_devices, nullptr)); rs232a.cts_handler().set(m_scc, FUNC(scc85230_device::ctsa_w)); rs232a.dcd_handler().set(m_scc, FUNC(scc85230_device::dcda_w)); rs232a.rxd_handler().set(m_scc, FUNC(scc85230_device::rxa_w)); rs232_port_device &rs232b(RS232_PORT(config, RS232B_TAG, default_rs232_devices, nullptr)); rs232b.cts_handler().set(m_scc, FUNC(scc85230_device::ctsb_w)); rs232b.dcd_handler().set(m_scc, FUNC(scc85230_device::dcdb_w)); rs232b.rxd_handler().set(m_scc, FUNC(scc85230_device::rxb_w)); PC_LPT(config, m_pi1); // keyboard connector pc_kbdc_device &kbd_con(PC_KBDC(config, "kbd_con", 0)); kbd_con.out_clock_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::kbd_clk_w)); kbd_con.out_data_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::kbd_data_w)); // keyboard port pc_kbdc_slot_device &kbd(PC_KBDC_SLOT(config, "kbd", pc_at_keyboards, STR_KBD_MICROSOFT_NATURAL)); kbd.set_pc_kbdc_slot(&kbd_con); // auxiliary connector pc_kbdc_device &aux_con(PC_KBDC(config, "aux_con", 0)); aux_con.out_clock_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::aux_clk_w)); aux_con.out_data_cb().set(m_kbdc, FUNC(ps2_keyboard_controller_device::aux_data_w)); // auxiliary port pc_kbdc_slot_device &aux(PC_KBDC_SLOT(config, "aux", ps2_mice, STR_HLE_PS2_MOUSE)); aux.set_pc_kbdc_slot(&aux_con); // keyboard controller PS2_KEYBOARD_CONTROLLER(config, m_kbdc, 12_MHz_XTAL); m_kbdc->kbd_clk().set(kbd_con, FUNC(pc_kbdc_device::clock_write_from_mb)); m_kbdc->kbd_data().set(kbd_con, FUNC(pc_kbdc_device::data_write_from_mb)); m_kbdc->aux_clk().set(aux_con, FUNC(pc_kbdc_device::clock_write_from_mb)); m_kbdc->aux_data().set(aux_con, FUNC(pc_kbdc_device::data_write_from_mb)); input_merger_device &kbdc_irq(INPUT_MERGER_ANY_HIGH(config, "kbdc_irq")); kbdc_irq.output_handler().set(FUNC(ioc2_device::kbdc_int_w)); m_kbdc->kbd_irq().set(kbdc_irq, FUNC(input_merger_device::in_w<0>)); m_kbdc->aux_irq().set(kbdc_irq, FUNC(input_merger_device::in_w<1>)); PIT8254(config, m_pit, 0); m_pit->set_clk<0>(0); m_pit->set_clk<1>(0); m_pit->set_clk<2>(1000000); m_pit->out_handler<0>().set(FUNC(ioc2_device::timer0_int)); m_pit->out_handler<1>().set(FUNC(ioc2_device::timer1_int)); m_pit->out_handler<2>().set(FUNC(ioc2_device::pit_clock2_out)); } ioc2_device::ioc2_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock) , m_maincpu(*this, finder_base::DUMMY_TAG) , m_scc(*this, SCC_TAG) , m_pi1(*this, PI1_TAG) , m_kbdc(*this, KBDC_TAG) , m_pit(*this, PIT_TAG) , m_gen_ctrl_select_reg(0) , m_gen_ctrl_reg(0) , m_front_panel_reg(0) , m_read_reg(0) , m_dma_sel(0) , m_reset_reg(0) , m_write_reg(0) , m_int3_map_status_reg(0) , m_int3_map_pol_reg(0) , m_int3_err_status_reg(0) , m_par_read_cnt(0) , m_par_cntl(0) { } void ioc2_device::device_start() { save_item(NAME(m_par_read_cnt)); save_item(NAME(m_par_cntl)); save_item(NAME(m_gen_ctrl_select_reg)); save_item(NAME(m_gen_ctrl_reg)); save_item(NAME(m_front_panel_reg)); save_item(NAME(m_read_reg)); save_item(NAME(m_dma_sel)); save_item(NAME(m_reset_reg)); save_item(NAME(m_write_reg)); save_item(NAME(m_int3_local_status_reg)); save_item(NAME(m_int3_local_mask_reg)); save_item(NAME(m_int3_map_status_reg)); save_item(NAME(m_int3_map_mask_reg)); save_item(NAME(m_int3_map_pol_reg)); save_item(NAME(m_int3_err_status_reg)); } void ioc2_device::device_reset() { m_par_read_cnt = 0; m_par_cntl = 0; m_gen_ctrl_select_reg = 0; m_gen_ctrl_reg = 0; m_front_panel_reg = FRONT_PANEL_VOL_UP_HOLD | FRONT_PANEL_VOL_DOWN_HOLD | FRONT_PANEL_POWER_STATE; m_read_reg = 0; m_dma_sel = 0; m_reset_reg = 0; m_write_reg = 0; memset(m_int3_local_status_reg, 0, sizeof(uint8_t) * 2); memset(m_int3_local_mask_reg, 0, sizeof(uint8_t) * 2); m_int3_map_status_reg = 0; memset(m_int3_map_mask_reg, 0, sizeof(uint8_t) * 2); m_int3_map_pol_reg = 0; m_int3_err_status_reg = 0; } void ioc2_device::raise_local_irq(int channel, uint8_t mask) { const uint8_t old = m_int3_local_status_reg[channel]; m_int3_local_status_reg[channel] |= mask; LOGMASKED(LOG_IRQS, "Raising Local%d interrupt mask %02x, interrupt status was %02x, now %02x, %sing interrupt\n", channel, mask, old, m_int3_local_status_reg[channel], (m_int3_local_mask_reg[channel] & m_int3_local_status_reg[channel]) ? "rais" : "lower"); m_maincpu->set_input_line(channel, (m_int3_local_mask_reg[channel] & m_int3_local_status_reg[channel]) ? ASSERT_LINE : CLEAR_LINE); } void ioc2_device::lower_local_irq(int channel, uint8_t mask) { const uint8_t old = m_int3_local_status_reg[channel]; m_int3_local_status_reg[channel] &= ~mask; LOGMASKED(LOG_IRQS, "Lowering Local%d interrupt mask %02x, interrupt status was %02x, now %02x, %sing interrupt\n", channel, mask, old, m_int3_local_status_reg[channel], (m_int3_local_mask_reg[channel] & m_int3_local_status_reg[channel]) ? "rais" : "lower"); m_maincpu->set_input_line(channel, (m_int3_local_mask_reg[channel] & m_int3_local_status_reg[channel]) ? ASSERT_LINE : CLEAR_LINE); } WRITE_LINE_MEMBER(ioc2_device::timer0_int) { if (state) { LOGMASKED(LOG_IRQS, "Raising IRQ line 2\n"); m_maincpu->set_input_line(2, ASSERT_LINE); } } WRITE_LINE_MEMBER(ioc2_device::timer1_int) { if (state) { LOGMASKED(LOG_IRQS, "Raising IRQ line 3\n"); m_maincpu->set_input_line(3, ASSERT_LINE); } } WRITE_LINE_MEMBER(ioc2_device::pit_clock2_out) { m_pit->write_clk0(state); m_pit->write_clk1(state); } WRITE_LINE_MEMBER(ioc2_device::kbdc_int_w) { set_mappable_int(0x10, state); } WRITE_LINE_MEMBER(ioc2_device::duart_int_w) { set_mappable_int(0x20, state); } void ioc2_device::set_mappable_int(uint8_t mask, bool state) { const uint8_t old = m_int3_map_status_reg; const uint8_t old0 = m_int3_map_mask_reg[0] & old; const uint8_t old1 = m_int3_map_mask_reg[1] & old; if (state) m_int3_map_status_reg |= mask; else m_int3_map_status_reg &= ~mask; const uint8_t new0 = m_int3_map_mask_reg[0] & m_int3_map_status_reg; if (old0 ^ new0) { check_mappable_interrupt(0); } const uint8_t new1 = m_int3_map_mask_reg[1] & m_int3_map_status_reg; if (old1 ^ new1) { check_mappable_interrupt(1); } } void ioc2_device::check_mappable_interrupt(int channel) { if (channel == 0) { if (m_int3_map_mask_reg[channel] & m_int3_map_status_reg) raise_local_irq(channel, INT3_LOCAL0_MAPPABLE0); else lower_local_irq(channel, INT3_LOCAL0_MAPPABLE0); } else { if (m_int3_map_mask_reg[channel] & m_int3_map_status_reg) raise_local_irq(channel, INT3_LOCAL1_MAPPABLE1); else lower_local_irq(channel, INT3_LOCAL1_MAPPABLE1); } } u8 ioc2_device::pi1_dma_ctrl_r() { LOGMASKED(LOG_PI1, "%s: Read PI1 DMA Control Register: 00\n", machine().describe_context()); return 0; } void ioc2_device::pi1_dma_ctrl_w(u8 data) { LOGMASKED(LOG_PI1, "%s: Write PI1 DMA Control Register: %02x\n", machine().describe_context(), data); } u8 ioc2_device::pi1_int_status_r() { LOGMASKED(LOG_PI1, "%s: Read PI1 Interrupt Status Register: 00\n", machine().describe_context()); return 0; } void ioc2_device::pi1_int_status_w(u8 data) { LOGMASKED(LOG_PI1, "%s: Write PI1 Interrupt Status Register: %02x\n", machine().describe_context(), data); } u8 ioc2_device::pi1_int_mask_r() { LOGMASKED(LOG_PI1, "%s: Read PI1 Interrupt Mask Register: 00\n", machine().describe_context()); return 0; } void ioc2_device::pi1_int_mask_w(u8 data) { LOGMASKED(LOG_PI1, "%s: Write PI1 Interrupt Mask Register: %02x\n", machine().describe_context(), data); } u8 ioc2_device::pi1_timer1_r() { LOGMASKED(LOG_PI1, "%s: Read PI1 Timer1 Register: 00\n", machine().describe_context()); return 0; } u8 ioc2_device::pi1_timer2_r() { LOGMASKED(LOG_PI1, "%s: Read PI1 Timer2 Register: 00\n", machine().describe_context()); return 0; } u8 ioc2_device::pi1_timer3_r() { LOGMASKED(LOG_PI1, "%s: Read PI1 Timer3 Register: 00\n", machine().describe_context()); return 0; } u8 ioc2_device::pi1_timer4_r() { LOGMASKED(LOG_PI1, "%s: Read PI1 Timer4 Register: 00\n", machine().describe_context()); return 0; } void ioc2_device::pi1_timer1_w(u8 data) { LOGMASKED(LOG_PI1, "%s: Write PI1 Timer1 Register: %02x\n", machine().describe_context(), data); } void ioc2_device::pi1_timer2_w(u8 data) { LOGMASKED(LOG_PI1, "%s: Write PI1 Timer2 Register: %02x\n", machine().describe_context(), data); } void ioc2_device::pi1_timer3_w(u8 data) { LOGMASKED(LOG_PI1, "%s: Write PI1 Timer3 Register: %02x\n", machine().describe_context(), data); } void ioc2_device::pi1_timer4_w(u8 data) { LOGMASKED(LOG_PI1, "%s: Write PI1 Timer4 Register: %02x\n", machine().describe_context(), data); } u8 ioc2_device::gc_select_r() { LOGMASKED(LOG_READ, "%s: Read GC Select Register: 00\n", machine().describe_context()); return 0; } void ioc2_device::gc_select_w(u8 data) { LOGMASKED(LOG_WRITE, "%s: Write GC Select Register: %02x\n", machine().describe_context(), data); } u8 ioc2_device::gen_cntl_r() { LOGMASKED(LOG_READ, "%s: Read General Control Register: 00\n", machine().describe_context()); return 0; } void ioc2_device::gen_cntl_w(u8 data) { LOGMASKED(LOG_WRITE, "%s: Write General Control Register: %02x\n", machine().describe_context(), data); } u8 ioc2_device::front_panel_r() { LOGMASKED(LOG_PANEL, "%s: Read Front Panel Register: %02x\n", machine().describe_context(), m_front_panel_reg); return m_front_panel_reg; } void ioc2_device::front_panel_w(u8 data) { LOGMASKED(LOG_PANEL, "%s: Write Front Panel Register: %02x\n", machine().describe_context(), data); m_front_panel_reg &= ~(data & (FRONT_PANEL_VOL_UP_INT | FRONT_PANEL_VOL_DOWN_INT | FRONT_PANEL_POWER_BUTTON_INT)); if (!(m_front_panel_reg & FRONT_PANEL_INT_MASK)) lower_local_irq(1, INT3_LOCAL1_PANEL); } u8 ioc2_device::system_id_r() { const uint8_t data = get_system_id(); LOGMASKED(LOG_SYSID, "%s: Read System ID Register: %02x\n", machine().describe_context(), data); return data; } u8 ioc2_device::read_r() { LOGMASKED(LOG_READ, "%s: Read Read Register: %02x\n", machine().describe_context(), m_read_reg); return m_read_reg; } u8 ioc2_device::dma_sel_r() { // Bits 2-0 not quite understood, seem to be copy/paste error in SGI's own documents: // // 2 RW Parallel Port DMA Select. A high bit selects the Parallel Port DMA channel. 0\h is the default after reset. [this makes sense. -ed.] // 1 RW ISDN Channel B DMA Select. A high bit selects the Parallel Port DMA channel. 0\h is the default after reset. [is this a copy/paste error? perhaps "Parallel Port" should be "ISDN Channel B"?] // 0 RW [same text as above. Another copy/paste error, maybe? Should be channel A, with the bit selecting DMA channel 0/1 for ISDN channel A, the and the same for ISDN channel B in bit 1?] LOGMASKED(LOG_DMA_SEL, "%s: Read DMA Select Register: %02x\n", machine().describe_context(), m_dma_sel); return m_dma_sel; } void ioc2_device::dma_sel_w(u8 data) { // Bits 2-0 not quite understood, seem to be copy/paste error in SGI's own documents: // // 5:4 RW Serial Port Clock Select: 00 selects a 10MHz internal clock (default), 01 selects a 6.67MHz internal clock, and 02 or 03 selects the external clock input. // 2 RW Parallel Port DMA Select. A high bit selects the Parallel Port DMA channel. 0\h is the default after reset. [this makes sense. -ed.] // 1 RW ISDN Channel B DMA Select. A high bit selects the Parallel Port DMA channel. 0\h is the default after reset. [is this a copy/paste error? perhaps "Parallel Port" should be "ISDN Channel B"?] // 0 RW [same text as above. Another copy/paste error, maybe? Should be channel A, with the bit selecting DMA channel 0/1 for ISDN channel A, the and the same for ISDN channel B in bit 1?] LOGMASKED(LOG_DMA_SEL, "%s: Write DMA Select Register: %02x\n", machine().describe_context(), data); uint8_t old = m_dma_sel; m_dma_sel = data; uint8_t changed = old ^ m_dma_sel; if (changed & DMA_SEL_CLOCK_SEL_MASK) { if (changed & DMA_SEL_CLOCK_SEL_EXT) { LOGMASKED(LOG_DMA_SEL, "%s: External clock select %sselected\n", machine().describe_context(), (old & DMA_SEL_CLOCK_SEL_EXT) != 0 ? "de" : ""); // TODO: verify the external Rx/Tx clock, is it fixed or programmable? } } // TODO: Currently we always assume a 10MHz clock as PCLK } u8 ioc2_device::reset_r() { LOGMASKED(LOG_RESET, "%s: Read Reset Register: %02x\n", machine().describe_context(), m_reset_reg); return m_reset_reg; } void ioc2_device::reset_w(u8 data) { LOGMASKED(LOG_RESET, "%s: Write Reset Register: %02x\n", machine().describe_context(), data); handle_reset_reg_write(data); } u8 ioc2_device::write_r() { // Not yet implemented, some bits unnecessary: // // Bit Oper Description // 7 RW Margin High. Set low for normal +5V operation, high to step supply up to +5.5V. Cleared at reset. // 6 RW Margin Low. Set lowf or normal +5V operation, high to step supply down to +4.5V. Cleared at reset. // 5 RW UART1 PC Mode. Set low to configure Port1 for RS422 Mac mode, high to select RS232 PC mode. Cleared at reset. // 4 RW UART2 PC Mode. Set low to configure Port2 for RS422 Mac mode, high to select RS232 PC mode. Cleared at reset. // 3 RW Ethernet Auto Select (active high). Set low for manual mode, high to have LXT901 automatically select TP or AUI based on link integrity. Cleared at reset. // 2 RW Ethernet Port Select. Set low for TP, high for AUI. This setting is only used when Auto Select is in manual mode. Cleared at reset. // 1 RW Ethernet UTP/STP select. Set low to select 150 ohm termination fro shielded TP (default), set high to select 100 ohm termination for unshielded TP. Cleared at reset. // 0 RW Ethernet Normal Threshold (NTH) select. Set low to select the normal TP squelch threshold (default), high to reduce threshold by 4.5 dB (set low when reset). LOGMASKED(LOG_WRITE, "%s: Read Write Register: %02x\n", machine().describe_context(), m_write_reg); return m_write_reg; } void ioc2_device::write_w(u8 data) { LOGMASKED(LOG_WRITE, "%s: Write Write Register: %02x\n", machine().describe_context(), data); m_write_reg = data; } template u8 ioc2_device::local_status_r() { LOGMASKED(LOG_INT3, "%s: Read Interrupt Local%d Status Register: %02x\n", machine().describe_context(), N, m_int3_local_status_reg[N]); return m_int3_local_status_reg[N]; } template u8 ioc2_device::local_mask_r() { LOGMASKED(LOG_INT3, "%s: Read Interrupt Local%d Mask Register: %02x\n", machine().describe_context(), N, m_int3_local_mask_reg[N]); return m_int3_local_mask_reg[N]; } template void ioc2_device::local_mask_w(u8 data) { LOGMASKED(LOG_INT3, "%s: Write Interrupt Local%d Mask Register: %02x\n", machine().describe_context(), N, data); set_local_int_mask(N, data); } u8 ioc2_device::map_status_r() { LOGMASKED(LOG_INT3, "%s: Read Interrupt Map Status Register: %02x\n", machine().describe_context(), m_int3_map_status_reg); return m_int3_map_status_reg; } template u8 ioc2_device::map_mask_r() { LOGMASKED(LOG_INT3, "%s: Read Interrupt Map Mask%d Register: %02x\n", machine().describe_context(), N, m_int3_map_mask_reg[N]); return m_int3_map_mask_reg[N]; } template void ioc2_device::map_mask_w(u8 data) { LOGMASKED(LOG_INT3, "%s: Write Interrupt Map Mask%d Register: %02x\n", machine().describe_context(), N, data); set_map_int_mask(N, data); } u8 ioc2_device::map_pol_r() { LOGMASKED(LOG_INT3, "%s: Read Interrupt Map Polarity Register: %02x\n", machine().describe_context(), m_int3_map_pol_reg); return m_int3_map_pol_reg; } void ioc2_device::map_pol_w(u8 data) { // TODO: Mappable interrupt polarity select LOGMASKED(LOG_INT3, "%s: Write Interrupt Map Polarity Register: %02x\n", machine().describe_context(), data); m_int3_map_pol_reg = data; } void ioc2_device::timer_int_clear_w(u8 data) { LOGMASKED(LOG_INT3, "%s: Write Interrupt Timer Clear Register: %02x\n", machine().describe_context(), data); set_timer_int_clear(data); } u8 ioc2_device::error_status_r() { LOGMASKED(LOG_INT3, "%s: Read Interrupt Error Status Register: %02x\n", machine().describe_context(), m_int3_err_status_reg); return m_int3_err_status_reg; } void ioc2_device::base_map(address_map &map) { map(0x00, 0x02).rw(m_pi1, FUNC(pc_lpt_device::read), FUNC(pc_lpt_device::write)); map(0x03, 0x03).rw(FUNC(ioc2_device::pi1_dma_ctrl_r), FUNC(ioc2_device::pi1_dma_ctrl_w)); map(0x04, 0x04).rw(FUNC(ioc2_device::pi1_int_status_r), FUNC(ioc2_device::pi1_int_status_w)); map(0x05, 0x05).rw(FUNC(ioc2_device::pi1_int_mask_r), FUNC(ioc2_device::pi1_int_mask_w)); map(0x06, 0x06).rw(FUNC(ioc2_device::pi1_timer1_r), FUNC(ioc2_device::pi1_timer1_w)); map(0x07, 0x07).rw(FUNC(ioc2_device::pi1_timer2_r), FUNC(ioc2_device::pi1_timer2_w)); map(0x08, 0x08).rw(FUNC(ioc2_device::pi1_timer3_r), FUNC(ioc2_device::pi1_timer3_w)); map(0x09, 0x09).rw(FUNC(ioc2_device::pi1_timer4_r), FUNC(ioc2_device::pi1_timer4_w)); map(0x0c, 0x0f).rw(m_scc, FUNC(z80scc_device::ab_dc_r), FUNC(z80scc_device::ab_dc_w)); map(0x10, 0x10).rw(m_kbdc, FUNC(ps2_keyboard_controller_device::data_r), FUNC(ps2_keyboard_controller_device::data_w)); map(0x11, 0x11).rw(m_kbdc, FUNC(ps2_keyboard_controller_device::status_r), FUNC(ps2_keyboard_controller_device::command_w)); map(0x12, 0x12).rw(FUNC(ioc2_device::gc_select_r), FUNC(ioc2_device::gc_select_w)); map(0x13, 0x13).rw(FUNC(ioc2_device::gen_cntl_r), FUNC(ioc2_device::gen_cntl_w)); map(0x14, 0x14).rw(FUNC(ioc2_device::front_panel_r), FUNC(ioc2_device::front_panel_w)); map(0x16, 0x16).r(FUNC(ioc2_device::system_id_r)); map(0x18, 0x18).r(FUNC(ioc2_device::read_r)); map(0x1a, 0x1a).rw(FUNC(ioc2_device::dma_sel_r), FUNC(ioc2_device::dma_sel_w)); map(0x1c, 0x1c).rw(FUNC(ioc2_device::reset_r), FUNC(ioc2_device::reset_w)); map(0x1e, 0x1e).rw(FUNC(ioc2_device::write_r), FUNC(ioc2_device::write_w)); } void ioc2_guinness_device::map(address_map &map) { base_map(map); map(0x20, 0x20).r(FUNC(ioc2_guinness_device::local_status_r<0>)); map(0x21, 0x21).rw(FUNC(ioc2_guinness_device::local_mask_r<0>), FUNC(ioc2_guinness_device::local_mask_w<0>)); map(0x22, 0x22).r(FUNC(ioc2_guinness_device::local_status_r<1>)); map(0x23, 0x23).rw(FUNC(ioc2_guinness_device::local_mask_r<1>), FUNC(ioc2_guinness_device::local_mask_w<1>)); map(0x24, 0x24).r(FUNC(ioc2_guinness_device::map_status_r)); map(0x25, 0x25).rw(FUNC(ioc2_guinness_device::map_mask_r<0>), FUNC(ioc2_guinness_device::map_mask_w<0>)); map(0x26, 0x26).rw(FUNC(ioc2_guinness_device::map_mask_r<1>), FUNC(ioc2_guinness_device::map_mask_w<1>)); map(0x27, 0x27).rw(FUNC(ioc2_guinness_device::map_pol_r), FUNC(ioc2_guinness_device::map_pol_w)); map(0x28, 0x28).w(FUNC(ioc2_guinness_device::timer_int_clear_w)); map(0x29, 0x29).r(FUNC(ioc2_guinness_device::error_status_r)); map(0x2c, 0x2f).rw(m_pit, FUNC(pit8254_device::read), FUNC(pit8254_device::write)); } void ioc2_full_house_device::map(address_map &map) { base_map(map); } void ioc2_full_house_device::int2_map(address_map &map) { map(0x00, 0x00).r(FUNC(ioc2_full_house_device::local_status_r<0>)); map(0x01, 0x01).rw(FUNC(ioc2_full_house_device::local_mask_r<0>), FUNC(ioc2_full_house_device::local_mask_w<0>)); map(0x02, 0x02).r(FUNC(ioc2_full_house_device::local_status_r<1>)); map(0x03, 0x03).rw(FUNC(ioc2_full_house_device::local_mask_r<1>), FUNC(ioc2_full_house_device::local_mask_w<1>)); map(0x04, 0x04).r(FUNC(ioc2_full_house_device::map_status_r)); map(0x05, 0x05).rw(FUNC(ioc2_full_house_device::map_mask_r<0>), FUNC(ioc2_full_house_device::map_mask_w<0>)); map(0x06, 0x06).rw(FUNC(ioc2_full_house_device::map_mask_r<1>), FUNC(ioc2_full_house_device::map_mask_w<1>)); map(0x08, 0x08).w(FUNC(ioc2_full_house_device::timer_int_clear_w)); map(0x0c, 0x0f).rw(m_pit, FUNC(pit8254_device::read), FUNC(pit8254_device::write)); } void ioc2_device::set_local_int_mask(int channel, uint8_t mask) { uint8_t old = m_int3_local_mask_reg[channel]; m_int3_local_mask_reg[channel] = mask; bool old_line = (old & m_int3_local_status_reg[channel]) != 0; bool new_line = (m_int3_local_mask_reg[channel] & m_int3_local_status_reg[channel]) != 0; if (old_line != new_line) { const uint8_t int_bits = (m_int3_local_mask_reg[channel] & m_int3_local_status_reg[channel]); m_maincpu->set_input_line(channel, int_bits != 0 ? ASSERT_LINE : CLEAR_LINE); } } void ioc2_device::set_map_int_mask(int channel, uint8_t mask) { m_int3_map_mask_reg[channel] = mask; check_mappable_interrupt(channel); } void ioc2_device::set_timer_int_clear(uint8_t data) { if (BIT(data, 0)) { LOGMASKED(LOG_IRQS, "Lowering IRQ line 2\n"); m_maincpu->set_input_line(2, CLEAR_LINE); } if (BIT(data, 1)) { LOGMASKED(LOG_IRQS, "Lowering IRQ line 3\n"); m_maincpu->set_input_line(3, CLEAR_LINE); } } void ioc2_device::handle_reset_reg_write(uint8_t data) { // guinness/fullhouse-specific implementations can handle bit 3 being used for ISDN reset on Indy only and bit 2 for EISA reset on Indigo 2 only, but for now we do nothing with it if (!BIT(data, 1)) { //m_kbdc->reset(); } m_reset_reg = 0; } INPUT_CHANGED_MEMBER( ioc2_device::power_button ) { if (!newval) m_front_panel_reg |= FRONT_PANEL_POWER_BUTTON_INT; else m_front_panel_reg &= ~FRONT_PANEL_POWER_BUTTON_INT; if (m_front_panel_reg & FRONT_PANEL_INT_MASK) raise_local_irq(1, INT3_LOCAL1_PANEL); } INPUT_CHANGED_MEMBER( ioc2_device::volume_up ) { if (!newval) { m_front_panel_reg |= FRONT_PANEL_VOL_UP_INT; m_front_panel_reg &= ~FRONT_PANEL_VOL_UP_HOLD; } else { m_front_panel_reg |= FRONT_PANEL_VOL_UP_HOLD; } if (m_front_panel_reg & FRONT_PANEL_INT_MASK) raise_local_irq(1, INT3_LOCAL1_PANEL); } INPUT_CHANGED_MEMBER( ioc2_device::volume_down ) { if (!newval) { m_front_panel_reg |= FRONT_PANEL_VOL_DOWN_INT; m_front_panel_reg &= ~FRONT_PANEL_VOL_DOWN_HOLD; } else { m_front_panel_reg |= FRONT_PANEL_VOL_DOWN_HOLD; } if (m_front_panel_reg & FRONT_PANEL_INT_MASK) raise_local_irq(1, INT3_LOCAL1_PANEL); } WRITE_LINE_MEMBER(ioc2_device::gio_int0_w) { if (state == ASSERT_LINE) raise_local_irq(0, ioc2_device::INT3_LOCAL0_FIFO); else lower_local_irq(0, ioc2_device::INT3_LOCAL0_FIFO); } WRITE_LINE_MEMBER(ioc2_device::gio_int1_w) { if (state == ASSERT_LINE) raise_local_irq(0, ioc2_device::INT3_LOCAL0_GRAPHICS); else lower_local_irq(0, ioc2_device::INT3_LOCAL0_GRAPHICS); } WRITE_LINE_MEMBER(ioc2_device::gio_int2_w) { if (state == ASSERT_LINE) raise_local_irq(1, ioc2_device::INT3_LOCAL1_RETRACE); else lower_local_irq(1, ioc2_device::INT3_LOCAL1_RETRACE); } WRITE_LINE_MEMBER(ioc2_device::hpc_dma_done_w) { if (state) raise_local_irq(1, ioc2_device::INT3_LOCAL1_HPC_DMA); else lower_local_irq(1, ioc2_device::INT3_LOCAL1_HPC_DMA); } WRITE_LINE_MEMBER(ioc2_device::mc_dma_done_w) { if (state == ASSERT_LINE) raise_local_irq(0, ioc2_device::INT3_LOCAL0_MC_DMA); else lower_local_irq(0, ioc2_device::INT3_LOCAL0_MC_DMA); } WRITE_LINE_MEMBER(ioc2_device::scsi0_int_w) { if (state) raise_local_irq(0, ioc2_device::INT3_LOCAL0_SCSI0); else lower_local_irq(0, ioc2_device::INT3_LOCAL0_SCSI0); } WRITE_LINE_MEMBER(ioc2_device::scsi1_int_w) { if (state) raise_local_irq(0, ioc2_device::INT3_LOCAL0_SCSI1); else lower_local_irq(0, ioc2_device::INT3_LOCAL0_SCSI1); } WRITE_LINE_MEMBER(ioc2_device::enet_int_w) { if (state) raise_local_irq(0, ioc2_device::INT3_LOCAL0_ETHERNET); else lower_local_irq(0, ioc2_device::INT3_LOCAL0_ETHERNET); } WRITE_LINE_MEMBER(ioc2_device::video_int_w) { if (state) raise_local_irq(1, ioc2_device::INT3_LOCAL1_VIDEO); else lower_local_irq(1, ioc2_device::INT3_LOCAL1_VIDEO); }