summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/ioc2.cpp
blob: eeff60b99d17b3909a09d9237e1ffb6c252dcbb1 (plain) (tree)
1
2
3
4
5
6
7
8
9
10









                                                                       
                                 

                         










                                 

                                                                                                                                                                      
                                                
 
                           

                     





                                                                
 

                                                                                       
                                                         
                                                                                       

                                                         
                                                                                               


                                                                                                                           
                                                                    



                                                                                                                               
                                                                      














                                                                                                                                          

                                                            







                                                                                                                      
                                                                      
 



                                                                                                  
 



                                                                                                  
 
                              
 



                                                                                            

                        










                                                                                                          


                                                             



                                                                                  




                                                                                 
 
                                  

                             
                                   

                                                                   
                                                                       
 

 
                                                                                                                           












                                                    
                                  
                               


                                  




                                











                                               

                                                 
                                               
                                             

                                               








                                  
                                                                                                          





                        

                                                                
                                  
                                                            
                               


                                  
                                                            
 
                                                             
                                                 


                                                                                                                                              

 
                                                            
 
                                                             
                                                  


                                                                                                                                               

 

                                          
                  



                                                            



                                          
                  









                                                            

 











                                                            



                                                          




                                               


                                                                            
                                            
         
 


                                                                            
















                                                                         
                    
                                                                        
         

 
                                
 


                                                                                                    
 



                                                                                                             
 




                                                                                                         
 



                                                                                                                  
 




                                                                                                       
 



                                                                                                                
 




                                                                                               
 


                                                                                               


                 
                              
 













                                                                                                        
 























































































                                                                                                                                                                                                                      
         
                                                    
                 

                                                                                                                                                                       
                 
         
                                                                 

 
                         
 

                                                                                                           

 
                                  
 






































































































                                                                                                                                                                                             
                                                                                              











































                                                                                                                                    


                                                               
 
                                                     
                                              

                                                                                                 

                                 
                                                                                                             
                                                                                             


         
                                                             
 
                                            
                                          

 
                                                   

                         



                                                             
                         



                                                             

 


                                                                                                                                                                                           
                          
         
                                  

                        




                                                 
                                                                  



                                                                   
                                                      






                                                            
                                                              


            
                                                             
         

                                                     
                                                      






                                                              
                                                                


            
                                                               
         

                                                     
                                                      
 


























                                                                      
                  











                                                                     















                                                                   








                                                                      






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