summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/cpu/m6805/m68hc05.cpp
blob: 518a0086025c99469c21f7f368d626c1aa0de50a (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12

                              









                                                                       
                                                                       

                                                                        



                                                                         
  


                      
                     
 
 



                                                                              




                              

                              
 
                                                            


                                



                                                          

                                                          
 


           



















                                                                                                                     




                                                                       
                             

                                                    












                                                                                                                     






                                                          

                                                 
                                                 
                                                 

                                                   
 

                                                      

                                                      
 
                                                                                                                  
 


                        
 



                                                                              


                                                                                           
                                                                                           
                                                                                         
                                                                                          
 











                                                                              
                               
                                
                                                     





                                
                                                                                                               
                                     


                                                   
                                                    


                                                


                                 


                             








                                                         



 
                                                                          


                                                                                              















                                                                                                                            

 
                                       

                                 
                                                                                

                                                                                                                                

                                                             




                                                                                                                                     

                                                                          



























                                                                                                    

                                                 


                                                                                                          














                                                                                                                                                     



                                                                                      







                                                                  
                                                                              


                      











                                                         

                                     


                                
                                       
         

                      
                                                                                                                            








                                                                                                                
                                                                                









                                                     
                                                                              
                        
                               
                      




                                                                             




                                                                                                                 
                      




                                       

















                                                                  




                                                                                      














                                                                  
                                                

                                                 
                                                       






                                         




                                                                                      



                                      










                                         
                                               



                 




































                                                                                   










                                                                                                    






                                                                                                                        
                                                                                 


                                        
                                                                                 
         
                               









                                    
                               





















                                                                             
                               








                                                                            












                                                                                              
                                                          
            
                                                           



                                   
                                               

















                                                                                                                                  
                                               







                                                                   
                                                                                                        













                                                                                     
 


                                                                 
                                                                            



                                                   
                                                                                        

                                          

 





                                                                 
                                               







                                                                    
                                                                                                        























                                                                                      
                                                       







                                                                                                                 
                                                                                                        





                                      
                                                                              









                                                                                              
                                        












                                                                                                       
                                      






                                                             
                                       





                                                                                                                       
                                      












                                                               


                                    


                                 
                                          


                                      


                                          
 















                                          
                                





                                          
                              
 













                                        
                                                
                                                                                  

                                                                          
                                 
 















                                              
                      





                              
                    
 






                                          

                                                                        







                                                                      

                          
 















                               
                    
 







                                                    






















                                                 






                             



                                                               











                                                                                                            







                                                                                                           
                                                                                                    



                                           
                
                                                                                  


         
                                                                       



                                
                                                                       



                          
                                                                             
 
                                                        


 

                                
                                                                       
         







                                                           
                 
                                                                 
                                        
                                                                 
                                                                                
                 


                                                                  
                                                                                  
                 









                                                                                





                                                                                                                


         




                                


























                                                                                           
























                                                                                           
                                                                            
 



                                   
         

                                                                               




                                                            
                                                                                                                         






                                                                            
                                                               




                                            
         
 

                                    
         
                                             
                 
                                                  


                    
                                                              



                                               
                                               





                                                  
                                             
                 
                                                  


                    
                                                           


                                               
                                               
                 
         


 













                                                                                                                                             









                                                            
                                      





                                                                     
                                      





                                                                     
























                                                                                                                      










                                                                              
                               
                                                     
                                                                                                            




                        




 



                                                                              



                                               
 
                                                                                                        
                                                                                                       
                               







                                                                                                 




                                                                                             
                               


                                               
                               
                                                
                               

                                                  
 
 
                                                                                                              





                                  
                           
                               
                                                                                      
 
                                                                              

 





                                       
                                                                                  



                          
                                                                               
 
                                                                      







                                                                              



                                               
 
                                                                                                        
                                                                                                       
                               







                                                                                                 




                                                                                             
                               



                                                
                               

                                                  

 
                                                                                                              





                                  
                           
                               
                                                                                      
 
                                                                              






                                       
                                                                                  



                          
                                                                               

                                           
                                                                      







                                                                              



                                                  
 
                                                                                                            
                                                                                                           
                               







                                                                                                     




                                                                                                 
                      

                                                                                                     
                        




                                                                                                      
                                                         
                                                                                   
                                                                

                                                                                  

 
                                                                                                                  





                                    
                           
                                                                                         
 
                                                                              












                                                                   
                                                                                  








                                        


                                                                                                


 
                                                                                 
 
                                                                        
 



                                                                             













































                                                                                                                  






                                        

                                                                                










                                                                                 













                                                                                                        




                                                                                                 




                                                                                             
                                              























                                                                                                              
                               

















                                                                                      
                                                                      
 
















                                                                                                          




                                                                                                   







































                                                                                                                
                               



















                                                                                        
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*
High-speed CMOS 6805-compatible microcontrollers

The M68HC05 family uses the M6805 instruction set with a few additions
but runs at two clocks per machine cycle, and has incompatible on-board
peripherals.  It comes in mask ROM (M68HC05), EPROM (M68HC705) and
EEPROM (M68HC805) variants.  The suffix gives some indication of the
memory sizes and on-board peripherals, but there's not a lot of
consistency across the ROM/EPROM/EEPROM variants.

Most devices in this family have a 16-bit free-running counter fed from
the internal clock.  The counter value can be captured on an input edge,
and an output can be automatically set when the counter reaches a
certain value.  The lower-end devices instead have a 15-bit multifunction
ripple counter with a programmable selector for the last four stages that
determines both the COP watchdog timeout and the real-time interrupt rate
(this is not currently emulated).
*/
#include "emu.h"
#include "m68hc05.h"
#include "m6805defs.h"
#include "6805dasm.h"


/****************************************************************************
 * Configurable logging
 ****************************************************************************/

#define LOG_GENERAL (1U <<  0)
#define LOG_INT     (1U <<  1)
#define LOG_IOPORT  (1U <<  2)
#define LOG_TIMER   (1U <<  3)
#define LOG_COP     (1U <<  4)
#define LOG_UART    (1U <<  5)
#define LOG_SPI     (1U <<  6)

#define VERBOSE (LOG_GENERAL | LOG_INT | LOG_COP | LOG_UART)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"

#define LOGINT(...)     LOGMASKED(LOG_INT,    __VA_ARGS__)
#define LOGIOPORT(...)  LOGMASKED(LOG_IOPORT, __VA_ARGS__)
#define LOGTIMER(...)   LOGMASKED(LOG_TIMER,  __VA_ARGS__)
#define LOGCOP(...)     LOGMASKED(LOG_COP,    __VA_ARGS__)
#define LOGUART(...)    LOGMASKED(LOG_UART,   __VA_ARGS__)
#define LOGSPI(...)     LOGMASKED(LOG_SPI,    __VA_ARGS__)


namespace {

std::pair<u16, char const *> const m68hc05c4_syms[] = {
	{ 0x0000, "PORTA"  }, { 0x0001, "PORTB"  }, { 0x0002, "PORTC"  }, { 0x0003, "PORTD"  },
	{ 0x0004, "DDRA"   }, { 0x0005, "DDRB"   }, { 0x0006, "DDRC"   },
	{ 0x000a, "SPCR"   }, { 0x000b, "SPSR"   }, { 0x000c, "SPDR"   },
	{ 0x000d, "BAUD"   }, { 0x000e, "SCCR1"  }, { 0x000f, "SCCR2"  }, { 0x0010, "SCSR"   }, { 0x0011, "SCDR"   },
	{ 0x0012, "TCR"    }, { 0x0013, "TSR"    },
	{ 0x0014, "ICRH"   }, { 0x0015, "ICRL"   }, { 0x0016, "OCRH"   }, { 0x0017, "OCRL"   },
	{ 0x0018, "TRH"    }, { 0x0019, "TRL"    }, { 0x001a, "ATRH"   }, { 0x001b, "ATRL"   } };

std::pair<u16, char const *> const m68hc705c8a_syms[] = {
	{ 0x0000, "PORTA"  }, { 0x0001, "PORTB" }, { 0x0002, "PORTC" }, { 0x0003, "PORTD" },
	{ 0x0004, "DDRA"   }, { 0x0005, "DDRB"  }, { 0x0006, "DDRC"  },
	{ 0x000a, "SPCR"   }, { 0x000b, "SPSR"  }, { 0x000c, "SPDR"  },
	{ 0x000d, "BAUD"   }, { 0x000e, "SCCR1" }, { 0x000f, "SCCR2" }, { 0x0010, "SCSR"  }, { 0x0011, "SCDR"  },
	{ 0x0012, "TCR"    }, { 0x0013, "TSR"   },
	{ 0x0014, "ICRH"   }, { 0x0015, "ICRL"  }, { 0x0016, "OCRH"  }, { 0x0017, "OCRL"  },
	{ 0x0018, "TRH"    }, { 0x0019, "TRL"   }, { 0x001a, "ATRH"  }, { 0x001b, "ATRL"  },
	{ 0x001c, "PROG"   },
	{ 0x001d, "COPRST" }, { 0x001e, "COPCR" } };

std::pair<u16, char const *> const m68hc705j1a_syms[] = {
	{ 0x0000, "PORTA"  }, { 0x0001, "PORTB" },
	{ 0x0004, "DDRA"   }, { 0x0005, "DDRB"  },
	{ 0x0008, "TSCR"   }, { 0x0009, "TCR"   }, { 0x000a, "ISCR"  },
	{ 0x0010, "PDRA"   }, { 0x0011, "PDRB"  },
	{ 0x0014, "EPROG"  },
	{ 0x07f0, "COPR"   }, { 0x07f1, "MOR"   } };

std::pair<u16, char const *> const m68hc05l9_syms[] = {
	{ 0x0000, "PORTA"  }, { 0x0001, "PORTB"  }, { 0x0002, "PORTC"  }, { 0x0003, "PORTD"  },
	{ 0x0004, "DDRA"   }, { 0x0005, "DDRB"   }, { 0x0006, "DDRC"   }, { 0x0007, "DDRD"   },
	{ 0x0008, "COUNT"  },
	{ 0x0009, "GCR1"   }, { 0x000a, "GCR2"   },
	{ 0x000b, "MINA"   }, { 0x000c, "HOURA"  },
	{ 0x000d, "BAUD"   }, { 0x000e, "SCCR1"  }, { 0x000f, "SCCR2"  }, { 0x0010, "SCSR"   }, { 0x0011, "SCDR"   },
	{ 0x0012, "TCR"    }, { 0x0013, "TSR"    },
	{ 0x0014, "ICRH"   }, { 0x0015, "ICRL"   }, { 0x0016, "OCRH"   }, { 0x0017, "OCRL"   },
	{ 0x0018, "TRH"    }, { 0x0019, "TRL"    }, { 0x001a, "ATRH"   }, { 0x001b, "ATRL"   },
	{ 0x001c, "RTCSR"  },
	{ 0x001d, "HOUR"   }, { 0x001e, "MIN"    }, { 0x001f, "SEC"    } };


ROM_START( m68hc705c8a )
	ROM_REGION(0x00f0, "bootstrap", 0)
	ROM_LOAD("bootstrap.bin", 0x0000, 0x00f0, NO_DUMP)
ROM_END


constexpr u16 M68HC05_VECTOR_SPI        = 0xfff4;
constexpr u16 M68HC05_VECTOR_SCI        = 0xfff6;
constexpr u16 M68HC05_VECTOR_TIMER      = 0xfff8;
constexpr u16 M68HC05_VECTOR_IRQ        = 0xfffa;
constexpr u16 M68HC05_VECTOR_SWI        = 0xfffc;
//constexpr u16 M68HC05_VECTOR_RESET      = 0xfffe;

constexpr u16 M68HC05_INT_IRQ           = u16(1) << 0;
constexpr u16 M68HC05_INT_TIMER         = u16(1) << 1;
constexpr u16 M68HC05_INT_SCI           = u16(1) << 2;
constexpr u16 M68HC05_INT_SPI           = u16(1) << 3;

constexpr u16 M68HC05_INT_MASK          = M68HC05_INT_IRQ | M68HC05_INT_TIMER | M68HC05_INT_SCI | M68HC05_INT_SPI;

} // anonymous namespace



/****************************************************************************
 * Global variables
 ****************************************************************************/

DEFINE_DEVICE_TYPE(M68HC05C4,   m68hc05c4_device,   "m68hc05c4",   "Motorola MC68HC05C4")
DEFINE_DEVICE_TYPE(M68HC05C8,   m68hc05c8_device,   "m68hc05c8",   "Motorola MC68HC05C8")
DEFINE_DEVICE_TYPE(M68HC705C8A, m68hc705c8a_device, "m68hc705c8a", "Motorola MC68HC705C8A")
DEFINE_DEVICE_TYPE(M68HC705J1A, m68hc705j1a_device, "m68hc705j1a", "Motorola MC68HC705J1A")
DEFINE_DEVICE_TYPE(M68HC05L9,   m68hc05l9_device,   "m68hc05l9",   "Motorola MC68HC05L9")
DEFINE_DEVICE_TYPE(M68HC05L11,  m68hc05l11_device,  "m68hc05l11",  "Motorola MC68HC05L11")



/****************************************************************************
 * M68HC05 base device
 ****************************************************************************/

m68hc05_device::m68hc05_device(
		machine_config const &mconfig,
		char const *tag,
		device_t *owner,
		u32 clock,
		device_type type,
		u32 addr_width,
		u16 vector_mask,
		address_map_constructor internal_map)
	: m6805_base_device(
			mconfig,
			tag,
			owner,
			clock,
			type,
			{ s_hc_ops, s_hc_cycles, addr_width, 0x00ff, 0x00c0, vector_mask, M68HC05_VECTOR_SWI },
			internal_map)
	, m_port_cb_r{ *this, *this, *this, *this }
	, m_port_cb_w{ *this, *this, *this, *this }
	, m_port_bits{ 0xff, 0xff, 0xff, 0xff }
	, m_port_interrupt{ 0x00, 0x00, 0x00, 0x00 }
	, m_port_input{ 0xff, 0xff, 0xff, 0xff }
	, m_port_latch{ 0xff, 0xff, 0xff, 0xff }
	, m_port_ddr{ 0x00, 0x00, 0x00, 0x00 }
	, m_port_irq_state(false)
	, m_irq_line_state(false)
	, m_irq_latch(0)
	, m_uart_tx_cb(*this)
	, m_sck_out_cb(*this)
	, m_sda_out_cb(*this)
	, m_tcmp_cb(*this)
	, m_tcap_state(false)
	, m_tcr(0x00)
	, m_tsr(0x00), m_tsr_seen(0x00)
	, m_prescaler(0x00)
	, m_counter(0xfffc), m_icr(0x0000), m_ocr(0x0000)
	, m_inhibit_cap(false), m_inhibit_cmp(false)
	, m_trl_buf{ 0xfc, 0xfc }
	, m_trl_latched{ false, false }
{
}


void m68hc05_device::set_port_bits(std::array<u8, PORT_COUNT> const &bits)
{
	if (configured() || started())
		throw emu_fatalerror("Attempt to set physical port bits after configuration");

	for (unsigned i = 0; PORT_COUNT > i; ++i)
		m_port_bits[i] = bits[i];
}

void m68hc05_device::set_port_interrupt(std::array<u8, PORT_COUNT> const &interrupt)
{
	u8 diff(0x00);
	for (unsigned i = 0; PORT_COUNT > i; ++i)
	{
		diff |= (m_port_interrupt[i] ^ interrupt[i]) & ~m_port_ddr[i];
		m_port_interrupt[i] = interrupt[i];
		if (interrupt[i] && !m_port_cb_r[i].isnull())
			logerror("PORT%c has interrupts enabled with pulled inputs, behaviour may be incorrect\n", 'A' + i);
	}
	if (diff) update_port_irq();
}

READ8_MEMBER(m68hc05_device::port_read)
{
	offset &= PORT_COUNT - 1;
	if (!machine().side_effects_disabled() && !m_port_cb_r[offset].isnull())
	{
		u8 const newval(m_port_cb_r[offset](space, 0, ~m_port_ddr[offset] & m_port_bits[offset]) & m_port_bits[offset]);
		u8 const diff(newval ^ m_port_input[offset]);
		if (diff)
		{
			LOGIOPORT("read PORT%c: new input = %02X & %02X (was %02X)\n",
					char('A' + offset), newval, ~m_port_ddr[offset] & m_port_bits[offset], m_port_input[offset]);
		}
		m_port_input[offset] = newval;
		if (diff & m_port_interrupt[offset] & ~m_port_ddr[offset])
			update_port_irq();
	}
	return port_value(offset);
}

WRITE8_MEMBER(m68hc05_device::port_latch_w)
{
	offset &= PORT_COUNT - 1;
	data &= m_port_bits[offset];
	u8 const diff = m_port_latch[offset] ^ data;
	if (diff)
	{
		LOGIOPORT("write PORT%c latch: %02X & %02X (was %02X)\n",
				char('A' + offset), data, m_port_ddr[offset], m_port_latch[offset]);
	}
	m_port_latch[offset] = data;
	if (diff & m_port_ddr[offset])
		m_port_cb_w[offset](space, 0, port_value(offset), m_port_ddr[offset]);
}

READ8_MEMBER(m68hc05_device::port_ddr_r)
{
	return m_port_ddr[offset & (PORT_COUNT - 1)];
}

WRITE8_MEMBER(m68hc05_device::port_ddr_w)
{
	offset &= PORT_COUNT - 1;
	data &= m_port_bits[offset];
	u8 const diff(data ^ m_port_ddr[offset]);
	if (diff)
	{
		LOGIOPORT("write DDR%c: %02X (was %02X)\n", char('A' + offset), data, m_port_ddr[offset]);
		m_port_ddr[offset] = data;
		if (diff & m_port_interrupt[offset])
		{
			if (!m_port_cb_r[offset].isnull())
			{
				u8 const newval(m_port_cb_r[offset](space, 0, ~m_port_ddr[offset] & m_port_bits[offset]) & m_port_bits[offset]);
				u8 const diff(newval ^ m_port_input[offset]);
				if (diff)
				{
					LOGIOPORT("read PORT%c: new input = %02X & %02X (was %02X)\n",
							char('A' + offset), newval, ~m_port_ddr[offset] & m_port_bits[offset], m_port_input[offset]);
				}
				m_port_input[offset] = newval;
			}
			update_port_irq();
		}
		m_port_cb_w[offset](space, 0, port_value(offset), m_port_ddr[offset]);
	}
}

u8 m68hc05_device::spcr_spr_divider() const
{
	static const uint32_t s_rate_values[4] = { 2, 4, 16, 32 };
	return s_rate_values[spcr_spr()];
}

READ8_MEMBER(m68hc05_device::spcr_r)
{
	LOGSPI("%s: read SPCR: %02x\n", machine().describe_context(), m_spcr);
	return m_spcr;
}

void m68hc05_device::check_spi_interrupts()
{
	if (spcr_spie() && (m_spsr & 0x90))
	{
		m_pending_interrupts |= M68HC05_INT_SPI;
	}
	else
	{
		m_pending_interrupts &= ~M68HC05_INT_SPI;
	}
}

WRITE8_MEMBER(m68hc05_device::spcr_w)
{
	if (spsr_modf())
	{
		m_spsr &= ~0x10;
		check_spi_interrupts();
	}
	data &= 0xdf;
	m_spcr = data;
	LOGSPI("%s: write SPCR: SPIE=%u SPE=%u MSTR=%u CPOL=%u CPHA=%u SPR=%u (divider %u)\n", machine().describe_context(),
		spcr_spie(), spcr_spe(), spcr_mstr(), spcr_cpol(), spcr_cpha(), spcr_spr(), spcr_spr_divider());
	if (m_spi_tx_clocks == ~0U)
	{
		m_sck = spcr_cpol() ? 1 : 0;
	}
}

READ8_MEMBER(m68hc05_device::spsr_r)
{
	//LOGSPI("%s: read SPSR: %02x\n", machine().describe_context(), m_spsr);
	return m_spsr;
}

WRITE8_MEMBER(m68hc05_device::spsr_w)
{
	LOGSPI("write SPSR (ignored): %02x\n", data);
}

READ8_MEMBER(m68hc05_device::spdr_r)
{
	LOGSPI("%s: read SPDR: %02x\n", machine().describe_context(), m_sprr);
	m_spsr &= ~0xd0;
	check_spi_interrupts();
	return m_sprr;
}

WRITE8_MEMBER(m68hc05_device::spdr_w)
{
	LOGSPI("%s: write SPDR: %02x\n", machine().describe_context(), data);
	if (spcr_mstr() && spsr_spif())
	{
		LOGSPI("%s: SPDR write inhibited due to MSTR and SPIF both set\n", machine().describe_context());
		return;
	}
	m_spdr = data;
	if (spsr_spif())
	{
		m_spsr &= ~0x80;
		check_spi_interrupts();
	}
	if (spcr_spe())
	{
		if (m_spi_tx_clocks != ~0U)
		{
			// flag write collision
			m_spsr |= 0x40;
		}
		else
		{
			m_spi_tx_clocks = spcr_spr_divider() >> 1;
			m_spi_tx_cnt = 8;
			m_spi_run_clocks = 0;
		}
	}
}

WRITE_LINE_MEMBER(m68hc05_device::sck_in)
{
	if (!spcr_mstr() && m_ss)
	{
		LOGSPI("sck_in (ignored due to high SS and non-master): %d\n", state);
		return;
	}
	LOGSPI("sck_in: %d\n", state);
	u8 old = m_sck;
	m_sck = state;
	if (spcr_spe())
	{
		if (old != m_sck && m_sck != spcr_cpol())
		{
			if (m_spi_rx_cnt == 0)
			{
				m_spdr = 0;
			}
			m_spdr |= (m_sda ? 1 : 0) << m_spi_rx_cnt;
			m_spi_rx_cnt++;
			if (m_spi_rx_cnt == 8)
			{
				m_sprr = m_spdr;
				m_spi_rx_cnt = 0;
				m_spsr |= 0x80;
				check_spi_interrupts();
			}
		}
	}
}

WRITE_LINE_MEMBER(m68hc05_device::sda_in)
{
	if (!spcr_mstr() && m_ss)
	{
		LOGSPI("sda_in (ignored due to high SS and non-master): %d\n", state);
		return;
	}
	LOGSPI("sda_in: %d\n", state);
	m_sda = state;
}

WRITE_LINE_MEMBER(m68hc05_device::ss_in)
{
	LOGSPI("ss_in: %d\n", state);
	u8 old = m_ss;
	m_ss = state;
	if (old != m_ss)
	{
		if (spcr_mstr() && state)
		{
			m_spsr |= 0x10;
			m_spcr &= ~0x50;
			check_spi_interrupts();
		}
	}
}

u8 m68hc05_device::baud_scp_count() const
{
	static const uint32_t s_prescale_values[4] = { 1, 3, 4, 13 };
	return s_prescale_values[baud_scp()];
}

READ8_MEMBER(m68hc05_device::baud_r)
{
	return m_baud;
}

WRITE8_MEMBER(m68hc05_device::baud_w)
{
	data &= 0x37;
	m_baud = data;
	LOGUART("write BAUD: SCP=%u SCR=%u (prescale=%u clocks, rate=%u clocks)\n",
		baud_scp(), baud_scr(), baud_scp_count(), baud_scr_count());
}

READ8_MEMBER(m68hc05_device::sccr1_r)
{
	return m_sccr1;
}

WRITE8_MEMBER(m68hc05_device::sccr1_w)
{
	data &= 0xd8;
	m_sccr1 = data;
	LOGUART("write SCCR1: R8=%u T8=%u M=%u WAKE=%u\n",
		sccr1_r8(), sccr1_t8(), sccr1_m(), sccr1_wake());
}

READ8_MEMBER(m68hc05_device::sccr2_r)
{
	return m_sccr2;
}

void m68hc05_device::check_sci_interrupts()
{
	if (m_scsr & m_sccr2 & 0xf0)
	{
		LOGUART("SCI active because SCSR %02x & SCCR2 %02x & 0xf0 != 0\n", m_scsr, m_sccr2);
		m_pending_interrupts |= M68HC05_INT_SCI;
	}
	else
		m_pending_interrupts &= ~M68HC05_INT_SCI;
}

WRITE8_MEMBER(m68hc05_device::sccr2_w)
{
	m_sccr2 = data;
	LOGUART("write SCCR2: TIE=%u TCIE=%u RIE=%u ILIE=%u TE=%u RE=%u RWU=%u SBK=%u\n",
		sccr2_tie(), sccr2_tcie(), sccr2_rie(), sccr2_ilie(), sccr2_te(), sccr2_re(), sccr2_rwu(), sccr2_sbk());
	if (sccr2_te() && m_tdr_pending)
	{
		m_uart_tx_clocks = 10 * 32 * baud_scp_count() * baud_scr_count();
	}
	if (sccr2_re() && m_rdr_pending)
	{
		m_uart_rx_clocks = 10 * 32 * baud_scp_count() * baud_scr_count();
	}
	check_sci_interrupts();
}

READ8_MEMBER(m68hc05_device::scsr_r)
{
	return m_scsr;
}

READ8_MEMBER(m68hc05_device::rdr_r)
{
	m_scsr &= ~0x20;
	check_sci_interrupts();
	LOGUART("%s: read RDR: %02x\n", machine().describe_context(), m_rdr);
	return m_rdr;
}

void m68hc05_device::uart_rx(u8 data)
{
	m_rdr = data;
	if (sccr2_re())
	{
		m_uart_rx_clocks = 32 * baud_scp_count() * baud_scr_count();
	}
	else
	{
		m_rdr_pending = true;
	}
}

WRITE8_MEMBER(m68hc05_device::tdr_w)
{
	m_tdr = data;
	LOGUART("write TDR: %02x\n", data);
	m_scsr &= ~0xc0;
	check_sci_interrupts();
	if (sccr2_te())
	{
		m_uart_tx_clocks = 32 * baud_scp_count() * baud_scr_count();
	}
	else
	{
		m_tdr_pending = true;
	}
}

READ8_MEMBER(m68hc05_device::tcr_r)
{
	return m_tcr;
}

WRITE8_MEMBER(m68hc05_device::tcr_w)
{
	data &= 0xe3;
	LOGTIMER("write TCR: ICIE=%u OCIE=%u TOIE=%u IEDG=%u OLVL=%u\n",
			BIT(data, 7), BIT(data, 6), BIT(data, 5), BIT(data, 1), BIT(data, 0));
	m_tcr = data;
	if (m_tcr & m_tsr & 0xe0)
		m_pending_interrupts |= M68HC05_INT_TIMER;
	else
		m_pending_interrupts &= ~M68HC05_INT_TIMER;
}

READ8_MEMBER(m68hc05_device::tsr_r)
{
	if (!machine().side_effects_disabled())
	{
		u8 const events(m_tsr & ~m_tsr_seen);
		if (events)
		{
			LOGTIMER("read TSR: seen%s%s%s\n",
					BIT(events, 7) ? " ICF" : "", BIT(events, 6) ? " OCF" : "", BIT(events, 5) ? " TOF" : "");
		}
		m_tsr_seen = m_tsr;
	}
	return m_tsr;
}

READ8_MEMBER(m68hc05_device::icr_r)
{
	// reading IRCH inhibits capture until ICRL is read
	// reading ICRL after reading TCR with ICF set clears ICF

	u8 const low(BIT(offset, 0));
	if (!machine().side_effects_disabled())
	{
		if (low)
		{
			if (BIT(m_tsr_seen, 7))
			{
				LOGTIMER("read ICRL, clear ICF\n");
				m_tsr &= 0x7f;
				m_tsr_seen &= 0x7f;
				if (!(m_tcr & m_tsr & 0xe0)) m_pending_interrupts &= ~M68HC05_INT_TIMER;
			}
			if (m_inhibit_cap) LOGTIMER("read ICRL, enable capture\n");
			m_inhibit_cap = false;
		}
		else
		{
			if (!m_inhibit_cap) LOGTIMER("read ICRH, inhibit capture\n");
			m_inhibit_cap = true;
		}
	}
	return u8(m_icr >> (low ? 0 : 8));
}

READ8_MEMBER(m68hc05_device::ocr_r)
{
	// reading OCRL after reading TCR with OCF set clears OCF

	u8 const low(BIT(offset, 0));
	if (!machine().side_effects_disabled() && low && BIT(m_tsr_seen, 6))
	{
		LOGTIMER("read OCRL, clear OCF\n");
		m_tsr &= 0xbf;
		m_tsr_seen &= 0xbf;
		if (!(m_tcr & m_tsr & 0xe0)) m_pending_interrupts &= ~M68HC05_INT_TIMER;
	}
	return u8(m_ocr >> (low ? 0 : 8));
}

WRITE8_MEMBER(m68hc05_device::ocr_w)
{
	// writing ORCH inhibits compare until OCRL is written
	// writing OCRL after reading TCR with OCF set clears OCF

	u8 const low(BIT(offset, 0));
	if (!machine().side_effects_disabled())
	{
		if (low)
		{
			if (BIT(m_tsr_seen, 6))
			{
				LOGTIMER("write OCRL, clear OCF\n");
				m_tsr &= 0xbf;
				m_tsr_seen &= 0xbf;
				if (!(m_tcr & m_tsr & 0xe0)) m_pending_interrupts &= ~M68HC05_INT_TIMER;
			}
			if (m_inhibit_cmp) LOGTIMER("write OCRL, enable compare\n");
			m_inhibit_cmp = false;
		}
		else
		{
			if (!m_inhibit_cmp) LOGTIMER("write OCRH, inhibit compare\n");
			m_inhibit_cmp = true;
		}
	}
	m_ocr = (m_ocr & (low ? 0xff00 : 0x00ff)) | (u16(data) << (low ? 0 : 8));
}

READ8_MEMBER(m68hc05_device::timer_r)
{
	// reading [A]TRH returns current counter MSB and latches [A]TRL buffer
	// reading [A]TRL returns current [A]TRL buffer and completes read sequence
	// reading TRL after reading TSR with TOF set clears TOF
	// reading ATRL doesn't affect TOF

	u8 const low(BIT(offset, 0));
	u8 const alt(BIT(offset, 1));
	if (low)
	{
		if (!machine().side_effects_disabled())
		{
			if (m_trl_latched[alt]) LOGTIMER("read %sTRL, read sequence complete\n", alt ? "A" : "");
			m_trl_latched[alt] = false;
			if (!alt && BIT(m_tsr_seen, 5))
			{
				LOGTIMER("read TRL, clear TOF\n");
				m_tsr &= 0xdf;
				m_tsr_seen &= 0xdf;
				if (!(m_tcr & m_tsr & 0xe0)) m_pending_interrupts &= ~M68HC05_INT_TIMER;
			}
		}
		return m_trl_buf[alt];
	}
	else
	{
		if (!machine().side_effects_disabled() && !m_trl_latched[alt])
		{
			LOGTIMER("read %sTRH, latch %sTRL\n", alt ? "A" : "", alt ? "A" : "");
			m_trl_latched[alt] = true;
			m_trl_buf[alt] = u8(m_counter);
		}
		return u8(m_counter >> 8);
	}
}


WRITE8_MEMBER(m68hc705_device::coprst_w)
{
	LOGCOP("write COPRST=%02x%s\n", data, ((0xaa == data) && (0x55 == m_coprst)) ? ", reset" : "");
	if (0x55 == data)
	{
		m_coprst = data;
	}
	else if (0xaa == data)
	{
		if (0x55 == m_coprst) m_pcop_cnt &= 0x00007fff;
		m_coprst = data;
	}
}

READ8_MEMBER(m68hc705_device::copcr_r)
{
	if (copcr_copf()) LOGCOP("read COPCR, clear COPF\n");
	u8 const result(m_copcr);
	m_copcr &= 0xef;
	return result;
}

WRITE8_MEMBER(m68hc705_device::copcr_w)
{
	LOGCOP("write COPCR: CME=%u PCOPE=%u [%s] CM=%u\n",
			BIT(data, 3), BIT(data, 2), (!copcr_pcope() && BIT(data, 2)) ? "set" : "ignored", data & 0x03);
	m_copcr = (m_copcr & 0xf4) | (data & 0x0f); // PCOPE is set-only, hence the mask overlap
}

WRITE8_MEMBER(m68hc705_device::copr_w)
{
	LOGCOP("write COPR: COPC=%u\n", BIT(data, 0));
	if (!BIT(data, 0)) m_ncop_cnt = 0;
}


void m68hc05_device::device_start()
{
	m6805_base_device::device_start();

	// resolve callbacks
	for (devcb_read8 &cb : m_port_cb_r) cb.resolve();
	for (devcb_write8 &cb : m_port_cb_w) cb.resolve_safe();
	m_uart_tx_cb.resolve_safe();
	m_sck_out_cb.resolve_safe();
	m_sda_out_cb.resolve_safe();
	m_tcmp_cb.resolve_safe();

	// save digital I/O
	save_item(NAME(m_port_interrupt));
	save_item(NAME(m_port_input));
	save_item(NAME(m_port_latch));
	save_item(NAME(m_port_ddr));
	save_item(NAME(m_port_irq_state));
	save_item(NAME(m_irq_line_state));
	save_item(NAME(m_irq_latch));

	// save UART
	save_item(NAME(m_baud));
	save_item(NAME(m_sccr1));
	save_item(NAME(m_sccr2));
	save_item(NAME(m_scsr));
	save_item(NAME(m_rdr));
	save_item(NAME(m_tdr));
	save_item(NAME(m_rdr_pending));
	save_item(NAME(m_tdr_pending));
	save_item(NAME(m_uart_tx_clocks));
	save_item(NAME(m_uart_rx_clocks));

	// save SPI
	save_item(NAME(m_spcr));
	save_item(NAME(m_spsr));
	save_item(NAME(m_spdr));
	save_item(NAME(m_sprr));
	save_item(NAME(m_spi_rx_cnt));
	save_item(NAME(m_spi_tx_cnt));
	save_item(NAME(m_spi_tx_clocks));
	save_item(NAME(m_spi_run_clocks));
	save_item(NAME(m_sck));
	save_item(NAME(m_sda));
	save_item(NAME(m_ss));

	// save timer/counter
	save_item(NAME(m_tcap_state));
	save_item(NAME(m_tcr));
	save_item(NAME(m_tsr));
	save_item(NAME(m_tsr_seen));
	save_item(NAME(m_prescaler));
	save_item(NAME(m_counter));
	save_item(NAME(m_icr));
	save_item(NAME(m_ocr));
	save_item(NAME(m_inhibit_cap));;
	save_item(NAME(m_inhibit_cmp));
	save_item(NAME(m_trl_buf));
	save_item(NAME(m_trl_latched));

	// digital I/O state unaffected by reset
	std::fill(std::begin(m_port_interrupt), std::end(m_port_interrupt), 0x00);
	std::fill(std::begin(m_port_input), std::end(m_port_input), 0xff);
	std::fill(std::begin(m_port_latch), std::end(m_port_latch), 0xff);
	m_irq_line_state = false;

	// some UART state unaffected by reset
	m_baud = 0x00;
	m_sccr1 = 0x00;
	m_sccr2 = 0x00;
	m_scsr = 0x00;
	m_rdr = 0x00;
	m_tdr = 0x00;
	m_rdr_pending = false;
	m_tdr_pending = false;
	m_uart_tx_clocks = ~0U;
	m_uart_rx_clocks = ~0U;

	// some SPI sate unaffected by reset
	m_spcr = 0x00;
	m_spsr = 0x00;
	m_spdr = 0x00;
	m_sprr = 0x00;
	m_spi_rx_cnt = 0;
	m_spi_tx_cnt = 0;
	m_spi_tx_clocks = ~0U;
	m_spi_run_clocks = 0;
	m_sck = 0x00;
	m_sda = 0x00;
	m_ss = 0x01;

	// timer state unaffected by reset
	m_tcap_state = false;
	m_tcr = 0x00;
	m_tsr = 0x00;
	m_icr = 0x0000;
	m_ocr = 0x0000;

	// expose most basic state to debugger
	state_add(M68HC05_IRQLATCH, "IRQLATCH", m_irq_latch).mask(0x01);
}

void m68hc05_device::device_reset()
{
	m6805_base_device::device_reset();

	// digital I/O reset
	std::fill(std::begin(m_port_ddr), std::end(m_port_ddr), 0x00);
	m_irq_latch = 0;
	update_port_irq();

	// UART reset
	m_baud &= 0x07;
	m_sccr2 = 0x00;
	m_scsr = 0xc0;
	m_uart_tx_clocks = ~0U;
	m_uart_rx_clocks = ~0U;

	// SPI reset
	m_spcr &= 0x2f;
	m_spsr &= 0x2f;
	m_spi_rx_cnt = 0;
	m_spi_tx_cnt = 0;
	m_spi_tx_clocks = ~0U;
	m_spi_run_clocks = 0;
	m_sck = 0x00;
	m_sda = 0x00;
	m_ss = 0x01;

	// timer reset
	m_tcr &= 0x02;
	m_tsr_seen = 0x00;
	m_prescaler = 0;
	m_counter = 0xfffc;
	m_inhibit_cap = m_inhibit_cmp = false;
	m_trl_buf[0] = m_trl_buf[1] = u8(m_counter);
	m_trl_latched[0] = m_trl_latched[1] = false;
}

void m68hc705_device::device_start()
{
	m68hc05_device::device_start();

	// save COP watchdogs
	save_item(NAME(m_pcop_cnt));
	save_item(NAME(m_ncop_cnt));
	save_item(NAME(m_coprst));
	save_item(NAME(m_copcr));
	save_item(NAME(m_ncope));

	// COP watchdog state unaffected by reset
	m_pcop_cnt = 0;
	m_coprst = 0x00;
	m_copcr = 0x00;
	m_ncope = 0;
}

void m68hc705_device::device_reset()
{
	m68hc05_device::device_reset();

	// COP watchdog reset
	m_ncop_cnt = 0;
	m_copcr &= 0x10;
}


void m68hc05_device::execute_set_input(int inputnum, int state)
{
	switch (inputnum)
	{
	case M68HC05_IRQ_LINE:
		if ((CLEAR_LINE != state) && !m_irq_line_state)
		{
			LOGINT("/IRQ edge%s\n", (m_port_irq_state || m_irq_latch) ? "" : ", set IRQ latch");
			if (!m_port_irq_state)
			{
				m_irq_latch = 1;
				m_pending_interrupts |= M68HC05_INT_IRQ;
			}
		}
		m_irq_line_state = ASSERT_LINE == state;
		break;
	case M68HC05_TCAP_LINE:
		if ((bool(state) != m_tcap_state) && (bool(state) == tcr_iedg()))
		{
			LOGTIMER("input capture %04X%s\n", m_counter, m_inhibit_cap ? " [inhibited]" : "");
			if (!m_inhibit_cap)
			{
				m_tsr |= 0x80;
				m_icr = m_counter;
				if (m_tcr & m_tsr & 0xe0) m_pending_interrupts |= M68HC05_INT_TIMER;
			}
		}
		m_tcap_state = bool(state);
		break;
	default:
		fatalerror("m68hc05[%s]: unknown input line %d", tag(), inputnum);
	}
}

u64 m68hc05_device::execute_clocks_to_cycles(u64 clocks) const noexcept
{
	return (clocks + 1) / 2;
}

u64 m68hc05_device::execute_cycles_to_clocks(u64 cycles) const noexcept
{
	return cycles * 2;
}

std::unique_ptr<util::disasm_interface> m68hc05_device::create_disassembler()
{
	return std::make_unique<m68hc05_disassembler>();
}


void m68hc05_device::interrupt()
{
	if ((m_pending_interrupts & M68HC05_INT_MASK) && !(CC & IFLAG))
	{
		pushword(m_pc);
		pushbyte(m_x);
		pushbyte(m_a);
		pushbyte(m_cc);
		SEI;
		standard_irq_callback(0);

		if (m_pending_interrupts & M68HC05_INT_IRQ)
		{
			LOGINT("servicing external interrupt\n");
			m_irq_latch = 0;
			m_pending_interrupts &= ~M68HC05_INT_IRQ;
			rm16(M68HC05_VECTOR_IRQ & m_params.m_vector_mask, m_pc);
		}
		else if (m_pending_interrupts & M68HC05_INT_TIMER)
		{
			LOGINT("servicing timer interrupt\n");
			rm16(M68HC05_VECTOR_TIMER & m_params.m_vector_mask, m_pc);
		}
		else if (m_pending_interrupts & M68HC05_INT_SCI)
		{
			LOGINT("servicing SCI interrupt\n");
			rm16(M68HC05_VECTOR_SCI & m_params.m_vector_mask, m_pc);
		}
		else if (m_pending_interrupts & M68HC05_INT_SPI)
		{
			LOGINT("servicing SPI interrupt\n");
			rm16(M68HC05_VECTOR_SPI & m_params.m_vector_mask, m_pc);
		}
		else
		{
			fatalerror("m68hc05[%s]: unknown pending interrupt(s) %x", tag(), m_pending_interrupts);
		}
		m_icount -= 10;
		burn_cycles(10);
	}
}

bool m68hc05_device::test_il()
{
	return m_irq_line_state;
}

void m68hc05_device::run_cop(unsigned count)
{
	// C4 and C8 devices don't have a COP or watchdog
}

void m68hc705_device::run_cop(unsigned count)
{
	// run programmable COP
	u32 const pcop_timeout(u32(1) << ((copcr_cm() << 1) + 15));
	if (copcr_pcope() && (pcop_timeout <= ((m_pcop_cnt & (pcop_timeout - 1)) + count)))
	{
		LOGCOP("PCOP reset\n");
		m_copcr |= 0x10;
		pulse_input_line(INPUT_LINE_RESET, attotime::zero);
	}
	m_pcop_cnt = (m_pcop_cnt + count) & ((u32(1) << 21) - 1);

	// run non-programmable COP
	m_ncop_cnt += count;
	if ((u32(1) << 17) <= m_ncop_cnt)
	{
		pulse_input_line(INPUT_LINE_RESET, attotime::zero);
		LOGCOP("NCOP reset\n");
	}
	m_ncop_cnt &= (u32(1) << 17) - 1;
}

void m68hc05_device::burn_cycles(unsigned count)
{
	// calculate new timer values (fixed prescaler of four)
	unsigned const ps_opt(4);
	unsigned const ps_mask((1 << ps_opt) - 1);
	unsigned const increments((count + (m_prescaler & ps_mask)) >> ps_opt);
	u32 const new_counter(u32(m_counter) + increments);
	bool const timer_rollover((0x010000 > m_counter) && (0x010000 <= new_counter));
	bool const output_compare_match((m_ocr > m_counter) && (m_ocr <= new_counter));
	m_prescaler = (count + m_prescaler) & ps_mask;
	m_counter = u16(new_counter);
	if (timer_rollover)
	{
		LOGTIMER("timer rollover\n");
		m_tsr |= 0x20;
	}
	if (output_compare_match)
	{
		LOGTIMER("output compare match %s\n", m_inhibit_cmp ? " [inhibited]" : "");
		if (!m_inhibit_cmp)
		{
			m_tsr |= 0x40;
			m_tcmp_cb(tcr_olvl() ? 1 : 0);
		}
	}
	if (m_tcr & m_tsr & 0xe0) m_pending_interrupts |= M68HC05_INT_TIMER;

	run_cop(count);

	// run SPI Tx
	if (m_spi_tx_clocks != ~0U)
	{
		m_spi_run_clocks += count << ps_opt;
		while (m_spi_run_clocks >= m_spi_tx_clocks && m_spi_tx_cnt > 0)
		{
			m_spi_run_clocks -= m_spi_tx_clocks;

			if (m_sck == !spcr_cpol())
			{
				LOGSPI("Transmitting SPI bit %u: %u\n", 8 - m_spi_tx_cnt, BIT(m_spdr, 8 - m_spi_tx_cnt));
				m_sda_out_cb(BIT(m_spdr, 8 - m_spi_tx_cnt));
				m_spi_tx_cnt--;
				if (m_spi_tx_cnt == 0)
				{
					m_spi_tx_clocks = ~0U;
					m_spi_run_clocks = 0;
					m_spsr |= 0x80;
					check_spi_interrupts();
				}
			}
			m_sck_out_cb(m_sck);
			m_sck = 1 - m_sck;
		}
	}

	// run UART Tx
	if (m_uart_tx_clocks != ~0U)
	{
		if (m_uart_tx_clocks > count)
		{
			m_uart_tx_clocks -= count;
		}
		else
		{
			LOGUART("Transmitting %02x\n", m_tdr);
			m_tdr_pending = false;
			m_uart_tx_clocks = ~0U;
			m_scsr |= 0xc0;
			m_uart_tx_cb(m_tdr);
			check_sci_interrupts();
		}
	}

	// run UART Rx
	if (m_uart_rx_clocks != ~0U && sccr2_re())
	{
		if (m_uart_rx_clocks > count)
		{
			m_uart_rx_clocks -= count;
		}
		else
		{
			LOGUART("Receiving %02x\n", m_rdr);
			m_rdr_pending = false;
			m_uart_rx_clocks = ~0U;
			m_scsr |= 0x20;
			check_sci_interrupts();
		}
	}
}


void m68hc05_device::add_port_state(std::array<bool, PORT_COUNT> const &ddr)
{
	for (unsigned i = 0; PORT_COUNT > i; ++i)
	{
		if (m_port_bits[i])
			state_add(M68HC05_LATCHA + i, util::string_format("LATCH%c", 'A' + i).c_str(), m_port_latch[i]).mask(m_port_bits[i]);
	}
	for (unsigned i = 0; PORT_COUNT > i; ++i)
	{
		if (ddr[i] && m_port_bits[i])
			state_add(M68HC05_DDRA + i, util::string_format("DDR%c", 'A' + i).c_str(), m_port_ddr[i]).mask(m_port_bits[i]);
	}
}

void m68hc05_device::add_timer_state()
{
	state_add(M68HC05_TCR, "TCR", m_tcr).mask(0x7f);
	state_add(M68HC05_TSR, "TSR", m_tsr).mask(0xff);
	state_add(M68HC05_ICR, "ICR", m_icr).mask(0xffff);
	state_add(M68HC05_OCR, "OCR", m_ocr).mask(0xffff);
	state_add(M68HC05_PS, "PS", m_prescaler).mask(0x03);
	state_add(M68HC05_TR, "TR", m_counter).mask(0xffff);
}

void m68hc705_device::add_pcop_state()
{
	state_add(M68HC05_COPRST, "COPRST", m_coprst).mask(0xff);
	state_add(M68HC05_COPCR, "COPCR", m_copcr).mask(0x1f);
	state_add(M68HC05_PCOP, "PCOP", m_pcop_cnt).mask(0x001fffff);
}

void m68hc705_device::add_ncop_state()
{
	state_add(M68HC05_NCOPE, "NCOPE", m_ncope).mask(0x01);
	state_add(M68HC05_NCOP, "NCOP", m_ncop_cnt).mask(0x0001ffff);
}


u8 m68hc05_device::port_value(unsigned offset) const
{
	return (m_port_latch[offset] & m_port_ddr[offset]) | (m_port_input[offset] & ~m_port_ddr[offset]);
}

void m68hc05_device::update_port_irq()
{
	u8 state(0x00);
	for (unsigned i = 0; i < PORT_COUNT; ++i)
		state |= m_port_interrupt[i] & ~m_port_ddr[i] & ~m_port_input[i];

	if (bool(state) != m_port_irq_state)
	{
		LOGINT("I/O port IRQ state now %u%s\n",
				state ? 1 : 0, (!m_irq_line_state && state && !m_irq_latch) ? ", set IRQ latch" : "");
		m_port_irq_state = bool(state);
		if (!m_irq_line_state && state)
		{
			m_irq_latch = 1;
			m_pending_interrupts |= M68HC05_INT_IRQ;
		}
	}
}



/****************************************************************************
 * M68HC705 base device
 ****************************************************************************/

m68hc705_device::m68hc705_device(
		machine_config const &mconfig,
		char const *tag,
		device_t *owner,
		u32 clock,
		device_type type,
		u32 addr_width,
		address_map_constructor internal_map)
	: m68hc05_device(mconfig, tag, owner, clock, type, addr_width, (1U << addr_width) - 1, internal_map)
	, m_pcop_cnt(0)
	, m_ncop_cnt(0)
	, m_coprst(0x00)
	, m_copcr(0x00)
	, m_ncope(0)
{
}



/****************************************************************************
 * MC68HC05C4 device
 ****************************************************************************/

void m68hc05c4_device::c4_map(address_map &map)
{
	map.global_mask(0x1fff);
	map.unmap_value_high();

	map(0x0000, 0x0003).rw(FUNC(m68hc05c4_device::port_read), FUNC(m68hc05c4_device::port_latch_w));
	map(0x0004, 0x0006).rw(FUNC(m68hc05c4_device::port_ddr_r), FUNC(m68hc05c4_device::port_ddr_w));
	// 0x0007-0x0009 unused
	map(0x000a, 0x000a).rw(FUNC(m68hc05c4_device::spcr_r), FUNC(m68hc05c4_device::spcr_w));
	map(0x000b, 0x000b).rw(FUNC(m68hc05c4_device::spsr_r), FUNC(m68hc05c4_device::spsr_w));
	map(0x000c, 0x000c).rw(FUNC(m68hc05c4_device::spdr_r), FUNC(m68hc05c4_device::spdr_w));
	map(0x000d, 0x000d).rw(FUNC(m68hc05c4_device::baud_r), FUNC(m68hc05c4_device::baud_w));
	map(0x000e, 0x000e).rw(FUNC(m68hc05c4_device::sccr1_r), FUNC(m68hc05c4_device::sccr1_w));
	map(0x000f, 0x000f).rw(FUNC(m68hc05c4_device::sccr2_r), FUNC(m68hc05c4_device::sccr2_w));
	map(0x0010, 0x0010).r(FUNC(m68hc05c4_device::scsr_r));
	map(0x0011, 0x0011).rw(FUNC(m68hc05c4_device::rdr_r), FUNC(m68hc05c4_device::tdr_w));
	map(0x0012, 0x0012).rw(FUNC(m68hc05c4_device::tcr_r), FUNC(m68hc05c4_device::tcr_w));
	map(0x0013, 0x0013).r(FUNC(m68hc05c4_device::tsr_r));
	map(0x0014, 0x0015).r(FUNC(m68hc05c4_device::icr_r));
	map(0x0016, 0x0017).rw(FUNC(m68hc05c4_device::ocr_r), FUNC(m68hc05c4_device::ocr_w));
	map(0x0018, 0x001b).r(FUNC(m68hc05c4_device::timer_r));
	// 0x001c-0x001f unused
	map(0x0020, 0x004f).rom(); // user ROM
	map(0x0050, 0x00ff).ram(); // RAM/stack
	map(0x0100, 0x10ff).rom(); // user ROM
	// 0x1100-0x1eff unused
	map(0x1f00, 0x1fef).rom(); // self-check
	// 0x1ff0-0x1ff3 unused
	map(0x1ff4, 0x1fff).rom(); // user vectors
}


m68hc05c4_device::m68hc05c4_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
	: m68hc05_device(
			mconfig,
			tag,
			owner,
			clock,
			M68HC05C4,
			13,
			0x1fff,
			address_map_constructor(FUNC(m68hc05c4_device::c4_map), this))
{
	set_port_bits(std::array<u8, PORT_COUNT>{{ 0xff, 0xff, 0xff, 0xbf }});
}



void m68hc05c4_device::device_start()
{
	m68hc05_device::device_start();

	add_port_state(std::array<bool, PORT_COUNT>{{ true, true, true, false }});
	add_timer_state();
}


std::unique_ptr<util::disasm_interface> m68hc05c4_device::create_disassembler()
{
	return std::make_unique<m68hc05_disassembler>(m68hc05c4_syms);
}



/****************************************************************************
 * MC68HC05C8 device
 ****************************************************************************/

void m68hc05c8_device::c8_map(address_map &map)
{
	map.global_mask(0x1fff);
	map.unmap_value_high();

	map(0x0000, 0x0003).rw(FUNC(m68hc05c8_device::port_read), FUNC(m68hc05c8_device::port_latch_w));
	map(0x0004, 0x0006).rw(FUNC(m68hc05c8_device::port_ddr_r), FUNC(m68hc05c8_device::port_ddr_w));
	// 0x0007-0x0009 unused
	map(0x000a, 0x000a).rw(FUNC(m68hc05c8_device::spcr_r), FUNC(m68hc05c8_device::spcr_w));
	map(0x000b, 0x000b).rw(FUNC(m68hc05c8_device::spsr_r), FUNC(m68hc05c8_device::spsr_w));
	map(0x000c, 0x000c).rw(FUNC(m68hc05c8_device::spdr_r), FUNC(m68hc05c8_device::spdr_w));
	map(0x000d, 0x000d).rw(FUNC(m68hc05c8_device::baud_r), FUNC(m68hc05c8_device::baud_w));
	map(0x000e, 0x000e).rw(FUNC(m68hc05c8_device::sccr1_r), FUNC(m68hc05c8_device::sccr1_w));
	map(0x000f, 0x000f).rw(FUNC(m68hc05c8_device::sccr2_r), FUNC(m68hc05c8_device::sccr2_w));
	map(0x0010, 0x0010).r(FUNC(m68hc05c8_device::scsr_r));
	map(0x0011, 0x0011).rw(FUNC(m68hc05c8_device::rdr_r), FUNC(m68hc05c8_device::tdr_w));
	map(0x0012, 0x0012).rw(FUNC(m68hc05c8_device::tcr_r), FUNC(m68hc05c8_device::tcr_w));
	map(0x0013, 0x0013).r(FUNC(m68hc05c8_device::tsr_r));
	map(0x0014, 0x0015).r(FUNC(m68hc05c8_device::icr_r));
	map(0x0016, 0x0017).rw(FUNC(m68hc05c8_device::ocr_r), FUNC(m68hc05c8_device::ocr_w));
	map(0x0018, 0x001b).r(FUNC(m68hc05c8_device::timer_r));
	// 0x001c-0x001f unused
	map(0x0020, 0x004f).rom(); // user ROM
	map(0x0050, 0x00ff).ram(); // RAM/stack
	map(0x0100, 0x1eff).rom(); // user ROM
	map(0x1f00, 0x1fef).rom(); // self-check
	// 0x1ff0-0x1ff3 unused
	map(0x1ff4, 0x1fff).rom(); // user vectors
}


m68hc05c8_device::m68hc05c8_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
	: m68hc05_device(
			mconfig,
			tag,
			owner,
			clock,
			M68HC05C8,
			13,
			0x1fff,
			address_map_constructor(FUNC(m68hc05c8_device::c8_map), this))
{
	set_port_bits(std::array<u8, PORT_COUNT>{{ 0xff, 0xff, 0xff, 0xbf }});
}


void m68hc05c8_device::device_start()
{
	m68hc05_device::device_start();

	add_port_state(std::array<bool, PORT_COUNT>{{ true, true, true, false }});
	add_timer_state();
}


std::unique_ptr<util::disasm_interface> m68hc05c8_device::create_disassembler()
{
	// same I/O registers as MC68HC05C4
	return std::make_unique<m68hc05_disassembler>(m68hc05c4_syms);
}



/****************************************************************************
 * MC68HC705C8A device
 ****************************************************************************/

void m68hc705c8a_device::c8a_map(address_map &map)
{
	map.global_mask(0x1fff);
	map.unmap_value_high();

	map(0x0000, 0x0003).rw(FUNC(m68hc705c8a_device::port_read), FUNC(m68hc705c8a_device::port_latch_w));
	map(0x0004, 0x0006).rw(FUNC(m68hc705c8a_device::port_ddr_r), FUNC(m68hc705c8a_device::port_ddr_w));
	// 0x0007-0x0009 unused
	map(0x000a, 0x000a).rw(FUNC(m68hc705c8a_device::spcr_r), FUNC(m68hc705c8a_device::spcr_w));
	map(0x000b, 0x000b).rw(FUNC(m68hc705c8a_device::spsr_r), FUNC(m68hc705c8a_device::spsr_w));
	map(0x000c, 0x000c).rw(FUNC(m68hc705c8a_device::spdr_r), FUNC(m68hc705c8a_device::spdr_w));
	map(0x000d, 0x000d).rw(FUNC(m68hc705c8a_device::baud_r), FUNC(m68hc705c8a_device::baud_w));
	map(0x000e, 0x000e).rw(FUNC(m68hc705c8a_device::sccr1_r), FUNC(m68hc705c8a_device::sccr1_w));
	map(0x000f, 0x000f).rw(FUNC(m68hc705c8a_device::sccr2_r), FUNC(m68hc705c8a_device::sccr2_w));
	map(0x0010, 0x0010).r(FUNC(m68hc705c8a_device::scsr_r));
	map(0x0011, 0x0011).rw(FUNC(m68hc705c8a_device::rdr_r), FUNC(m68hc705c8a_device::tdr_w));
	map(0x0012, 0x0012).rw(FUNC(m68hc705c8a_device::tcr_r), FUNC(m68hc705c8a_device::tcr_w));
	map(0x0013, 0x0013).r(FUNC(m68hc705c8a_device::tsr_r));
	map(0x0014, 0x0015).r(FUNC(m68hc705c8a_device::icr_r));
	map(0x0016, 0x0017).rw(FUNC(m68hc705c8a_device::ocr_r), FUNC(m68hc705c8a_device::ocr_w));
	map(0x0018, 0x001b).r(FUNC(m68hc705c8a_device::timer_r));
	// 0x001c PROG
	map(0x001d, 0x001d).w(FUNC(m68hc705c8a_device::coprst_w));
	map(0x001e, 0x001e).rw(FUNC(m68hc705c8a_device::copcr_r), FUNC(m68hc705c8a_device::copcr_w));
	// 0x001f unused
	map(0x0020, 0x004f).rom();                                 // user PROM FIXME: banked with RAM
	map(0x0050, 0x00ff).ram();                                 // RAM/stack
	map(0x0100, 0x015f).rom();                                 // user PROM FIXME: banked with RAM
	map(0x0160, 0x1eff).rom();                                 // user PROM
	map(0x1f00, 0x1fde).rom().region("bootstrap", 0x0000);  // bootloader
	// 0x1fdf option register FIXME: controls banking
	map(0x1fe0, 0x1fef).rom().region("bootstrap", 0x00e0);  // boot ROM vectors
	map(0x1ff0, 0x1ff0).w(FUNC(m68hc705c8a_device::copr_w));
	map(0x1ff0, 0x1fff).rom();                                 // user vectors
}


m68hc705c8a_device::m68hc705c8a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
	: m68hc705_device(
			mconfig,
			tag,
			owner,
			clock,
			M68HC705C8A,
			13,
			address_map_constructor(FUNC(m68hc705c8a_device::c8a_map), this))
{
	set_port_bits(std::array<u8, PORT_COUNT>{{ 0xff, 0xff, 0xff, 0xbf }});
}


tiny_rom_entry const *m68hc705c8a_device::device_rom_region() const
{
	return ROM_NAME(m68hc705c8a);
}


void m68hc705c8a_device::device_start()
{
	m68hc705_device::device_start();

	add_port_state(std::array<bool, PORT_COUNT>{{ true, true, true, false }});
	add_timer_state();
	add_pcop_state();
	add_ncop_state();
}

void m68hc705c8a_device::device_reset()
{
	m68hc705_device::device_reset();

	// latch MOR registers on reset
	set_port_interrupt(std::array<u8, PORT_COUNT>{{ 0x00, u8(rdmem(0xfff0)), 0x00, 0x00 }});
	set_ncope(BIT(rdmem(0xfff1), 0));
}


std::unique_ptr<util::disasm_interface> m68hc705c8a_device::create_disassembler()
{
	return std::make_unique<m68hc05_disassembler>(m68hc705c8a_syms);
}



/****************************************************************************
 * MC68HC05J1A device
 ****************************************************************************/

void m68hc705j1a_device::j1a_map(address_map &map)
{
	map.global_mask(0x07ff);
	map.unmap_value_high();

	map(0x0000, 0x0001).rw(FUNC(m68hc705j1a_device::port_read), FUNC(m68hc705j1a_device::port_latch_w));
	map(0x0004, 0x0005).rw(FUNC(m68hc705j1a_device::port_ddr_r), FUNC(m68hc705j1a_device::port_ddr_w));
	// 0x0008 TSCR (bits 7 and 6 are read-only; bits 3 and 2 are write-only)
	// 0x0009 TCR (read-only)
	// 0x000a ISCR (bits 7 and 3 are readable; bits 7, 4 and 1 are writeable)
	// 0x0010 PDRA (write-only)
	// 0x0011 PDRB (write-only)
	// 0x0014 EPROG
	// 0x001f reserved
	map(0x00c0, 0x00ff).ram();
	map(0x0300, 0x07cf).rom(); // EPROM
	map(0x07ee, 0x07ef).rom(); // test ROM
	map(0x07f0, 0x07f0).w(FUNC(m68hc705j1a_device::copr_w));
	map(0x07f1, 0x07f1).rom(); // MOR
	// 0x07f2-0x07f7 reserved
	map(0x07f8, 0x07ff).rom(); // user vectors
}


m68hc705j1a_device::m68hc705j1a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
	: m68hc705_device(
			mconfig,
			tag,
			owner,
			clock,
			M68HC705J1A,
			11,
			address_map_constructor(FUNC(m68hc705j1a_device::j1a_map), this))
{
	set_port_bits(std::array<u8, PORT_COUNT>{{ 0xff, 0x3f, 0x00, 0x00 }});
}


void m68hc705j1a_device::device_start()
{
	m68hc705_device::device_start();

	add_port_state(std::array<bool, PORT_COUNT>{{ true, true, false, false }});
	add_ncop_state();
}

void m68hc705j1a_device::device_reset()
{
	m68hc705_device::device_reset();

	// latch MOR register on reset
	set_ncope(BIT(rdmem(0x07f1), 0)); // FIXME: this is more like C8A's PCOP
}


std::unique_ptr<util::disasm_interface> m68hc705j1a_device::create_disassembler()
{
	return std::make_unique<m68hc05_disassembler>(m68hc705j1a_syms);
}



/****************************************************************************
 * MC68HC05L9 device
 ****************************************************************************/

void m68hc05l9_device::l9_map(address_map &map)
{
	map.global_mask(0xffff);
	map.unmap_value_high();

	map(0x0000, 0x0003).rw(FUNC(m68hc05l9_device::port_read), FUNC(m68hc05l9_device::port_latch_w));
	map(0x0004, 0x0007).rw(FUNC(m68hc05l9_device::port_ddr_r), FUNC(m68hc05l9_device::port_ddr_w));
	// 0x0008 count down
	// 0x0009-0x000a configuration
	// 0x000b minute alarm
	// 0x000c hour alarm
	map(0x000d, 0x000d).rw(FUNC(m68hc05l9_device::baud_r), FUNC(m68hc05l9_device::baud_w));
	map(0x000e, 0x000e).rw(FUNC(m68hc05l9_device::sccr1_r), FUNC(m68hc05l9_device::sccr1_w));
	map(0x000f, 0x000f).rw(FUNC(m68hc05l9_device::sccr2_r), FUNC(m68hc05l9_device::sccr2_w));
	map(0x0010, 0x0010).r(FUNC(m68hc05l9_device::scsr_r));
	map(0x0011, 0x0011).rw(FUNC(m68hc05l9_device::rdr_r), FUNC(m68hc05l9_device::tdr_w));
	map(0x0012, 0x0012).rw(FUNC(m68hc05l9_device::tcr_r), FUNC(m68hc05l9_device::tcr_w));
	map(0x0013, 0x0013).r(FUNC(m68hc05l9_device::tsr_r));
	map(0x0014, 0x0015).r(FUNC(m68hc05l9_device::icr_r));
	map(0x0016, 0x0017).rw(FUNC(m68hc05l9_device::ocr_r), FUNC(m68hc05l9_device::ocr_w));
	map(0x0018, 0x001b).r(FUNC(m68hc05l9_device::timer_r));
	// 0x001c RTC status and clock control
	// 0x001d hours
	// 0x001e minutes
	// 0x001f seconds
	map(0x0020, 0x004f).rom(); // user ROM
	map(0x0050, 0x00ff).ram(); // RAM/stack
	// 0x0100-0x01ff unused
	map(0x0200, 0x027f).ram(); // display RAM (128x5)
	// 0x0280-0x048f reserved for slaves (528x5)
	// 0x0490-0x07ff unused
	map(0x0800, 0x1e69).rom(); // user ROM
	map(0x1e6a, 0x1fef).rom(); // self-test (vectors at 0x1fe0-0x1fef)
	map(0x1ff0, 0x1fff).rom(); // user vectors
	// 0x2000-0xffff external memory
}


m68hc05l9_device::m68hc05l9_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
	: m68hc05_device(
			mconfig,
			tag,
			owner,
			clock,
			M68HC05L9,
			16,
			0x1fff,
			address_map_constructor(FUNC(m68hc05l9_device::l9_map), this))
{
	set_port_bits(std::array<u8, PORT_COUNT>{{ 0xff, 0xff, 0xff, 0x1f }});
}



void m68hc05l9_device::device_start()
{
	m68hc05_device::device_start();

	add_port_state(std::array<bool, PORT_COUNT>{{ true, true, true, false }});
	add_timer_state();
}


std::unique_ptr<util::disasm_interface> m68hc05l9_device::create_disassembler()
{
	return std::make_unique<m68hc05_disassembler>(m68hc05l9_syms);
}



/****************************************************************************
 * MC68HC05L11 device
 ****************************************************************************/

void m68hc05l11_device::l11_map(address_map &map)
{
	map(0x0000, 0x0003).rw(FUNC(m68hc05l11_device::port_read), FUNC(m68hc05l11_device::port_latch_w));
	// 0x0004 port E
	// 0x0005 port F
	map(0x0006, 0x0008).rw(FUNC(m68hc05l11_device::port_ddr_r), FUNC(m68hc05l11_device::port_ddr_w));
	// 0x0009 port E direction
	// 0x000a port F direction
	// 0x000b minute alarm
	// 0x000c hour alarm
	map(0x000d, 0x000d).rw(FUNC(m68hc05l11_device::baud_r), FUNC(m68hc05l11_device::baud_w));
	map(0x000e, 0x000e).rw(FUNC(m68hc05l11_device::sccr1_r), FUNC(m68hc05l11_device::sccr1_w));
	map(0x000f, 0x000f).rw(FUNC(m68hc05l11_device::sccr2_r), FUNC(m68hc05l11_device::sccr2_w));
	map(0x0010, 0x0010).r(FUNC(m68hc05l11_device::scsr_r));
	map(0x0011, 0x0011).rw(FUNC(m68hc05l11_device::rdr_r), FUNC(m68hc05l11_device::tdr_w));
	map(0x0012, 0x0012).rw(FUNC(m68hc05l11_device::tcr_r), FUNC(m68hc05l11_device::tcr_w));
	map(0x0013, 0x0013).r(FUNC(m68hc05l11_device::tsr_r));
	map(0x0014, 0x0015).r(FUNC(m68hc05l11_device::icr_r));
	map(0x0016, 0x0017).rw(FUNC(m68hc05l11_device::ocr_r), FUNC(m68hc05l11_device::ocr_w));
	map(0x0018, 0x001b).r(FUNC(m68hc05l11_device::timer_r));
	// 0x001c-0x001d output compare 2
	// 0x001e reserved
	// 0x001f RTC interrupt status
	// 0x0020 count down
	// 0x0021 control 1
	// 0x0022 SPCR
	// 0x0023 SPSR
	// 0x0024 SPDR
	// 0x0025 control 2
	// 0x0026 TONEA
	// 0x0027 TONEB
	// 0x0028-0x0033 LCD registers
	// 0x0033 reserved
	// 0x0034-0x003b MMU registers
	// 0x003c hours
	// 0x003d minutes
	// 0x003e seconds
	// 0x003f reserved
	map(0x0040, 0x01ff).ram(); // RAM/stack
	// 0x0200-0x6fff external memory (common area)
	map(0x7000, 0x7dff).rom().region(DEVICE_SELF, 0); // user ROM
	map(0x7e00, 0x7fef).rom().region(DEVICE_SELF, 0xe00); // self-check (incl. vectors)
	map(0x7ff0, 0x7fff).rom().region(DEVICE_SELF, 0xff0); // user vectors
	// 0x8000-0x7fffff external memory (banked in four 8K segments)
}


m68hc05l11_device::m68hc05l11_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
	: m68hc05_device(
			mconfig,
			tag,
			owner,
			clock,
			M68HC05L11,
			16, // FIXME: 16 logical mapped to 23 physical
			0x7fff,
			address_map_constructor(FUNC(m68hc05l11_device::l11_map), this))
{
	set_port_bits(std::array<u8, PORT_COUNT>{{ 0xff, 0xff, 0xff, 0xff }});
}



void m68hc05l11_device::device_start()
{
	m68hc05_device::device_start();

	add_port_state(std::array<bool, PORT_COUNT>{{ true, true, true, false }});
	add_timer_state();
}


std::unique_ptr<util::disasm_interface> m68hc05l11_device::create_disassembler()
{
	return std::make_unique<m68hc05_disassembler>(m68hc05c4_syms);
}