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










                                                                                     

                                                                                 
            

                                                             
 








                                               





































































































































                                                                                      
                         
 
                   

                     
 
                  
                     
 






                                                                                              

                                                              

                                                 
                          










                                                                                                       
 











                                                                                                                 
                                                                                                                                                         
                                    






                         


                            
                            

                          

                                               
 


                                 
 
 




                                                   


                                                                
 
                                     
 
 




                                                   
 

                                         

                                                                                                    

                                                       
 







                                  
 



                                       
 









                                         

                                





                                               
                                
 


                                                  
                                                
 






                                                                                         





                                                                                                
                                                                  
 
 













                                                                   
 

                            
 

                                 
                                



                                      
 
                                                                          

 







                                                                                         



                                                                            



                                                         
 
                                   

                                                        











                                                                                   

                                                                                                               






                                                                   


                                       
 





                                                                                 



















                                                                  
 





                                                                  
 















































                                           

 


























                                                   
 
                                             
 

                    

 
                                          




                      

                                          









                                               





                                                                            
                                                             


                                                                                
 
                               
 

                                                                  
 



                                                                                                  

                                                                                                                        


                                                                                                          
 






                                                                                                
 







                                                           
                                                   

                                                     
 
                                                          
 
























                                                                                              
 





                                                                






                                                                                                                              



                                        



                                                                              
 







                                                                                                  
 







                                                                              
 





                                                                          

                                               
                                                     
                                 


                                                                          
                                 
 




                                                                      

                                                                                                                   

                                              
 



















                                                                                                      
 

                                                                   






                                                 








                                          
 


                                      
         
                                                                            
 




                                                    
                                                                           
 


                                          
                                 










                                                            
 


                                             
                                                 








                                                                                  
 





















                                                                                                                                             

                                      
 











                                                                                                     

                                                              
 
 












                                            
 


                   



                                                                            



































































                                                                                                                           
 


 

                                             
                  


                                             
  









                                                                                      








                                                                                         

                                                                                    
                                                                                      



                                                                                                

  
                                         
 

                                                                  
 


                                                                                                 
 
                                       
 

                                                 
         
                                                 
 
                                          
                                                    
                                                                                    
 

                                           
 
                                       
 


                                                 
                                                 
 
                                          
 
                                                    
                                                                                    
 
                                           
         

 
                                                     
 

                                          
                       
 


                                                                      

 



                                                                                                                         
 
                                                         
 
// license:LGPL-2.1+
// copyright-holders:Angelo Salese, R. Belmont
/************************************************************************************

Sega Saturn SMPC - System Manager and Peripheral Control MCU simulation

The SMPC is actually a 4-bit Hitachi HD404920FS MCU, labeled with a Sega custom
315-5744 (that needs decapping)

MCU simulation by Angelo Salese & R. Belmont

TODO:
- timings;
- fix intback issue with inputs (according to the docs, it should fall in between
  VBLANK-IN and OUT, for obvious reasons);
- clean-ups;
- RTC subdevice (unknown type, handled here for convenience);
- Does ST-V even has a battery backed NVRAM?

Notes:
SMPC NVRAM contents:
[0] unknown (always 0)
[1] unknown (always 0)
[2] ---- -x-- Button Labels (0=enable)
    ---- --x- Audio Out (1=Mono 0=Stereo)
    ---- ---x BIOS audio SFXs enable (0=enable)
[3] language select (0=English, 5=Japanese)

*************************************************************************************/
/* SMPC Addresses

00
01 -w  Input Register 0 (IREG)
02
03 -w  Input Register 1
04
05 -w  Input Register 2
06
07 -w  Input Register 3
08
09 -w  Input Register 4
0a
0b -w  Input Register 5
0c
0d -w  Input Register 6
0e
0f
10
11
12
13
14
15
16
17
18
19
1a
1b
1c
1d
1e
1f -w  Command Register (COMREG)
20
21 r-  Output Register 0 (OREG)
22
23 r-  Output Register 1
24
25 r-  Output Register 2
26
27 r-  Output Register 3
28
29 r-  Output Register 4
2a
2b r-  Output Register 5
2c
2d r-  Output Register 6
2e
2f r-  Output Register 7
30
31 r-  Output Register 8
32
33 r-  Output Register 9
34
35 r-  Output Register 10
36
37 r-  Output Register 11
38
39 r-  Output Register 12
3a
3b r-  Output Register 13
3c
3d r-  Output Register 14
3e
3f r-  Output Register 15
40
41 r-  Output Register 16
42
43 r-  Output Register 17
44
45 r-  Output Register 18
46
47 r-  Output Register 19
48
49 r-  Output Register 20
4a
4b r-  Output Register 21
4c
4d r-  Output Register 22
4e
4f r-  Output Register 23
50
51 r-  Output Register 24
52
53 r-  Output Register 25
54
55 r-  Output Register 26
56
57 r-  Output Register 27
58
59 r-  Output Register 28
5a
5b r-  Output Register 29
5c
5d r-  Output Register 30
5e
5f r-  Output Register 31
60
61 r-  SR
62
63 rw  SF
64
65
66
67
68
69
6a
6b
6c
6d
6e
6f
70
71
72
73
74
75 rw PDR1
76
77 rw PDR2
78
79 -w DDR1
7a
7b -w DDR2
7c
7d -w IOSEL2/1
7e
7f -w EXLE2/1
*/

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

#include "screen.h"
#include "coreutil.h"


#define LOG_SMPC 0
#define LOG_PAD_CMD 0

//**************************************************************************
//  GLOBAL VARIABLES
//**************************************************************************

// device type definition
DEFINE_DEVICE_TYPE(SMPC_HLE, smpc_hle_device, "smpc_hle", "Sega Saturn SMPC HLE (HD404920FS)")

// TODO: use DEVICE_ADDRESS_MAP once this fatalerror is fixed:
// "uplift_submaps unhandled case: range straddling slots."
void smpc_hle_device::smpc_regs(address_map &map)
{
//  ADDRESS_MAP_UNMAP_HIGH
	map(0x00, 0x0d).w(FUNC(smpc_hle_device::ireg_w));
	map(0x1f, 0x1f).w(FUNC(smpc_hle_device::command_register_w));
	map(0x20, 0x5f).r(FUNC(smpc_hle_device::oreg_r));
	map(0x61, 0x61).r(FUNC(smpc_hle_device::status_register_r));
	map(0x63, 0x63).rw(FUNC(smpc_hle_device::status_flag_r), FUNC(smpc_hle_device::status_flag_w));
	map(0x75, 0x75).rw(FUNC(smpc_hle_device::pdr1_r), FUNC(smpc_hle_device::pdr1_w));
	map(0x77, 0x77).rw(FUNC(smpc_hle_device::pdr2_r), FUNC(smpc_hle_device::pdr2_w));
	map(0x79, 0x79).w(FUNC(smpc_hle_device::ddr1_w));
	map(0x7b, 0x7b).w(FUNC(smpc_hle_device::ddr2_w));
	map(0x7d, 0x7d).w(FUNC(smpc_hle_device::iosel_w));
	map(0x7f, 0x7f).w(FUNC(smpc_hle_device::exle_w));
}

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

//-------------------------------------------------
//  smpc_hle_device - constructor
//-------------------------------------------------

smpc_hle_device::smpc_hle_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SMPC_HLE, tag, owner, clock),
	device_memory_interface(mconfig, *this),
	  m_space_config("regs", ENDIANNESS_LITTLE, 8, 7, 0, address_map_constructor(), address_map_constructor(FUNC(smpc_hle_device::smpc_regs), this)),
	m_mini_nvram(*this, "smem"),
	m_mshres(*this),
	m_mshnmi(*this),
	m_sshres(*this),
	m_sndres(*this),
	m_sysres(*this),
	m_syshalt(*this),
	m_dotsel(*this),
	m_pdr1_read(*this),
	m_pdr2_read(*this),
	m_pdr1_write(*this),
	m_pdr2_write(*this),
	m_irq_line(*this),
	m_ctrl1(nullptr),
	m_ctrl2(nullptr),
	m_screen(*this, finder_base::DUMMY_TAG)
{
	m_ctrl1 = nullptr;
	m_ctrl2 = nullptr;
	m_has_ctrl_ports = false;
}

//-------------------------------------------------
//  device_add_mconfig - device-specific machine
//  configuration addiitons
//-------------------------------------------------

void smpc_hle_device::device_add_mconfig(machine_config &config)
{
	NVRAM(config, "smem", nvram_device::DEFAULT_ALL_0);

	// TODO: custom RTC subdevice
}

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

void smpc_hle_device::device_start()
{
	system_time systime;
	machine().base_datetime(systime);

//  check if SMEM has valid data via byte 4 in the array, if not then simulate a battery backup fail
//  (-> call the RTC / Language select menu for Saturn)
	m_mini_nvram->set_base(&m_smem, 5);

	m_mshres.resolve_safe();
	m_mshnmi.resolve_safe();
	m_sshres.resolve_safe();
	m_sndres.resolve_safe();
	m_sysres.resolve_safe();
	m_syshalt.resolve_safe();
	m_dotsel.resolve_safe();
	m_irq_line.resolve_safe();

	m_pdr1_read.resolve_safe(0xff);
	m_pdr2_read.resolve_safe(0xff);
	m_pdr1_write.resolve_safe();
	m_pdr2_write.resolve_safe();

	save_item(NAME(m_sf));
	save_item(NAME(m_sr));
	save_item(NAME(m_ddr1));
	save_item(NAME(m_ddr2));
	save_item(NAME(m_pdr1_readback));
	save_item(NAME(m_pdr2_readback));
	save_item(NAME(m_iosel1));
	save_item(NAME(m_iosel2));
	save_item(NAME(m_exle1));
	save_item(NAME(m_exle2));
	save_item(NAME(m_ireg));
	save_item(NAME(m_oreg));
	save_item(NAME(m_comreg));
	save_item(NAME(m_command_in_progress));
	save_item(NAME(m_intback_buf));
	save_item(NAME(m_intback_stage));
	save_item(NAME(m_pmode));
	save_item(NAME(m_rtc_data));
	save_item(NAME(m_smem));

	m_cmd_timer = timer_alloc(COMMAND_ID);
	m_rtc_timer = timer_alloc(RTC_ID);
	m_intback_timer = timer_alloc(INTBACK_ID);
	m_sndres_timer = timer_alloc(SNDRES_ID);

	m_rtc_data[0] = DectoBCD(systime.local_time.year / 100);
	m_rtc_data[1] = DectoBCD(systime.local_time.year % 100);
	m_rtc_data[2] = (systime.local_time.weekday << 4) | (systime.local_time.month+1);
	m_rtc_data[3] = DectoBCD(systime.local_time.mday);
	m_rtc_data[4] = DectoBCD(systime.local_time.hour);
	m_rtc_data[5] = DectoBCD(systime.local_time.minute);
	m_rtc_data[6] = DectoBCD(systime.local_time.second);

	if (m_has_ctrl_ports)
	{
		m_ctrl1 = downcast<saturn_control_port_device *>(machine().device(m_ctrl1_tag));
		m_ctrl2 = downcast<saturn_control_port_device *>(machine().device(m_ctrl2_tag));
	}
//  m_has_ctrl_ports = (m_ctrl1 != nullptr && m_ctrl2 != nullptr);
}


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

void smpc_hle_device::device_reset()
{
	m_sr = 0x40; // this bit is always on according to docs (?)
	m_sf = false;
	m_cd_sf = false;
	m_ddr1 = 0;
	m_ddr2 = 0;
	m_pdr1_readback = 0;
	m_pdr2_readback = 0;

	memset(m_ireg,0,7);
	memset(m_oreg,0,32);

	m_cmd_timer->reset();
	m_intback_timer->reset();
	m_sndres_timer->reset();
	m_comreg = 0xff;
	m_command_in_progress = false;
	m_NMI_reset = false;
	m_cur_dotsel = false;

	m_rtc_timer->adjust(attotime::zero, 0, attotime::from_seconds(1));
}

device_memory_interface::space_config_vector smpc_hle_device::memory_space_config() const
{
	return space_config_vector {
		std::make_pair(0, &m_space_config)
	};
}


//**************************************************************************
//  READ/WRITE HANDLERS
//**************************************************************************

WRITE8_MEMBER( smpc_hle_device::ireg_w )
{
	if (!(offset & 1)) // avoid writing to even bytes
		return;

	m_ireg[offset >> 1] = data;

	if(offset == 1) // check if we are under intback
	{
		if(m_intback_stage)
		{
			if(data & 0x40)
			{
				if(LOG_PAD_CMD) printf("SMPC: BREAK request\n");
				sr_ack();
				m_intback_stage = 0;
			}
			else if(data & 0x80)
			{
				if(LOG_PAD_CMD) printf("SMPC: CONTINUE request\n");

				m_intback_timer->adjust(attotime::from_usec(700));  // TODO: is timing correct?

				// TODO: following looks wrong here
				m_oreg[31] = 0x10;
				sf_set();
			}
		}
	}
}

READ8_MEMBER( smpc_hle_device::oreg_r )
{
	if (!(offset & 1)) // avoid reading to even bytes (TODO: is it 0s or 1s?)
		return 0x00;

	return m_oreg[offset >> 1];
}

READ8_MEMBER( smpc_hle_device::status_register_r )
{
	return m_sr;
}

READ8_MEMBER( smpc_hle_device::status_flag_r )
{
	// bit 3: CD enable related?
	return (m_sf<<0) | (m_cd_sf<<3);
}

WRITE8_MEMBER( smpc_hle_device::status_flag_w )
{
	m_sf = BIT(data,0);
	m_cd_sf = false;
}

READ8_MEMBER( smpc_hle_device::pdr1_r )
{
	uint8_t res = (m_pdr1_read() & ~m_ddr1) | m_pdr1_readback;

	return res;
}

READ8_MEMBER( smpc_hle_device::pdr2_r )
{
	uint8_t res = (m_pdr2_read() & ~m_ddr2) | m_pdr2_readback;

	return res;
}

WRITE8_MEMBER( smpc_hle_device::pdr1_w )
{
//  pins defined as output returns in input
	m_pdr1_readback = (data & m_ddr1);
	m_pdr1_readback &= 0x7f;
	m_pdr1_write(m_pdr1_readback);
//  bit 7 can be read back apparently
	m_pdr1_readback |= data & 0x80;
}

WRITE8_MEMBER( smpc_hle_device::pdr2_w )
{
//  pins defined as output returns in input
	m_pdr2_readback = (data & m_ddr2);
	m_pdr2_readback &= 0x7f;
	m_pdr2_write(m_pdr2_readback);
//  bit 7 can be read back apparently
	m_pdr2_readback |= data & 0x80;
}

WRITE8_MEMBER( smpc_hle_device::ddr1_w )
{
	m_ddr1 = data & 0x7f;
}

WRITE8_MEMBER( smpc_hle_device::ddr2_w )
{
	m_ddr2 = data & 0x7f;
}

WRITE8_MEMBER( smpc_hle_device::iosel_w )
{
	m_iosel1 = BIT(data,0);
	m_iosel2 = BIT(data,1);
}

WRITE8_MEMBER( smpc_hle_device::exle_w )
{
	m_exle1 = BIT(data,0);
	m_exle2 = BIT(data,1);
}

inline void smpc_hle_device::sr_ack()
{
	m_sr &= 0x0f;
}

inline void smpc_hle_device::sr_set(uint8_t data)
{
	m_sr = data;
}

inline void smpc_hle_device::sf_ack(bool cd_enable)
{
	m_sf = false;
	m_cd_sf = cd_enable;
}

inline void smpc_hle_device::sf_set()
{
	m_sf = true;
}

// Saturn Direct Mode polling check for delegate
bool smpc_hle_device::get_iosel(bool which)
{
	return which == true ? m_iosel2 : m_iosel1;
}

uint8_t smpc_hle_device::get_ddr(bool which)
{
	return which == true ? m_ddr2 : m_ddr1;
}


inline void smpc_hle_device::master_sh2_nmi()
{
	m_mshnmi(1);
	m_mshnmi(0);
}

inline void smpc_hle_device::irq_request()
{
	m_irq_line(1);
	m_irq_line(0);
}

// TODO: trampolines that needs to go away

READ8_MEMBER( smpc_hle_device::read )
{
	return this->space().read_byte(offset);
}

WRITE8_MEMBER( smpc_hle_device::write )
{
	this->space().write_byte(offset,data);
}

//**************************************************************************
//  Command simulation
//**************************************************************************

WRITE8_MEMBER( smpc_hle_device::command_register_w )
{
//  don't send a command if previous one is still in progress
//  ST-V tries to send a sysres command if OREG31 doesn't return the ack command
	if(m_command_in_progress == true)
		return;

	m_comreg = data & 0x1f;

	if(data & 0xe0)
		logerror("%s COMREG = %02x!?\n",this->tag(),data);

	m_command_in_progress = true;
	if(m_comreg == 0x0e || m_comreg == 0x0f)
	{
		/* on ST-V timing of this is pretty fussy, you get 2 credits at start-up otherwise
		 * My current theory is that the PLL device can halt the whole system until the frequency change occurs.
		 *  (cfr. diagram on page 3 of SMPC manual)
		 * I really don't think that the system can do an usable mid-frame clock switching anyway.
		 */
		m_syshalt(1);

		m_cmd_timer->adjust(m_screen->time_until_pos(m_screen->visible_area().max_y,0));
	}
	else if(m_comreg == 0x10)
	{
		// copy ireg to our intback buffer
		for(int i=0;i<3;i++)
			m_intback_buf[i] = m_ireg[i];

		// calculate the timing for intback command
		int timing;

		timing = 8;

		if( m_ireg[0] != 0) // non-peripheral data
			timing += 8;

		// TODO: At vblank-out actually ...
		if( m_ireg[1] & 8) // peripheral data
			timing += 700;

		// TODO: check against ireg2, must be 0xf0

		m_cmd_timer->adjust(attotime::from_usec(timing));
	}
	else
		m_cmd_timer->adjust(attotime::from_usec(m_cmd_table_timing[m_comreg]));
}


void smpc_hle_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch(id)
	{
		case COMMAND_ID:
		{
			switch(m_comreg)
			{
				case 0x00: // MSHON
					// enable Master SH2
					m_mshres(m_comreg & 1);
					break;

				case 0x02: // SSHON
				case 0x03: // SSHOFF
					// enable or disable Slave SH2
					m_sshres(m_comreg & 1);
					break;

				case 0x06: // SNDON
				case 0x07: // SNDOFF
					// enable or disable 68k
					m_sndres(m_comreg & 1);
					break;

				case 0x08: // CDON
				case 0x09: // CDOFF
					// ...
					m_command_in_progress = false;
					m_oreg[31] = m_comreg;
					sf_ack(true); //clear hand-shake flag (TODO: diagnostic wants this to have bit 3 high)
					return;

//              case 0x0a: // NETLINKON
//              case 0x0b: // NETLINKOFF

				case 0x0d: // SYSRES
					// send a 1 -> 0 to device reset lines
					m_sysres(1);
					m_sysres(0);

					// send a 1 -> 0 transition to reset line (was PULSE_LINE)
					m_mshres(1);
					m_mshres(0);
					break;

				case 0x0e: // CKCHG352
				case 0x0f: // CKCHG320
					m_dotsel(m_comreg & 1);

					// send a NMI to Master SH2 if enabled
					if(m_NMI_reset == false)
						master_sh2_nmi();

					// assert Slave SH2 line
					m_sshres(1);
					// clear PLL system halt
					m_syshalt(0);

					// setup the new dot select
					m_cur_dotsel = (m_comreg & 1) ^ 1;
					break;

				case 0x10: // INTBACK
					resolve_intback();
					return;

				case 0x16: // SETTIME
				{
					for(int i=0;i<7;i++)
						m_rtc_data[i] = m_ireg[i];
					break;
				}

				case 0x17: // SETSMEM
				{
					for(int i=0;i<4;i++)
						m_smem[i] = m_ireg[i];

					// clear the SETIME variable, simulate a cr2032 battery alive in the system
					m_smem[4] = 0xff;
					break;
				}

				case 0x18: // NMIREQ
					// NMI is unconditionally requested
					master_sh2_nmi();
					break;

				case 0x19: // RESENAB
				case 0x1a: // RESDISA
					m_NMI_reset = m_comreg & 1;
					break;

				default:
					logerror("%s unemulated %02x command\n",this->tag(),m_comreg);
					return;
			}

			m_command_in_progress = false;
			m_oreg[31] = m_comreg;
			sf_ack(false);
			break;
		}

		case INTBACK_ID: intback_continue_request(); break;
		case RTC_ID: handle_rtc_increment(); break;

		// from m68k reset opcode trigger
		case SNDRES_ID:
			m_sndres(1);
			m_sndres(0);
			break;

		default:
			printf("%d\n",id);
			break;
	}
}

void smpc_hle_device::resolve_intback()
{
	int i;

	m_command_in_progress = false;

	if(m_intback_buf[0] != 0)
	{
		m_oreg[0] = ((m_smem[4] & 0x80) | ((m_NMI_reset & 1) << 6));

		for(i=0;i<7;i++)
			m_oreg[1+i] = m_rtc_data[i];

		m_oreg[8] = 0; // CTG0 / CTG1?

		m_oreg[9] = m_region_code; // TODO: system region on Saturn

		/*
		 0-11 -1-- unknown
		 -x-- ---- VDP2 dot select
		 ---- x--- MSHNMI
		 ---- --x- SYSRES
		 ---- ---x SOUNDRES
		 */
		m_oreg[10] = 0 << 7 |
					 m_cur_dotsel << 6 |
					 1 << 5 |
					 1 << 4 |
					 0 << 3 |
					 1 << 2 |
					 0 << 1 |
					 0 << 0;

		m_oreg[11] = 0 << 6; // CDRES

		for(i=0;i<4;i++)
			m_oreg[12+i] = m_smem[i];

		for(i=0;i<15;i++)
			m_oreg[16+i] = 0xff; // undefined

		m_intback_stage = (m_intback_buf[1] & 8) >> 3; // first peripheral
		sr_set(0x40 | (m_intback_stage << 5));
		m_pmode = m_intback_buf[0]>>4;

		irq_request();

		// put issued command in OREG31
		m_oreg[31] = 0x10; // TODO: doc says 0?
		/* clear hand-shake flag */
		sf_ack(false);
	}
	else if(m_intback_buf[1] & 8)
	{
		m_intback_stage = (m_intback_buf[1] & 8) >> 3; // first peripheral
		sr_set(0x40);
		m_oreg[31] = 0x10;
		intback_continue_request();
	}
	else
	{
		/* Shienryu calls this, it would be plainly illegal on Saturn, I'll just return the command and clear the hs flag for now. */
		m_oreg[31] = 0x10;
		sf_ack(false);
	}
}

void smpc_hle_device::intback_continue_request()
{
	if( m_has_ctrl_ports == true )
		read_saturn_ports();

	if (m_intback_stage == 2)
	{
		sr_set(0x80 | m_pmode);     // pad 2, no more data, echo back pad mode set by intback
		m_intback_stage = 0;
	}
	else
	{
		sr_set(0xc0 | m_pmode);    // pad 1, more data, echo back pad mode set by intback
		m_intback_stage ++;
	}
	irq_request();

	m_oreg[31] = 0x10; // callback for last command issued
	sf_ack(false);
}

int smpc_hle_device::DectoBCD(int num)
{
	int i, cnt = 0, tmp, res = 0;

	while (num > 0) {
		tmp = num;
		while (tmp >= 10) tmp %= 10;
		for (i=0; i<cnt; i++)
			tmp *= 16;
		res += tmp;
		cnt++;
		num /= 10;
	}

	return res;
}

//**************************************************************************
//  RTC handling
//**************************************************************************

void smpc_hle_device::handle_rtc_increment()
{
	const uint8_t dpm[12] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 };
	int year_num, year_count;

	/*
	    m_smpc.rtc_data[0] = DectoBCD(systime.local_time.year /100);
	    m_smpc.rtc_data[1] = DectoBCD(systime.local_time.year %100);
	    m_smpc.rtc_data[2] = (systime.local_time.weekday << 4) | (systime.local_time.month+1);
	    m_smpc.rtc_data[3] = DectoBCD(systime.local_time.mday);
	    m_smpc.rtc_data[4] = DectoBCD(systime.local_time.hour);
	    m_smpc.rtc_data[5] = DectoBCD(systime.local_time.minute);
	    m_smpc.rtc_data[6] = DectoBCD(systime.local_time.second);
	*/

	m_rtc_data[6]++;

	/* seconds from 9 -> 10*/
	if((m_rtc_data[6] & 0x0f) >= 0x0a)         { m_rtc_data[6]+=0x10; m_rtc_data[6]&=0xf0; }
	/* seconds from 59 -> 0 */
	if((m_rtc_data[6] & 0xf0) >= 0x60)         { m_rtc_data[5]++;     m_rtc_data[6] = 0; }
	/* minutes from 9 -> 10 */
	if((m_rtc_data[5] & 0x0f) >= 0x0a)         { m_rtc_data[5]+=0x10; m_rtc_data[5]&=0xf0; }
	/* minutes from 59 -> 0 */
	if((m_rtc_data[5] & 0xf0) >= 0x60)         { m_rtc_data[4]++;     m_rtc_data[5] = 0; }
	/* hours from 9 -> 10 */
	if((m_rtc_data[4] & 0x0f) >= 0x0a)         { m_rtc_data[4]+=0x10; m_rtc_data[4]&=0xf0; }
	/* hours from 23 -> 0 */
	if((m_rtc_data[4] & 0xff) >= 0x24)             { m_rtc_data[3]++; m_rtc_data[2]+=0x10; m_rtc_data[4] = 0; }
	/* week day name sunday -> monday */
	if((m_rtc_data[2] & 0xf0) >= 0x70)             { m_rtc_data[2]&=0x0f; }
	/* day number 9 -> 10 */
	if((m_rtc_data[3] & 0x0f) >= 0x0a)             { m_rtc_data[3]+=0x10; m_rtc_data[3]&=0xf0; }

	// year BCD to dec conversion (for the leap year stuff)
	{
		year_num = (m_rtc_data[1] & 0xf);

		for(year_count = 0; year_count < (m_rtc_data[1] & 0xf0); year_count += 0x10)
			year_num += 0xa;

		year_num += (m_rtc_data[0] & 0xf)*0x64;

		for(year_count = 0; year_count < (m_rtc_data[0] & 0xf0); year_count += 0x10)
			year_num += 0x3e8;
	}

	/* month +1 check */
	/* the RTC have a range of 1980 - 2100, so we don't actually need to support the leap year special conditions */
	if(((year_num % 4) == 0) && (m_rtc_data[2] & 0xf) == 2)
	{
		if((m_rtc_data[3] & 0xff) >= dpm[(m_rtc_data[2] & 0xf)-1]+1+1)
			{ m_rtc_data[2]++; m_rtc_data[3] = 0x01; }
	}
	else if((m_rtc_data[3] & 0xff) >= dpm[(m_rtc_data[2] & 0xf)-1]+1){ m_rtc_data[2]++; m_rtc_data[3] = 0x01; }
	/* year +1 check */
	if((m_rtc_data[2] & 0x0f) > 12)                { m_rtc_data[1]++;  m_rtc_data[2] = (m_rtc_data[2] & 0xf0) | 0x01; }
	/* year from 9 -> 10 */
	if((m_rtc_data[1] & 0x0f) >= 0x0a)             { m_rtc_data[1]+=0x10; m_rtc_data[1]&=0xf0; }
	/* year from 99 -> 100 */
	if((m_rtc_data[1] & 0xf0) >= 0xa0)             { m_rtc_data[0]++; m_rtc_data[1] = 0; }

	// probably not SO precise, here just for reference ...
	/* year from 999 -> 1000 */
	//if((m_rtc_data[0] & 0x0f) >= 0x0a)               { m_rtc_data[0]+=0x10; m_rtc_data[0]&=0xf0; }
	/* year from 9999 -> 0 */
	//if((m_rtc_data[0] & 0xf0) >= 0xa0)               { m_rtc_data[0] = 0; } //roll over
}




/********************************************
 *
 * Saturn handlers
 *
 *******************************************/

/*
    [0] port status:
        0x04 Sega-tap
        0x16 Multi-tap
        0x2x clock serial peripheral
        0xf0 peripheral isn't connected
        0xf1 peripheral is connected
    [1] Peripheral ID (note: lowest four bits determines the size of the input packet)
        0x02 digital pad
        0x25 (tested by Game Basic?)
        0x34 keyboard

 Lower 4 bits of the port status tell the number of controllers to check for the port
 Lower 4 bits of the peripheral ID tell the number of registers used by each controller
 For multitap / segatap, we have implemented the following logic:
 SMPC reads in sequence
 - status for port 1
 - ID first controller, followed by the number of reads needed by the plugged controller
 - ID second controller, followed by the number of reads needed by the plugged controller
 - and so on... until the 4th (for SegaTap) or 6th (for Multitap) controller is read
 TODO: how does the multitap check if a controller is connected? does it ask for the
 controller status of each subport? how does this work exactly?
 currently, there is a small problem in some specific controller config which seems to
 lose track of one controller. E.g. if I put multitap in port2 with inserted joy1, joy2 and joy4
 it does not see joy4 controller, but if I put joy1, joy2, joy4 and joy5 it sees
 all four of them. The same happens if I skip controllers with id = 0xff...
 how did a real unit behave in this case?
*/

void smpc_hle_device::read_saturn_ports()
{
	uint8_t status1 = m_ctrl1 ? m_ctrl1->read_status() : 0xf0;
	uint8_t status2 = m_ctrl2 ? m_ctrl2->read_status() : 0xf0;

	uint8_t reg_offset = 0;
	uint8_t ctrl1_offset = 0;     // this is used when there is segatap or multitap connected
	uint8_t ctrl2_offset = 0;     // this is used when there is segatap or multitap connected

	m_oreg[reg_offset++] = status1;

	// read ctrl1
	for (int i = 0; i < (status1 & 0xf); i++)
	{
		uint8_t id = m_ctrl1->read_id(i);

		m_oreg[reg_offset++] = id;
		for (int j = 0; j < (id & 0xf); j++)
			m_oreg[reg_offset++] = m_ctrl1->read_ctrl(j + ctrl1_offset);

		ctrl1_offset += (id & 0xf);
	}

	m_oreg[reg_offset++] = status2;

	// read ctrl2
	for (int i = 0; i < (status2 & 0xf); i++)
	{
		uint8_t id = m_ctrl2->read_id(i);

		m_oreg[reg_offset++] = id;

		for (int j = 0; j < (id & 0xf); j++)
			m_oreg[reg_offset++] = m_ctrl2->read_ctrl(j + ctrl2_offset);

		ctrl2_offset += (id & 0xf);
	}
}

INPUT_CHANGED_MEMBER(smpc_hle_device::trigger_nmi_r )
{
	// punt if NMI trigger is disabled
	if(!m_NMI_reset)
		return;

	// TODO: generated during the 3VINT period according to manual
	if(newval)
		master_sh2_nmi();
}

/* Official documentation says that the "RESET/TAS opcodes aren't supported", but Out Run definitely contradicts with it.
   Since that m68k can't reset itself via the RESET opcode I suppose that the SMPC actually do it by reading an i/o
   connected to this opcode. */
void smpc_hle_device::m68k_reset_trigger()
{
	m_sndres_timer->adjust(attotime::from_usec(100));
}