summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/cs4031.cpp
blob: 4cc990b43dfe31e88058741d506bfcc2697d424e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

                              











                                                                            
                              




                                      
         


                                                                
 


                                                                            

                           
                        
 







                                                                       
 



                                                                
 
 



                                                                            
                                                             
 
                                                     


































                                                           




                                                           
                                                   
                                                 

                                                   




















































                                                                                       
                                                               

                                       
 








                                                                            
                                                                                                               
                                                     








                                






                                               
















                                


                                    

                              


 











                                                                    























                                          
                                       



                                         

                                                     

                                   
                                            




                                                        
                                
                                                                               
 
                                             
                                                                       

                               












                                                                                                                                                                                                        














                                                        
 

                                           








                                                   
                              























                                                                            
                                                   






                                                          
                                                               






                                                          
                                                   



                                
                                                                                         
                                      
 
                      

 
                                                               



                                
                                                                                                       















                                                            

                                       














                                                             

 



                                                                    
                                                                                                  










                                                                                              

                                                                            



                                                                            
                                                
 
                                       
   
                                 







                                         
                                                       

                                 
                                              



                    

                                            
                                                      






                                                  
                                      



























                                                                            
                                                  




                                                            
                                      
 
                              


                            
                                                                                                                    









                                                              
                                               


                            
                                                                                                   






                                                



                                           




                                 




                                           









                                 
                                 


                                              
                                  


                                               
                           



                                               
                                            
                                      
                              









                                                                            





                                                                                               
                                                                  





                                                                                          
                                                                  





                                                                                           
                                                                  





                                                                                 
                                                                  








                                                                                                                   
                                                                   





                                                                                           
                                                                   





                                                                                           
                                                                   





                                                                                 
                                                                   


























                                                                




                                                                            
                                 







                                                             
                                               
 












                                                        
                              





                                           
                      




                                               
                      

 
                                      
 
                                                      
 
                                   
 
 
                                                        


                                                         
                                         

 
                                                
 
                                                                   
 









                                             
                                         



                                          
                                             



























                                            
                                             







                                                     
                                                     

                    
                                                 




                                                                   
                                         

                      
 
 
                                    
 
                                                    
 
                                 

 
                                             
 
                                                                
 









                                                                              
                                      
         



                                             
                                                             
 
                                
 
 

                                           
                                                           
 
                                                                     
                                               
 

                                                             
         





                                               
         
 

                        
 
  
                               
 

                      

   
                                           
 
                                                      
 
                                   





                                            

         


                                  
                                  
 
                                                       



                                      
                                                        
 
                      
 
 
 




                                                                            









                                                                

  
                                






                                                                    
                                         









                                                                 
                                            








                                       
                            
 

                     
   
                                                      



                                                               





                                           
                                   
 
// license:GPL-2.0+
// copyright-holders:Dirk Best
/***************************************************************************

    Chips & Technologies CS4031 chipset

    Chipset for 486 based PC/AT compatible systems. Consists of two
    individual chips:

    * F84031
        - DRAM controller
        - ISA-bus controller
        - VESA VL-BUS controller

    * F84035 (82C206 IPC core)
        - 2x 8257 DMA controller
        - 2x 8259 interrupt controller
        - 8254 timer
        - MC14818 RTC

    TODO:
        - No emulation of memory parity checks
        - Move IPC core to its own file so it can be shared with
          other chipsets

***************************************************************************/

#include "emu.h"
#include "machine/cs4031.h"

#include "machine/ram.h"

#define LOG_GENERAL     (1U << 0)
#define LOG_REGISTER    (1U << 1)
#define LOG_MEMORY      (1U << 2)
#define LOG_IO          (1U << 3)
#define LOG_KEYBOARD    (1U << 4)

#define VERBOSE (LOG_REGISTER | LOG_MEMORY | LOG_IO /*| LOG_KEYBOARD*/)
#include "logmacro.h"

#define LOGREGISTER(...)    LOGMASKED(LOG_REGISTER, __VA_ARGS__)
#define LOGMEMORY(...)      LOGMASKED(LOG_MEMORY,   __VA_ARGS__)
#define LOGIO(...)          LOGMASKED(LOG_IO,       __VA_ARGS__)
#define LOGKEYBOARD(...)    LOGMASKED(LOG_KEYBOARD, __VA_ARGS__)


//**************************************************************************
//  DEVICE DEFINITIONS
//**************************************************************************

DEFINE_DEVICE_TYPE(CS4031, cs4031_device, "cs4031", "CS4031")

const char* const cs4031_device::m_register_names[] =
{
	/* 00 */ "RESERVED",
	/* 01 */ "DMA WAIT STATE CONTROL",
	/* 02 */ "RESERVED",
	/* 03 */ "RESERVED",
	/* 04 */ "RESERVED",
	/* 05 */ "ISA BUS COMMAND DELAY",
	/* 06 */ "ISA BUS WAIT STATES AND ADDRESS HOLD",
	/* 07 */ "ISA BUS CLOCK SELECTION",
	/* 08 */ "PERFORMANCE CONTROL",
	/* 09 */ "84035 MISC CONTROL",
	/* 0a */ "DMA CLOCK SELECTION",
	/* 0b */ "RESERVED",
	/* 0c */ "RESERVED",
	/* 0d */ "RESERVED",
	/* 0e */ "RESERVED",
	/* 0f */ "RESERVED",
	/* 10 */ "DRAM TIMING",
	/* 11 */ "DRAM SETUP",
	/* 12 */ "DRAM CONFIGURATION 0 AND 1",
	/* 13 */ "DRAM CONFIGURATION 2 AND 3",
	/* 14 */ "DRAM BLOCK 0 STARTING ADDRESS",
	/* 15 */ "DRAM BLOCK 1 STARTING ADDRESS",
	/* 16 */ "DRAM BLOCK 2 STARTING ADDRESS",
	/* 17 */ "DRAM BLOCK 3 STARTING ADDRESS",
	/* 18 */ "VIDEO AREA SHADOW AND LOCAL BUS CONTROL",
	/* 19 */ "DRAM SHADOW READ ENABLE",
	/* 1a */ "DRAM SHADOW WRITE ENABLE",
	/* 1b */ "ROMCS ENABLE",
	/* 1c */ "SOFT RESET AND GATEA20",
	/* 1d */ "RESERVED",
	/* 1e */ "RESERVED",
	/* 1f */ "RESERVED"
};

const float cs4031_device::m_dma_clock_divider[] =
{
	10, 8, 6, 0, 0, 0, 0, 0, 5, 4, 3, 2.5, 2, 1.5, 0, 0
};

//-------------------------------------------------
//  device_add_mconfig - add device configuration
//-------------------------------------------------

void cs4031_device::device_add_mconfig(machine_config &config)
{
	AM9517A(config, m_dma1, 0);
	m_dma1->out_hreq_callback().set(m_dma2, FUNC(am9517a_device::dreq0_w));
	m_dma1->out_eop_callback().set(FUNC(cs4031_device::dma1_eop_w));
	m_dma1->in_memr_callback().set(FUNC(cs4031_device::dma_read_byte));
	m_dma1->out_memw_callback().set(FUNC(cs4031_device::dma_write_byte));
	m_dma1->in_ior_callback<0>().set(FUNC(cs4031_device::dma1_ior0_r));
	m_dma1->in_ior_callback<1>().set(FUNC(cs4031_device::dma1_ior1_r));
	m_dma1->in_ior_callback<2>().set(FUNC(cs4031_device::dma1_ior2_r));
	m_dma1->in_ior_callback<3>().set(FUNC(cs4031_device::dma1_ior3_r));
	m_dma1->out_iow_callback<0>().set(FUNC(cs4031_device::dma1_iow0_w));
	m_dma1->out_iow_callback<1>().set(FUNC(cs4031_device::dma1_iow1_w));
	m_dma1->out_iow_callback<2>().set(FUNC(cs4031_device::dma1_iow2_w));
	m_dma1->out_iow_callback<3>().set(FUNC(cs4031_device::dma1_iow3_w));
	m_dma1->out_dack_callback<0>().set(FUNC(cs4031_device::dma1_dack0_w));
	m_dma1->out_dack_callback<1>().set(FUNC(cs4031_device::dma1_dack1_w));
	m_dma1->out_dack_callback<2>().set(FUNC(cs4031_device::dma1_dack2_w));
	m_dma1->out_dack_callback<3>().set(FUNC(cs4031_device::dma1_dack3_w));

	AM9517A(config, m_dma2, 0);
	m_dma2->out_hreq_callback().set(FUNC(cs4031_device::dma2_hreq_w));
	m_dma2->in_memr_callback().set(FUNC(cs4031_device::dma_read_word));
	m_dma2->out_memw_callback().set(FUNC(cs4031_device::dma_write_word));
	m_dma2->in_ior_callback<1>().set(FUNC(cs4031_device::dma2_ior1_r));
	m_dma2->in_ior_callback<2>().set(FUNC(cs4031_device::dma2_ior2_r));
	m_dma2->in_ior_callback<3>().set(FUNC(cs4031_device::dma2_ior3_r));
	m_dma2->out_iow_callback<1>().set(FUNC(cs4031_device::dma2_iow1_w));
	m_dma2->out_iow_callback<2>().set(FUNC(cs4031_device::dma2_iow2_w));
	m_dma2->out_iow_callback<3>().set(FUNC(cs4031_device::dma2_iow3_w));
	m_dma2->out_dack_callback<0>().set(FUNC(cs4031_device::dma2_dack0_w));
	m_dma2->out_dack_callback<1>().set(FUNC(cs4031_device::dma2_dack1_w));
	m_dma2->out_dack_callback<2>().set(FUNC(cs4031_device::dma2_dack2_w));
	m_dma2->out_dack_callback<3>().set(FUNC(cs4031_device::dma2_dack3_w));

	PIC8259(config, m_intc1, 0);
	m_intc1->out_int_callback().set(FUNC(cs4031_device::intc1_int_w));
	m_intc1->in_sp_callback().set_constant(1);
	m_intc1->read_slave_ack_callback().set(FUNC(cs4031_device::intc1_slave_ack_r));

	PIC8259(config, m_intc2, 0);
	m_intc2->out_int_callback().set(m_intc1, FUNC(pic8259_device::ir2_w));
	m_intc2->in_sp_callback().set_constant(0);

	PIT8254(config, m_ctc, 0);
	m_ctc->set_clk<0>(XTAL(14'318'181) / 12.0);
	m_ctc->out_handler<0>().set(m_intc1, FUNC(pic8259_device::ir0_w));
	m_ctc->set_clk<1>(XTAL(14'318'181) / 12.0);
	m_ctc->out_handler<1>().set(FUNC(cs4031_device::ctc_out1_w));
	m_ctc->set_clk<2>(XTAL(14'318'181) / 12.0);
	m_ctc->out_handler<2>().set(FUNC(cs4031_device::ctc_out2_w));

	DS12885(config, m_rtc);
	m_rtc->irq().set(m_intc2, FUNC(pic8259_device::ir0_w));
	m_rtc->set_century_index(0x32);
}


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  cs4031_device - constructor
//-------------------------------------------------

cs4031_device::cs4031_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
	device_t(mconfig, CS4031, tag, owner, clock),
	m_read_ior(*this),
	m_write_iow(*this),
	m_write_tc(*this),
	m_write_hold(*this),
	m_write_nmi(*this),
	m_write_intr(*this),
	m_write_cpureset(*this),
	m_write_a20m(*this),
	m_write_spkr(*this),
	m_cpu(*this, finder_base::DUMMY_TAG),
	m_keybc(*this, finder_base::DUMMY_TAG),
	m_isa(*this, finder_base::DUMMY_TAG),
	m_bios(*this, finder_base::DUMMY_TAG),
	m_space(nullptr),
	m_space_io(nullptr),
	m_ram(nullptr),
	m_dma1(*this, "dma1"),
	m_dma2(*this, "dma2"),
	m_intc1(*this, "intc1"),
	m_intc2(*this, "intc2"),
	m_ctc(*this, "ctc"),
	m_rtc(*this, "rtc"),
	m_dma_eop(0),
	m_dma_high_byte(0xff),
	m_dma_channel(-1),
	m_portb(0x0f),
	m_refresh_toggle(0),
	m_iochck(1),
	m_nmi_mask(1),
	m_cpureset(0),
	m_kbrst(1),
	m_ext_gatea20(0),
	m_fast_gatea20(0),
	m_emu_gatea20(0),
	m_keybc_d1_written(false),
	m_keybc_data_blocked(false),
	m_address(0),
	m_address_valid(false)
{
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void cs4031_device::device_start()
{
	ram_device *ram_dev = machine().device<ram_device>(RAM_TAG);

	// make sure the ram device is already running
	if (!ram_dev->started())
		throw device_missing_dependencies();

	// resolve callbacks
	m_read_ior.resolve_safe(0);
	m_write_iow.resolve_safe();
	m_write_tc.resolve_safe();
	m_write_hold.resolve_safe();
	m_write_nmi.resolve_safe();
	m_write_intr.resolve_safe();
	m_write_cpureset.resolve_safe();
	m_write_a20m.resolve_safe();
	m_write_spkr.resolve_safe();

	// register for state saving
	save_item(NAME(m_dma_eop));
	save_item(NAME(m_dma_page));
	save_item(NAME(m_dma_high_byte));
	save_item(NAME(m_dma_channel));
	save_item(NAME(m_portb));
	save_item(NAME(m_refresh_toggle));
	save_item(NAME(m_iochck));
	save_item(NAME(m_nmi_mask));
	save_item(NAME(m_cpureset));
	save_item(NAME(m_kbrst));
	save_item(NAME(m_ext_gatea20));
	save_item(NAME(m_fast_gatea20));
	save_item(NAME(m_emu_gatea20));
	save_item(NAME(m_address));
	save_item(NAME(m_address_valid));
	save_item(NAME(m_registers));

	m_space = &m_cpu->memory().space(AS_PROGRAM);
	m_space_io = &m_cpu->memory().space(AS_IO);

	m_ram = ram_dev->pointer();
	uint32_t ram_size = ram_dev->size();

	// install base memory
	m_space->install_ram(0x000000, 0x09ffff, m_ram);

	// install extended memory
	if (ram_size > 0x100000)
		m_space->install_ram(0x100000, ram_size - 1, m_ram + 0x100000);

	// install bios rom at cpu initial pc
	m_space->install_rom(0xffff0000, 0xffffffff, m_bios + 0xf0000);

	// install i/o accesses
	m_space_io->install_readwrite_handler(0x0000, 0x000f, read8sm_delegate(*m_dma1, FUNC(am9517a_device::read)), write8sm_delegate(*m_dma1, FUNC(am9517a_device::write)), 0xffffffff);
	m_space_io->install_readwrite_handler(0x0020, 0x0023, read8sm_delegate(*m_intc1, FUNC(pic8259_device::read)), write8sm_delegate(*m_intc1, FUNC(pic8259_device::write)), 0x0000ffff);
	m_space_io->install_write_handler(0x0020, 0x0023, write8smo_delegate(*this, FUNC(cs4031_device::config_address_w)), 0x00ff0000);
	m_space_io->install_readwrite_handler(0x0020, 0x0023, read8smo_delegate(*this, FUNC(cs4031_device::config_data_r)), write8smo_delegate(*this, FUNC(cs4031_device::config_data_w)), 0xff000000);
	m_space_io->install_readwrite_handler(0x0040, 0x0043, read8sm_delegate(*m_ctc, FUNC(pit8254_device::read)), write8sm_delegate(*m_ctc, FUNC(pit8254_device::write)), 0xffffffff);
	m_space_io->install_readwrite_handler(0x0060, 0x0063, read8smo_delegate(*this, FUNC(cs4031_device::keyb_data_r)), write8smo_delegate(*this, FUNC(cs4031_device::keyb_data_w)), 0x000000ff);
	m_space_io->install_readwrite_handler(0x0060, 0x0063, read8smo_delegate(*this, FUNC(cs4031_device::portb_r)), write8smo_delegate(*this, FUNC(cs4031_device::portb_w)), 0x0000ff00);
	m_space_io->install_readwrite_handler(0x0064, 0x0067, read8smo_delegate(*this, FUNC(cs4031_device::keyb_status_r)), write8smo_delegate(*this, FUNC(cs4031_device::keyb_command_w)), 0x000000ff);
	m_space_io->install_readwrite_handler(0x0070, 0x0073, read8sm_delegate(*m_rtc, FUNC(mc146818_device::read)), write8sm_delegate(*this, FUNC(cs4031_device::rtc_w)), 0x0000ffff);
	m_space_io->install_readwrite_handler(0x0080, 0x008f, read8sm_delegate(*this, FUNC(cs4031_device::dma_page_r)), write8sm_delegate(*this, FUNC(cs4031_device::dma_page_w)), 0xffffffff);
	m_space_io->install_readwrite_handler(0x0090, 0x0093, read8smo_delegate(*this, FUNC(cs4031_device::sysctrl_r)), write8smo_delegate(*this, FUNC(cs4031_device::sysctrl_w)), 0x00ff0000);
	m_space_io->install_readwrite_handler(0x00a0, 0x00a3, read8sm_delegate(*m_intc2, FUNC(pic8259_device::read)), write8sm_delegate(*m_intc2, FUNC(pic8259_device::write)), 0x0000ffff);
	m_space_io->install_readwrite_handler(0x00c0, 0x00df, read8sm_delegate(*this, FUNC(cs4031_device::dma2_r)), write8sm_delegate(*this, FUNC(cs4031_device::dma2_w)), 0xffffffff);
}

//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void cs4031_device::device_reset()
{
	// setup default values
	memset(&m_registers, 0x00, sizeof(m_registers));
	m_registers[ROMCS] = 0x60;

	// update rom/ram regions below 1mb
	update_read_regions();
	update_write_regions();

	// initialize dma controller clocks
	update_dma_clock();
}

//-------------------------------------------------
//  device_reset_after_children
//-------------------------------------------------

void cs4031_device::device_reset_after_children()
{
	// timer 2 default state
	m_ctc->write_gate2(1);
}


//**************************************************************************
//  DMA CONTROLLER
//**************************************************************************

offs_t cs4031_device::page_offset()
{
	switch (m_dma_channel)
	{
		case 0: return (offs_t) m_dma_page[0x07] << 16;
		case 1: return (offs_t) m_dma_page[0x03] << 16;
		case 2: return (offs_t) m_dma_page[0x01] << 16;
		case 3: return (offs_t) m_dma_page[0x02] << 16;
		case 5: return (offs_t) m_dma_page[0x0b] << 16;
		case 6: return (offs_t) m_dma_page[0x09] << 16;
		case 7: return (offs_t) m_dma_page[0x0a] << 16;
	}

	// should never get here
	return 0xff0000;
}

uint8_t cs4031_device::dma_read_byte(offs_t offset)
{
	if (m_dma_channel == -1)
		return 0xff;

	return m_space->read_byte(page_offset() + offset);
}

void cs4031_device::dma_write_byte(offs_t offset, uint8_t data)
{
	if (m_dma_channel == -1)
		return;

	m_space->write_byte(page_offset() + offset, data);
}

uint8_t cs4031_device::dma_read_word(offs_t offset)
{
	if (m_dma_channel == -1)
		return 0xff;

	uint16_t result = m_space->read_word((page_offset() & 0xfe0000) | (offset << 1));
	m_dma_high_byte = result >> 8;

	return result;
}

void cs4031_device::dma_write_word(offs_t offset, uint8_t data)
{
	if (m_dma_channel == -1)
		return;

	m_space->write_word((page_offset() & 0xfe0000) | (offset << 1), (m_dma_high_byte << 8) | data);
}

WRITE_LINE_MEMBER( cs4031_device::dma2_dack0_w )
{
	m_dma1->hack_w(state ? 0 : 1); // inverted?
}

WRITE_LINE_MEMBER( cs4031_device::dma1_eop_w )
{
	m_dma_eop = state;
	if (m_dma_channel != -1)
		m_write_tc(m_dma_channel, state, 0xff);
}

void cs4031_device::set_dma_channel(int channel, bool state)
{
	//m_write_dack(channel, state);

	if (!state)
	{
		m_dma_channel = channel;
		if (m_dma_eop)
			m_write_tc(channel, 1, 0xff);
	}
	else
	{
		if (m_dma_channel == channel)
		{
			m_dma_channel = -1;
			if (m_dma_eop)
				m_write_tc(channel, 0, 0xff);
		}
	}
}

void cs4031_device::update_dma_clock()
{
	if (m_dma_clock_divider[m_registers[DMA_CLOCK] & 0x0f] != 0)
	{
		uint32_t dma_clock = clock() / m_dma_clock_divider[m_registers[DMA_CLOCK] & 0x0f];

		if (!BIT(m_registers[DMA_WAIT_STATE], 0))
			dma_clock /= 2;

		logerror("cs4031_device::update_dma_clock: dma clock is now %u\n", dma_clock);

		m_dma1->set_unscaled_clock(dma_clock);
		m_dma2->set_unscaled_clock(dma_clock);
	}
}


//**************************************************************************
//  INTERRUPTS
//**************************************************************************

/*
    Check NMI sources and generate NMI if needed

    Not emulated here: Parity check NMI
 */
void cs4031_device::trigger_nmi()
{
	if (m_nmi_mask & BIT(m_portb, 6))
	{
		m_write_nmi(1);
		m_write_nmi(0);
	}
}

uint8_t cs4031_device::intc1_slave_ack_r(offs_t offset)
{
	if (offset == 2) // IRQ 2
		return m_intc2->acknowledge();

	return 0x00;
}

WRITE_LINE_MEMBER( cs4031_device::iochck_w )
{
	LOGIO("cs4031_device::iochck_w: %u\n", state);

	if (BIT(m_portb, 3) == 0)
	{
		if (m_iochck && state == 0)
		{
			// set channel check latch
			m_portb |= 1 << 6;
			trigger_nmi();
		}

		m_iochck = state;
	}
}


//**************************************************************************
//  TIMER
//**************************************************************************

WRITE_LINE_MEMBER( cs4031_device::ctc_out1_w )
{
	m_refresh_toggle ^= state;
	m_portb = (m_portb & 0xef) | (m_refresh_toggle << 4);
}

WRITE_LINE_MEMBER( cs4031_device::ctc_out2_w )
{
	m_write_spkr(!(state & BIT(m_portb, 1)));
	m_portb = (m_portb & 0xdf) | (state << 5);
}


//**************************************************************************
//  CHIPSET CONFIGURATION
//**************************************************************************

void cs4031_device::config_address_w(uint8_t data)
{
	m_address = data;
	m_address_valid = (m_address < 0x20) ? true : false;
}

uint8_t cs4031_device::config_data_r()
{
	uint8_t result = 0xff;

	if (m_address_valid)
	{
		LOGREGISTER("cs4031_device: read %s = %02x\n", m_register_names[m_address], m_registers[m_address]);

		result = m_registers[m_address];
	}

	// after a read the selected address needs to be reset
	m_address_valid = false;

	return result;
}

void cs4031_device::config_data_w(uint8_t data)
{
	if (m_address_valid)
	{
		LOGREGISTER("cs4031_device: write %s = %02x\n", m_register_names[m_address], data);

		// update register with new data
		m_registers[m_address] = data;

		// execute command
		switch (m_address)
		{
		case DMA_WAIT_STATE:
			update_dma_clock();
			break;

		case 0x05: break;
		case 0x06: break;
		case 0x07: break;
		case 0x08: break;
		case 0x09: break;

		case DMA_CLOCK:
			update_dma_clock();
			break;

		case 0x10: break;
		case 0x11: break;
		case 0x12: break;
		case 0x13: break;
		case 0x14: break;
		case 0x15: break;
		case 0x16: break;
		case 0x17: break;
		case 0x18: break;

		case SHADOW_READ:
			update_read_regions();
			break;

		case SHADOW_WRITE:
			update_write_regions();
			break;

		case ROMCS:
			update_read_regions();
			update_write_regions();
			break;

		case SOFT_RESET_AND_GATEA20:
			update_a20m();
			break;
		}
	}

	// after a write the selected address needs to be reset
	m_address_valid = false;
}


//**************************************************************************
//  MEMORY MAPPER
//**************************************************************************

void cs4031_device::update_read_region(int index, const char *region, offs_t start, offs_t end)
{
	if (!BIT(m_registers[SHADOW_READ], index) && BIT(m_registers[ROMCS], index))
	{
		LOGMEMORY("ROM read from %x to %x\n", start, end);

		m_space->install_read_bank(start, end, region);
		machine().root_device().membank(region)->set_base(m_bios + start);
	}
	else if (!BIT(m_registers[SHADOW_READ], index) && !BIT(m_registers[ROMCS], index))
	{
		LOGMEMORY("ISA read from %x to %x\n", start, end);

		m_space->install_read_bank(start, end, region);
		machine().root_device().membank(region)->set_base(m_isa + start - 0xc0000);
	}
	else if (BIT(m_registers[SHADOW_READ], index))
	{
		LOGMEMORY("RAM read from %x to %x\n", start, end);

		m_space->install_read_bank(start, end, region);
		machine().root_device().membank(region)->set_base(m_ram + start);
	}
	else
	{
		LOGMEMORY("NOP read from %x to %x\n", start, end);

		m_space->nop_read(start, end);
	}
}

void cs4031_device::update_write_region(int index, const char *region, offs_t start, offs_t end)
{
	if (!BIT(m_registers[SHADOW_WRITE], index) && BIT(m_registers[ROMCS], index) && BIT(m_registers[ROMCS], 7))
	{
		LOGMEMORY("ROM write from %x to %x\n", start, end);

		m_space->install_write_bank(start, end, region);
		machine().root_device().membank(region)->set_base(m_bios + start);
	}
	else if (!BIT(m_registers[SHADOW_WRITE], index) && !BIT(m_registers[ROMCS], index))
	{
		LOGMEMORY("ISA write from %x to %x\n", start, end);

		m_space->install_write_bank(start, end, region);
		machine().root_device().membank(region)->set_base(m_isa + start - 0xc0000);
	}
	else if (BIT(m_registers[SHADOW_WRITE], index))
	{
		LOGMEMORY("RAM write from %x to %x\n", start, end);

		m_space->install_write_bank(start, end, region);
		machine().root_device().membank(region)->set_base(m_ram + start);
	}
	else
	{
		LOGMEMORY("NOP write from %x to %x\n", start, end);

		m_space->nop_write(start, end);
	}
}

void cs4031_device::update_read_regions()
{
	update_read_region(0, "read_c0000", 0xc0000, 0xc3fff);
	update_read_region(1, "read_c4000", 0xc4000, 0xc7fff);
	update_read_region(2, "read_c8000", 0xc8000, 0xcbfff);
	update_read_region(3, "read_cc000", 0xcc000, 0xcffff);
	update_read_region(4, "read_d0000", 0xd0000, 0xdffff);
	update_read_region(5, "read_e0000", 0xe0000, 0xeffff);
	update_read_region(6, "read_f0000", 0xf0000, 0xfffff);
}

void cs4031_device::update_write_regions()
{
	update_write_region(0, "write_c0000", 0xc0000, 0xc3fff);
	update_write_region(1, "write_c4000", 0xc4000, 0xc7fff);
	update_write_region(2, "write_c8000", 0xc8000, 0xcbfff);
	update_write_region(3, "write_cc000", 0xcc000, 0xcffff);
	update_write_region(4, "write_d0000", 0xd0000, 0xdffff);
	update_write_region(5, "write_e0000", 0xe0000, 0xeffff);
	update_write_region(6, "write_f0000", 0xf0000, 0xfffff);
}


//**************************************************************************
//  KEYBOARD / 8042
//**************************************************************************

void cs4031_device::update_a20m()
{
	// external signal is ignored when emulation is on
	if (BIT(m_registers[SOFT_RESET_AND_GATEA20], 5))
		m_write_a20m(m_fast_gatea20 | m_emu_gatea20);
	else
		m_write_a20m(m_fast_gatea20 | m_ext_gatea20);
}

void cs4031_device::emulated_kbreset(int state)
{
	if (BIT(m_registers[SOFT_RESET_AND_GATEA20], 4))
	{
		// kbreset (input) is active low
		// cpureset (output) is active high
		m_write_cpureset(!state);
	}
}

void cs4031_device::emulated_gatea20(int state)
{
	if (BIT(m_registers[SOFT_RESET_AND_GATEA20], 5))
	{
		m_emu_gatea20 = state;
		update_a20m();
	}
}

void cs4031_device::fast_gatea20(int state)
{
	m_fast_gatea20 = state;
	update_a20m();
}

void cs4031_device::keyboard_gatea20(int state)
{
	m_ext_gatea20 = state;
	update_a20m();
}

uint8_t cs4031_device::keyb_status_r()
{
	LOGKEYBOARD("cs4031_device::keyb_status_r\n");

	return m_keybc->status_r();
}

void cs4031_device::keyb_command_blocked_w(uint8_t data)
{
	// command is optionally blocked
	if (!BIT(m_registers[SOFT_RESET_AND_GATEA20], 7))
		m_keybc->command_w(data);
}

void cs4031_device::keyb_command_w(uint8_t data)
{
	LOGKEYBOARD("cs4031_device::keyb_command_w: %02x\n", data);

	m_keybc_d1_written = false;

	switch (data)
	{
	// self-test
	case 0xaa:
		emulated_kbreset(1);
		emulated_gatea20(1);

		// self-test is never blocked
		m_keybc->command_w(data);
		break;

	case 0xd1:
		m_keybc_d1_written = true;
		keyb_command_blocked_w(data);
		break;

	case 0xf0:
	case 0xf1:
	case 0xf2:
	case 0xf4:
	case 0xf5:
	case 0xf6:
	case 0xf8:
	case 0xf9:
	case 0xfa:
	case 0xfc:
	case 0xfd:
	case 0xfe:
		// toggle keyboard reset?
		if (!BIT(data, 0))
		{
			emulated_kbreset(0);
			emulated_kbreset(1);
		}

		// toggle gatea20?
		if (!BIT(data, 1))
		{
			emulated_gatea20(0);
			emulated_gatea20(1);
		}

		keyb_command_blocked_w(data);

		break;

	case 0xff:
		// last data write was blocked?
		if (m_keybc_data_blocked)
		{
			m_keybc_data_blocked = false;
			keyb_command_blocked_w(data);
		}
		else
			m_keybc->command_w(data);

		break;

	// everything else goes directly to the keyboard controller
	default:
		m_keybc->command_w(data);
		break;
	}
}

uint8_t cs4031_device::keyb_data_r()
{
	LOGKEYBOARD("cs4031_device::keyb_data_r\n");

	return m_keybc->data_r();
}

void cs4031_device::keyb_data_w(uint8_t data)
{
	LOGKEYBOARD("cs4031_device::keyb_data_w: %02x\n", data);

	// data is blocked only for d1 command
	if (BIT(m_registers[SOFT_RESET_AND_GATEA20], 7) && m_keybc_d1_written)
	{
		m_keybc_data_blocked = true;
		emulated_kbreset(BIT(data, 0));
		emulated_gatea20(BIT(data, 1));
	}
	else
	{
		m_keybc_data_blocked = false;
		m_keybc->data_w(data);
	}
}

WRITE_LINE_MEMBER( cs4031_device::gatea20_w )
{
	LOGKEYBOARD("cs4031_device::gatea20_w: %u\n", state);

	keyboard_gatea20(state);
}

WRITE_LINE_MEMBER( cs4031_device::kbrst_w )
{
	LOGKEYBOARD("cs4031_device::kbrst_w: %u\n", state);

	// convert to active low signal (gets inverted in at_keybc.c)
	state = (state == ASSERT_LINE ? 0 : 1);

	// external kbreset is ignored when emulation enabled
	if (!BIT(m_registers[SOFT_RESET_AND_GATEA20], 4))
	{
		// detect transition
		if (m_kbrst == 1 && state == 0)
		{
			m_write_cpureset(1);
			m_write_cpureset(0);
		}
	}

	m_kbrst = state;
}

/*
    Fast CPU reset and Gate A20

    0 - Fast CPU reset
    1 - Fast Gate A20

 */
void cs4031_device::sysctrl_w(uint8_t data)
{
	LOGIO("cs4031_device::sysctrl_w: %u\n", data);

	fast_gatea20(BIT(data, 1));

	if (m_cpureset == 0 && BIT(data, 0))
	{
		// pulse reset line
		m_write_cpureset(1);
		m_write_cpureset(0);
	}

	m_cpureset = BIT(data, 0);
}

uint8_t cs4031_device::sysctrl_r()
{
	uint8_t result = 0; // reserved bits read as 0?

	result |= m_cpureset << 0;
	result |= m_fast_gatea20 << 1;

	LOGIO("cs4031_device::sysctrl_r: %u\n", result);

	return result;
}


//**************************************************************************
//  MISCELLANEOUS
//**************************************************************************

/*
    "Port B" - AT-compatible port with miscellaneous information

    0 - Timer 2 gate (rw)
    1 - Speaker data (rw)
    2 - Enable parity check (rw) [not emulated]
    3 - Enable IOCHECK (rw)
    4 - Refresh detect (r)
    5 - Timer 2 output (r)
    6 - Channel check latch (r)
    7 - Parity check latch (r) [not emulated]
*/

uint8_t cs4031_device::portb_r()
{
	if (0)
		logerror("cs4031_device::portb_r: %02x\n", m_portb);

	return m_portb;
}

void cs4031_device::portb_w(uint8_t data)
{
	if (0)
		logerror("cs4031_device::portb_w: %02x\n", data);

	m_portb = (m_portb & 0xf0) | (data & 0x0f);

	// bit 5 forced to 1 if timer disabled
	if (!BIT(m_portb, 0))
		m_portb |= 1 << 5;

	m_ctc->write_gate2(BIT(m_portb, 0));

	m_write_spkr(!BIT(m_portb, 1));

	// clear channel check latch?
	if (BIT(m_portb, 3))
		m_portb &= 0xbf;
}

/*
    NMI mask and RTC address

    7   - NMI mask
    6:0 - RTC address
 */
void cs4031_device::rtc_w(offs_t offset, uint8_t data)
{
	if (0)
		logerror("cs4031_device::rtc_w: %02x\n", data);

	if (offset == 0)
	{
		m_nmi_mask = !BIT(data, 7);
		data &= 0x7f;
	}

	m_rtc->write(offset, data);
}