summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/i8257.cpp
blob: a54d21697cce69fec10e28d969467d930c79da41 (plain) (tree)
1
2
3
4
5
6
                       
                                    



                                                                            









                                                                            
                                                        














                                                                            
                                       





















                                                                      
                











                                                                            
                                                             
 
                                                                                            






                                          
                                                     








                                                   
                                                        







                                                                                     
                                             












                                                   
                                           













                                                   
                                    











                                                   
                                    



























                                                               
 






                                                   
                                     







                                                               
                                                                                        































                                                               
                                   
 

                                                              
 
              
         




















                                                                                                                       
         








                                                                            
                              

                                                   
                                                                                                         
                                                                                       

                                                         
                                


                                   

                           


                                     
                                   


                             























                                                   
                                 






                                      
                                     
                                     



                                      








                                       










                                           












                                                






                                                   
                                 










                               
                                     
         


                                   
         



                    
                                 














                                                               
                                    
         
                                            
                 
                                                                  










                                                   
                                



























































                                                                
                                  




















                                                       
                                  






















                                                                           

                                                                             
                                                                                               

                                                                                 









                                                                         
                                          














                                                   
                                    


























                                                                                                                        



                                                                                                             
                                                                   
                                 
                                                                                                                    
                                                                                  
                                 











                                                                                                          
                                        


                                       
                                                                                                 








                                                   
                                         
 
                                                                             









                                                   
                                          
 
                                                                  








                                                   
                                          








                                                   
                                          








                                                   
                                          








                                                   
                                          


                              
// license:BSD-3-Clause
// copyright-holders:Curt Coder,Carl
/***************************************************************************

    Intel 8257 DMA Controller emulation

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

#include "i8257.h"



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

const device_type I8257 = &device_creator<i8257_device>;



//**************************************************************************
//  MACROS / CONSTANTS
//**************************************************************************

#define LOG 0


enum
{
	REGISTER_ADDRESS = 0,
	REGISTER_WORD_COUNT,
	REGISTER_STATUS = 8,
	REGISTER_MODE = REGISTER_STATUS
};


#define MODE_CHAN_ENABLE(x)      BIT(m_transfer_mode, x)
#define MODE_ROTATING_PRIORITY   BIT(m_transfer_mode, 4)
#define MODE_EXTENDED_WRITE      BIT(m_transfer_mode, 5)
#define MODE_TC_STOP             BIT(m_transfer_mode, 6)
#define MODE_AUTOLOAD            BIT(m_transfer_mode, 7)
#define MODE_TRANSFER_MASK       (m_channel[m_current_channel].m_mode)
#define MODE_TRANSFER_VERIFY     0
#define MODE_TRANSFER_WRITE      1
#define MODE_TRANSFER_READ       2


enum
{
	STATE_SI,
	STATE_S0,
	STATE_S1,
	STATE_S2,
	STATE_S3,
	STATE_SW,
	STATE_S4
};



//**************************************************************************
//  INLINE HELPERS
//**************************************************************************

//-------------------------------------------------
//  dma_request -
//-------------------------------------------------

inline void i8257_device::dma_request(int channel, int state)
{
	if (LOG) logerror("I8257 '%s' Channel %u DMA Request: %u\n", tag(), channel, state);

	if (state)
	{
		m_request |= 1 << channel;
	}
	else
	{
			m_request &= ~(1 << channel);
	}
	trigger(1);
}


//-------------------------------------------------
//  is_request_active -
//-------------------------------------------------

inline bool i8257_device::is_request_active(int channel)
{
	return (BIT(m_request, channel) && MODE_CHAN_ENABLE(channel)) ? true : false;
}

//-------------------------------------------------
//  set_hreq
//-------------------------------------------------

inline void i8257_device::set_hreq(int state)
{
	if (m_hreq != state)
	{
		m_out_hrq_cb(state);
		m_hreq = state;
	}
}


//-------------------------------------------------
//  set_tc -
//-------------------------------------------------

inline void i8257_device::set_tc(int state)
{
	if (m_tc != state)
	{
		m_out_tc_cb(state);

		m_tc = state;
	}
}


//-------------------------------------------------
//  set_dack - dack is active low
//-------------------------------------------------

inline void i8257_device::set_dack()
{
	m_out_dack_0_cb(m_current_channel != 0);
	m_out_dack_1_cb(m_current_channel != 1);
	m_out_dack_2_cb(m_current_channel != 2);
	m_out_dack_3_cb(m_current_channel != 3);
}


//-------------------------------------------------
//  dma_read -
//-------------------------------------------------

inline void i8257_device::dma_read()
{
	offs_t offset = m_channel[m_current_channel].m_address;

	switch (MODE_TRANSFER_MASK)
	{
	case MODE_TRANSFER_VERIFY:
	case MODE_TRANSFER_WRITE:
		switch(m_current_channel)
		{
			case 0:
				m_temp = m_in_ior_0_cb(offset);
				break;
			case 1:
				m_temp = m_in_ior_1_cb(offset);
				break;
			case 2:
				m_temp = m_in_ior_2_cb(offset);
				break;
			case 3:
				m_temp = m_in_ior_3_cb(offset);
				break;
		}
		break;

	case MODE_TRANSFER_READ:
		m_temp = m_in_memr_cb(offset);
		break;
	}

}


//-------------------------------------------------
//  dma_write -
//-------------------------------------------------

inline void i8257_device::dma_write()
{
	offs_t offset = m_channel[m_current_channel].m_address;

	switch (MODE_TRANSFER_MASK)
	{
	case MODE_TRANSFER_VERIFY: {
		UINT8 v1 = m_in_memr_cb(offset);
		if(0 && m_temp != v1)
			logerror("%s: verify error %02x vs. %02x\n", tag(), m_temp, v1);
		break;
	}

	case MODE_TRANSFER_WRITE:
		m_out_memw_cb(offset, m_temp);
		break;

	case MODE_TRANSFER_READ:
		switch(m_current_channel)
		{
			case 0:
				m_out_iow_0_cb(offset, m_temp);
				break;
			case 1:
				m_out_iow_1_cb(offset, m_temp);
				break;
			case 2:
				m_out_iow_2_cb(offset, m_temp);
				break;
			case 3:
				m_out_iow_3_cb(offset, m_temp);
				break;
		}
		break;
	}
}


//-------------------------------------------------
//  end_of_process -
//-------------------------------------------------

inline void i8257_device::advance()
{
	bool tc = (m_channel[m_current_channel].m_count == 0);
	bool al = (MODE_AUTOLOAD && (m_current_channel == 2));

	if(tc)
	{
		m_status |= 1 << m_current_channel;
		m_request &= ~(1 << m_current_channel); // docs imply this isn't right but pc-8001 works better with it
		set_tc(1);

		if(al)
		{
			// autoinitialize
			m_channel[2].m_address = m_channel[3].m_address;
			m_channel[2].m_count = m_channel[3].m_count;
			m_channel[2].m_mode = m_channel[3].m_mode;
		}
		else if(MODE_TC_STOP)
			// disable channel
			m_transfer_mode &= ~(1 << m_current_channel);
	}

	if(!(al && tc))
	{
		m_channel[m_current_channel].m_count--;
		m_channel[m_current_channel].m_count &= 0x3fff;
		m_channel[m_current_channel].m_address++;
	}
}



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

//-------------------------------------------------
//  i8257_device - constructor
//-------------------------------------------------

i8257_device::i8257_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
	: device_t(mconfig, I8257, "Intel 8257", tag, owner, clock, "i8257", __FILE__),
		device_execute_interface(mconfig, *this),
		m_icount(0),
		m_reverse_rw(0),
		m_tc(false),
		m_msb(0),
		m_hreq(CLEAR_LINE),
		m_hack(0),
		m_ready(1),
		m_state(0),
		m_current_channel(0),
		m_last_channel(0),
		m_transfer_mode(0),
		m_status(0),
		m_request(0),
		m_temp(0),
		m_out_hrq_cb(*this),
		m_out_tc_cb(*this),
		m_in_memr_cb(*this),
		m_out_memw_cb(*this),
		m_in_ior_0_cb(*this),
		m_in_ior_1_cb(*this),
		m_in_ior_2_cb(*this),
		m_in_ior_3_cb(*this),
		m_out_iow_0_cb(*this),
		m_out_iow_1_cb(*this),
		m_out_iow_2_cb(*this),
		m_out_iow_3_cb(*this),
		m_out_dack_0_cb(*this),
		m_out_dack_1_cb(*this),
		m_out_dack_2_cb(*this),
		m_out_dack_3_cb(*this)
{
}


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

void i8257_device::device_start()
{
	// set our instruction counter
	m_icountptr = &m_icount;

	// resolve callbacks
	m_out_hrq_cb.resolve_safe();
	m_out_tc_cb.resolve_safe();
	m_in_memr_cb.resolve_safe(0);
	m_out_memw_cb.resolve_safe();
	m_in_ior_0_cb.resolve_safe(0);
	m_in_ior_1_cb.resolve_safe(0);
	m_in_ior_2_cb.resolve_safe(0);
	m_in_ior_3_cb.resolve_safe(0);
	m_out_iow_0_cb.resolve_safe();
	m_out_iow_1_cb.resolve_safe();
	m_out_iow_2_cb.resolve_safe();
	m_out_iow_3_cb.resolve_safe();
	m_out_dack_0_cb.resolve_safe();
	m_out_dack_1_cb.resolve_safe();
	m_out_dack_2_cb.resolve_safe();
	m_out_dack_3_cb.resolve_safe();

	// state saving
	save_item(NAME(m_msb));
	save_item(NAME(m_hreq));
	save_item(NAME(m_hack));
	save_item(NAME(m_ready));
	save_item(NAME(m_state));
	save_item(NAME(m_current_channel));
	save_item(NAME(m_last_channel));
	save_item(NAME(m_transfer_mode));
	save_item(NAME(m_status));
	save_item(NAME(m_request));

	save_item(NAME(m_channel[0].m_address));
	save_item(NAME(m_channel[0].m_count));
	save_item(NAME(m_channel[0].m_mode));
	save_item(NAME(m_channel[1].m_address));
	save_item(NAME(m_channel[1].m_count));
	save_item(NAME(m_channel[1].m_mode));
	save_item(NAME(m_channel[2].m_address));
	save_item(NAME(m_channel[2].m_count));
	save_item(NAME(m_channel[2].m_mode));
	save_item(NAME(m_channel[3].m_address));
	save_item(NAME(m_channel[3].m_count));
	save_item(NAME(m_channel[3].m_mode));
}


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

void i8257_device::device_reset()
{
	m_state = STATE_SI;
	m_transfer_mode = 0;
	m_status = 0;
	m_request = 0;
	m_msb = 0;
	m_current_channel = -1;
	m_last_channel = 3;
	m_hreq = -1;
	m_tc = 0;

	for (auto & elem : m_channel)
	{
		elem.m_address = 0;
		elem.m_count = 0;
		elem.m_mode = 0;
	}
	set_hreq(0);
	set_dack();
}

bool i8257_device::next_channel()
{
	int priority[] = { 0, 1, 2, 3 };

	if (MODE_ROTATING_PRIORITY)
	{
		int last_channel = m_last_channel;

		for (int channel = 3; channel >= 0; channel--)
		{
			priority[channel] = last_channel;
			last_channel--;
			if (last_channel < 0) last_channel = 3;
		}
	}

	for (auto & elem : priority)
	{
		if (is_request_active(elem))
		{
			m_current_channel = m_last_channel = elem;
			return true;
		}
	}
	return false;
}


//-------------------------------------------------
//  execute_run -
//-------------------------------------------------

void i8257_device::execute_run()
{
	do
	{
		switch (m_state)
		{
		case STATE_SI:
			set_tc(0);
			if(next_channel())
				m_state = STATE_S0;
			else
			{
				suspend_until_trigger(1, true);
				m_icount = 0;
			}
			break;

		case STATE_S0:
			set_hreq(1);

			if (m_hack)
			{
				m_state = STATE_S1;
			}
			else
			{
				suspend_until_trigger(1, true);
				m_icount = 0;
			}
			break;

		case STATE_S1:
			set_tc(0);
			m_state = STATE_S2;
			break;

		case STATE_S2:
			set_dack();
			m_state = STATE_S3;
			break;

		case STATE_S3:
			dma_read();

			if (MODE_EXTENDED_WRITE)
			{
				dma_write();
			}

			m_state = m_ready ? STATE_S4 : STATE_SW;
			break;

		case STATE_SW:
			m_state = m_ready ? STATE_S4 : STATE_SW;
			break;

		case STATE_S4:
			if (!MODE_EXTENDED_WRITE)
			{
				dma_write();
			}
			advance();

			if(next_channel())
				m_state = STATE_S1;
			else
			{
				set_hreq(0);
				m_current_channel = -1;
				m_state = STATE_SI;
				set_dack();
			}
			break;
		}
		m_icount--;
	} while (m_icount > 0);
}


//-------------------------------------------------
//  read -
//-------------------------------------------------

READ8_MEMBER( i8257_device::read )
{
	UINT8 data = 0;

	if (!BIT(offset, 3))
	{
		int channel = (offset >> 1) & 0x03;

		switch (offset & 0x01)
		{
		case REGISTER_ADDRESS:
			if (m_msb)
			{
				data = m_channel[channel].m_address >> 8;
			}
			else
			{
				data = m_channel[channel].m_address & 0xff;
			}
			break;

		case REGISTER_WORD_COUNT:
			if (m_msb)
			{
				data = (m_channel[channel].m_count >> 8);
				if(m_reverse_rw && m_channel[channel].m_mode)
					data |= (m_channel[channel].m_mode == 1) ? 0x80 : 0x40;
				else
					data |= (m_channel[channel].m_mode << 6);
			}
			else
			{
				data = m_channel[channel].m_count & 0xff;
			}
			break;
		}

		m_msb = !m_msb;
	}
	else if(offset == REGISTER_STATUS)
	{
		data = m_status;

		// clear TC bits
		m_status &= 0xf0;
	}

	return data;
}


//-------------------------------------------------
//  write -
//-------------------------------------------------

WRITE8_MEMBER( i8257_device::write )
{
	if (!BIT(offset, 3))
	{
		int channel = (offset >> 1) & 0x03;

		switch (offset & 0x01)
		{
		case REGISTER_ADDRESS:
			if (m_msb)
			{
				m_channel[channel].m_address = (data << 8) | (m_channel[channel].m_address & 0xff);
				if(MODE_AUTOLOAD && (channel == 2))
					m_channel[3].m_address = (data << 8) | (m_channel[3].m_address & 0xff);
			}
			else
			{
				m_channel[channel].m_address = (m_channel[channel].m_address & 0xff00) | data;
				if(MODE_AUTOLOAD && (channel == 2))
					m_channel[3].m_address = (m_channel[3].m_address & 0xff00) | data;
			}
			break;

		case REGISTER_WORD_COUNT:
			if (m_msb)
			{
				m_channel[channel].m_count = ((data & 0x3f) << 8) | (m_channel[channel].m_count & 0xff);
				m_channel[channel].m_mode = (data >> 6);

				if(m_reverse_rw && m_channel[channel].m_mode)
					m_channel[channel].m_mode = (m_channel[channel].m_mode == 1) ? 2 : 1;

				if(MODE_AUTOLOAD && (channel == 2))
				{
					m_channel[3].m_count = ((data & 0x3f) << 8) | (m_channel[3].m_count & 0xff);
					m_channel[3].m_mode = m_channel[2].m_mode;
				}
			}
			else
			{
				m_channel[channel].m_count = (m_channel[channel].m_count & 0xff00) | data;
				if(MODE_AUTOLOAD && (channel == 2))
					m_channel[3].m_count = (m_channel[3].m_count & 0xff00) | data;
			}
			break;
		}

		m_msb = !m_msb;
	}
	else if(offset == REGISTER_MODE)
	{
		m_transfer_mode = data;

		if (LOG) logerror("I8257 '%s' Command Register: %02x\n", tag(), m_transfer_mode);
	}
	trigger(1);
}


//-------------------------------------------------
//  hlda_w - hold acknowledge
//-------------------------------------------------

WRITE_LINE_MEMBER( i8257_device::hlda_w )
{
	if (LOG) logerror("I8257 '%s' Hold Acknowledge: %u\n", tag(), state);

	m_hack = state;
	trigger(1);
}


//-------------------------------------------------
//  ready_w - ready
//-------------------------------------------------

WRITE_LINE_MEMBER( i8257_device::ready_w )
{
	if (LOG) logerror("I8257 '%s' Ready: %u\n", tag(), state);

	m_ready = state;
}


//-------------------------------------------------
//  dreq0_w - DMA request for channel 0
//-------------------------------------------------

WRITE_LINE_MEMBER( i8257_device::dreq0_w )
{
	dma_request(0, state);
}


//-------------------------------------------------
//  dreq0_w - DMA request for channel 1
//-------------------------------------------------

WRITE_LINE_MEMBER( i8257_device::dreq1_w )
{
	dma_request(1, state);
}


//-------------------------------------------------
//  dreq1_w - DMA request for channel 2
//-------------------------------------------------

WRITE_LINE_MEMBER( i8257_device::dreq2_w )
{
	dma_request(2, state);
}


//-------------------------------------------------
//  dreq3_w - DMA request for channel 3
//-------------------------------------------------

WRITE_LINE_MEMBER( i8257_device::dreq3_w )
{
	dma_request(3, state);
}