summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/mos6530.cpp
blob: 2b50efd9b470c0725288f5b26a3bf294669c603a (plain) (tree)
1
2

                               































                                                                            
                            
 
                                                                            
                    

                                                                            
                                                                        
 
                                                                                                               
                                                        



                                   

 
 
                                                   

                                                   
 



                                   
 
                               



                                   
 

                                                  
 
                                      





                                         






                                      
 


                                                   
 
                                   
 
                              



                            









                                         


 




                                                                            




                                                   
                                      
 
                                      
 

                                                                               
 
                                    






                                                   
                                   

                               
                                       


                                                               

                                                                              


                                                                        
                                                              











                                                                            
                                                                                             
 
                   
         


                                                           
 




                                                                                    
 











                                                                                


         







                                                                            
                                      
 


                                                     
                                                                     
                                                    
                               

                                                     
                                                      


                                                      
                                                  
                    
                                                   

                                                      


                                                                           

                                      


                                                                                 





                                                           
                                                             


                                                                  
                                           



                                                                     
                                                      
                                           
 
                                                        
                         

                                                                                                       

                         

                                                             
                            
                                                             








                                                   
                                    
 
                    



                                                                    
                                 




                                                              
                                  


                                                      
                                                  
                    
                                                   

                                                      


                                                                   





                                                                      
                                                             


                                                               
                                          



                                                                     
                                                  
 

                                                                                               

                                                                  

                                                           
                            
                                                           

                                                         
                                                                                










                                                   
                                                             
 
                                                                  







                                                   
                                                             
 
                                                                  







                                                   
                                      
 
                              







                                                   
                                      
 
                              







                                                   
                                       
 
                               







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

  MIOT 6530 emulation

The timer seems to follow these rules:
- When the timer flag changes from 0 to 1 the timer continues to count
  down at a 1 cycle rate.
- When the timer is being read or written the timer flag is reset.
- When the timer flag is set and the timer contents are 0, the counting
  stops.

From the operation of the KIM1 it expects the irqflag to be set whenever
the unit is reset. This is something that is not clear from the datasheet
and should be verified against real hardware.

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

#include "emu.h"
#include "mos6530.h"


/***************************************************************************
    CONSTANTS
***************************************************************************/

enum
{
	TIMER_IDLE,
	TIMER_COUNTING,
	TIMER_FINISHING
};

#define TIMER_FLAG      0x80

/***************************************************************************
    DEVICE INTERFACE
***************************************************************************/

DEFINE_DEVICE_TYPE(MOS6530, mos6530_device, "mos6530", "MOS 6530 RRIOT")

mos6530_device::mos6530_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, MOS6530, tag, owner, clock),
		m_in_pa_cb(*this),
		m_out_pa_cb(*this),
		m_in_pb_cb(*this),
		m_out_pb_cb(*this)
{
}

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

void mos6530_device::device_start()
{
	/* set static values */
	m_clock = clock();

	/* resolve callbacks */
	m_in_pa_cb.resolve_safe(0);
	m_out_pa_cb.resolve_safe();
	m_in_pb_cb.resolve_safe(0);
	m_out_pb_cb.resolve_safe();

	/* allocate timers */
	m_timer = timer_alloc(TIMER_END_CALLBACK);

	/* register for save states */
	save_item(NAME(m_port[0].m_in));
	save_item(NAME(m_port[0].m_out));
	save_item(NAME(m_port[0].m_ddr));
	save_item(NAME(m_port[1].m_in));
	save_item(NAME(m_port[1].m_out));
	save_item(NAME(m_port[1].m_ddr));

	save_item(NAME(m_irqstate));
	save_item(NAME(m_irqenable));

	save_item(NAME(m_timershift));
	save_item(NAME(m_timerstate));
}

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

void mos6530_device::device_reset()
{
	/* reset I/O states */
	m_port[0].m_out = 0;
	m_port[0].m_ddr = 0;
	m_port[1].m_out = 0;
	m_port[1].m_ddr = 0;

	/* reset IRQ states */
	m_irqenable = 0;
	m_irqstate = TIMER_FLAG;
	update_irqstate();

	/* reset timer states */
	m_timershift = 0;
	m_timerstate = TIMER_IDLE;
	m_timer->adjust(attotime::never);
}


/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/


/*-------------------------------------------------
    update_irqstate - update the IRQ state
    based on interrupt enables
-------------------------------------------------*/

void mos6530_device::update_irqstate()
{
	uint8_t out = m_port[1].m_out;

	if (m_irqenable)
		out = ((m_irqstate & TIMER_FLAG) ? 0x00 : 0x80) | (out & 0x7F);

	m_out_pb_cb((offs_t)0, out);
}


/*-------------------------------------------------
    get_timer - return the current timer value
-------------------------------------------------*/

uint8_t mos6530_device::get_timer()
{
	/* if idle, return 0 */
	if (m_timerstate == TIMER_IDLE)
		return 0;

	/* if counting, return the number of ticks remaining */
	else if (m_timerstate == TIMER_COUNTING)
		return m_timer->remaining().as_ticks(m_clock) >> m_timershift;

	/* if finishing, return the number of ticks without the shift */
	else
		return m_timer->remaining().as_ticks(m_clock);
}


/***************************************************************************
    INTERNAL FUNCTIONS
***************************************************************************/

/*-------------------------------------------------
    timer_end_callback - callback to process the
    timer
-------------------------------------------------*/

void mos6530_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		// deferred reset
		case TIMER_END_CALLBACK:
			assert(m_timerstate != TIMER_IDLE);

			/* if we finished counting, switch to the finishing state */
			if (m_timerstate == TIMER_COUNTING)
			{
				m_timerstate = TIMER_FINISHING;
				m_timer->adjust(attotime::from_ticks(256, m_clock));

				/* signal timer IRQ as well */
				m_irqstate |= TIMER_FLAG;
				update_irqstate();
			}

			/* if we finished finishing, switch to the idle state */
			else if (m_timerstate == TIMER_FINISHING)
			{
				m_timerstate = TIMER_IDLE;
				m_timer->adjust(attotime::never);
			}
			break;
	}
}

/***************************************************************************
    I/O ACCESS
***************************************************************************/

/*-------------------------------------------------
    mos6530_w - master I/O write access
-------------------------------------------------*/

WRITE8_MEMBER( mos6530_device::write )
{
	/* if A2 == 1, we are writing to the timer */
	if (offset & 0x04)
	{
		static const uint8_t timershift[4] = { 0, 3, 6, 10 };
		attotime curtime = machine().time();
		int64_t target;

		/* A0-A1 contain the timer divisor */
		m_timershift = timershift[offset & 3];

		/* A3 contains the timer IRQ enable */
		if (offset & 8)
			m_irqenable |= TIMER_FLAG;
		else
			m_irqenable &= ~TIMER_FLAG;

		/* writes here clear the timer flag */
		if (m_timerstate != TIMER_FINISHING || get_timer() != 0xff)
			m_irqstate &= ~TIMER_FLAG;
		update_irqstate();

		/* update the timer */
		m_timerstate = TIMER_COUNTING;
		target = curtime.as_ticks(m_clock) + 1 + (data << m_timershift);
		m_timer->adjust(attotime::from_ticks(target, m_clock) - curtime);
	}

	/* if A2 == 0, we are writing to the I/O section */
	else
	{
		/* A1 selects the port */
		mos6530_port *port = &m_port[BIT(offset, 1)];

		/* if A0 == 1, we are writing to the port's DDR */
		if (offset & 1)
			port->m_ddr = data;

		/* if A0 == 0, we are writing to the port's output */
		else
		{
			uint8_t olddata = port->m_out;
			port->m_out = data;

			if ((offset & 2) && m_irqenable)
			{
				olddata = ((m_irqstate & TIMER_FLAG) ? 0x00 : 0x80) | (olddata & 0x7F);
				data = ((m_irqstate & TIMER_FLAG) ? 0x00 : 0x80) | (data & 0x7F);
			}

			if (!BIT(offset, 1))
				m_out_pa_cb((offs_t)0, data);
			else
				m_out_pb_cb((offs_t)0, data);
		}
	}
}


/*-------------------------------------------------
    mos6530_r - master I/O read access
-------------------------------------------------*/

READ8_MEMBER( mos6530_device::read )
{
	uint8_t val;

	/* if A2 == 1 and A0 == 1, we are reading interrupt flags */
	if ((offset & 0x05) == 0x05)
	{
		val = m_irqstate;
	}

	/* if A2 == 1 and A0 == 0, we are reading the timer */
	else if ((offset & 0x05) == 0x04)
	{
		val = get_timer();

		/* A3 contains the timer IRQ enable */
		if (offset & 8)
			m_irqenable |= TIMER_FLAG;
		else
			m_irqenable &= ~TIMER_FLAG;

		/* implicitly clears the timer flag */
		if (m_timerstate != TIMER_FINISHING || val != 0xff)
			m_irqstate &= ~TIMER_FLAG;
		update_irqstate();
	}

	/* if A2 == 0 and A0 == anything, we are reading from ports */
	else
	{
		/* A1 selects the port */
		mos6530_port *port = &m_port[BIT(offset, 1)];

		/* if A0 == 1, we are reading the port's DDR */
		if (offset & 1)
			val = port->m_ddr;

		/* if A0 == 0, we are reading the port as an input */
		else
		{
			uint8_t out = port->m_out;

			if ((offset & 2) && m_irqenable)
				out = ((m_irqstate & TIMER_FLAG) ? 0x00 : 0x80) | (out & 0x7F);

			/* call the input callback if it exists */
			if (!BIT(offset, 1))
				port->m_in = m_in_pa_cb(0);
			else
				port->m_in = m_in_pb_cb(0);

			/* apply the DDR to the result */
			val = (out & port->m_ddr) | (port->m_in & ~port->m_ddr);
		}
	}
	return val;
}


/*-------------------------------------------------
    mos6530_porta_in_set - set port A input
    value
-------------------------------------------------*/

void mos6530_device::porta_in_set(uint8_t data, uint8_t mask)
{
	m_port[0].m_in = (m_port[0].m_in & ~mask) | (data & mask);
}


/*-------------------------------------------------
    mos6530_portb_in_set - set port B input
    value
-------------------------------------------------*/

void mos6530_device::portb_in_set(uint8_t data, uint8_t mask)
{
	m_port[1].m_in = (m_port[1].m_in & ~mask) | (data & mask);
}


/*-------------------------------------------------
    mos6530_porta_in_get - return port A input
    value
-------------------------------------------------*/

uint8_t mos6530_device::porta_in_get()
{
	return m_port[0].m_in;
}


/*-------------------------------------------------
    mos6530_portb_in_get - return port B input
    value
-------------------------------------------------*/

uint8_t mos6530_device::portb_in_get()
{
	return m_port[1].m_in;
}


/*-------------------------------------------------
    mos6530_porta_in_get - return port A output
    value
-------------------------------------------------*/

uint8_t mos6530_device::porta_out_get()
{
	return m_port[0].m_out;
}


/*-------------------------------------------------
    mos6530_portb_in_get - return port B output
    value
-------------------------------------------------*/

uint8_t mos6530_device::portb_out_get()
{
	return m_port[1].m_out;
}