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



                                                                            









                                                                            
































































































                                                                                     





                                        
 

                                                


                                                                                                   
                                                                                                                       
                                                                   
                                                                   
                                                                   



                                                                                                                                 




                                                                                                         



                                                                                                         
                                                                
 






                                                                                                                 

                                                


 




                                                   




















                                                                






                                                                                           












                                                   
 
































                                                
 



                                                    
                                                










                                                   
 






                                          


 








                                                                


                                                                                              
 








                                                              
 
                                              
                                                                                                            




































                                                                             
 























                                                                                       
 



















                                                                                                          
 
























                                                                                                                                                         
 




                                                                                                                                                                   
                                                                                        





                                              
 


























































































                                                                                                                                                                     
 

































































                                                                                                                                    
                                                                                         












































                                                                             
 
                                                    
 




                                          
                                                                                           




                                                
 
                  
                                                                                              





                                                              
                                                                                           
































                                                                                    
 





















                                                                                         
                                                                                                               










                                                                           
 

                                     
                                                                                  









                                               
 

                                    
                                                                                  









                                               
 

                                    
                                                                                  









                                              
 

                                   
                                                                                  












                                               
                                                                                









                                              
 
                               
                                                                                









                                                
                                                                                  












                                                                            
// license:LGPL-2.1+
// copyright-holders:Angelo Salese
/***************************************************************************

    Sega System Control Unit (c) 1995 Sega

    TODO:
    - make a screen_device subclass, add pixel timers & irq callbacks to it;
    - A-Bus external interrupts;
    - Pad signal (lightgun I presume);
    - Improve DMA, use DRQ model and timers, make them subdevices?
    (old DMA TODO)
    - Remove CD transfer DMA hack (tied with CD block bug(s)?)
    - Add timings(but how fast are each DMA?).
    - Add level priority & DMA status register.

***************************************************************************/
/**********************************************************************************
SCU Register Table
offset,relative address
Registers are in long words.
===================================================================================
0     0000  Level 0 DMA Set Register
1     0004
2     0008
3     000c
4     0010
5     0014
6     0018
7     001c
8     0020  Level 1 DMA Set Register
9     0024
10    0028
11    002c
12    0030
13    0034
14    0038
15    003c
16    0040  Level 2 DMA Set Register
17    0044
18    0048
19    004c
20    0050
21    0054
22    0058
23    005c
24    0060  DMA Forced Stop
25    0064
26    0068
27    006c
28    0070  <Free>
29    0074
30    0078
31    007c  DMA Status Register
32    0080  DSP Program Control Port
33    0084  DSP Program RAM Data Port
34    0088  DSP Data RAM Address Port
35    008c  DSP Data RAM Data Port
36    0090  Timer 0 Compare Register
37    0094  Timer 1 Set Data Register
38    0098  Timer 1 Mode Register
39    009c  <Free>
40    00a0  Interrupt Mask Register
41    00a4  Interrupt Status Register
42    00a8  A-Bus Interrupt Acknowledge
43    00ac  <Free>
44    00b0  A-Bus Set Register
45    00b4
46    00b8  A-Bus Refresh Register
47    00bc  <Free>
48    00c0
49    00c4  SCU SDRAM Select Register
50    00c8  SCU Version Register
51    00cc  <Free>
52    00cf
===================================================================================
DMA Status Register(32-bit):
xxxx xxxx x--- xx-- xx-- xx-- xx-- xx-- UNUSED
---- ---- -x-- ---- ---- ---- ---- ---- DMA DSP-Bus access
---- ---- --x- ---- ---- ---- ---- ---- DMA B-Bus access
---- ---- ---x ---- ---- ---- ---- ---- DMA A-Bus access
---- ---- ---- --x- ---- ---- ---- ---- DMA lv 1 interrupt
---- ---- ---- ---x ---- ---- ---- ---- DMA lv 0 interrupt
---- ---- ---- ---- --x- ---- ---- ---- DMA lv 2 in stand-by
---- ---- ---- ---- ---x ---- ---- ---- DMA lv 2 in operation
---- ---- ---- ---- ---- --x- ---- ---- DMA lv 1 in stand-by
---- ---- ---- ---- ---- ---x ---- ---- DMA lv 1 in operation
---- ---- ---- ---- ---- ---- --x- ---- DMA lv 0 in stand-by
---- ---- ---- ---- ---- ---- ---x ---- DMA lv 0 in operation
---- ---- ---- ---- ---- ---- ---- --x- DSP side DMA in stand-by
---- ---- ---- ---- ---- ---- ---- ---x DSP side DMA in operation

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

#include "emu.h"
#include "sega_scu.h"



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

// device type definition
DEFINE_DEVICE_TYPE(SEGA_SCU, sega_scu_device, "sega_scu", "Sega System Control Unit")


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

//map(0x0000, 0x0003) src
//map(0x0004, 0x0007) dst
//map(0x0008, 0x000b) size
//map(0x000c, 0x000f) src/dst add values
//map(0x0010, 0x0013) DMA enable
//map(0x0014, 0x0017) DMA start factor

void sega_scu_device::regs_map(address_map &map)
{
	map(0x0000, 0x0017).rw(FUNC(sega_scu_device::dma_lv0_r), FUNC(sega_scu_device::dma_lv0_w));
	map(0x0020, 0x0037).rw(FUNC(sega_scu_device::dma_lv1_r), FUNC(sega_scu_device::dma_lv1_w));
	map(0x0040, 0x0057).rw(FUNC(sega_scu_device::dma_lv2_r), FUNC(sega_scu_device::dma_lv2_w));
	// Super Major League and Shin Megami Tensei - Akuma Zensho reads from there (undocumented), DMA status mirror?
	map(0x005c, 0x005f).r(FUNC(sega_scu_device::dma_status_r));
//  map(0x0060, 0x0063).w(FUNC(sega_scu_device::dma_force_stop_w));
	map(0x007c, 0x007f).r(FUNC(sega_scu_device::dma_status_r));
	map(0x0080, 0x0083).rw(m_scudsp, FUNC(scudsp_cpu_device::program_control_r), FUNC(scudsp_cpu_device::program_control_w));
	map(0x0084, 0x0087).w(m_scudsp, FUNC(scudsp_cpu_device::program_w));
	map(0x0088, 0x008b).w(m_scudsp, FUNC(scudsp_cpu_device::ram_address_control_w));
	map(0x008c, 0x008f).rw(m_scudsp, FUNC(scudsp_cpu_device::ram_address_r), FUNC(scudsp_cpu_device::ram_address_w));
	map(0x0090, 0x0093).w(FUNC(sega_scu_device::t0_compare_w));
	map(0x0094, 0x0097).w(FUNC(sega_scu_device::t1_setdata_w));
	map(0x009a, 0x009b).w(FUNC(sega_scu_device::t1_mode_w));
	map(0x00a0, 0x00a3).rw(FUNC(sega_scu_device::irq_mask_r), FUNC(sega_scu_device::irq_mask_w));
	map(0x00a4, 0x00a7).rw(FUNC(sega_scu_device::irq_status_r), FUNC(sega_scu_device::irq_status_w));
//  map(0x00a8, 0x00ab).w(FUNC(sega_scu_device::abus_irqack_w));
//  map(0x00b0, 0x00b7).rw(FUNC(sega_scu_device::abus_set_r), FUNC(sega_scu_device::abus_set_w));
//  map(0x00b8, 0x00bb).rw(FUNC(sega_scu_device::abus_refresh_r), FUNC(sega_scu_device::abus_refresh_w));
//  map(0x00c4, 0x00c7).rw(FUNC(sega_scu_device::sdram_r), FUNC(sega_scu_device::sdram_w));
	map(0x00c8, 0x00cb).r(FUNC(sega_scu_device::version_r));
}

//-------------------------------------------------
//  sega_scu_device - constructor
//-------------------------------------------------

sega_scu_device::sega_scu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, SEGA_SCU, tag, owner, clock),
	m_scudsp(*this, "scudsp"),
	m_hostcpu(*this, finder_base::DUMMY_TAG)
{
}

//-------------------------------------------------
//  add_device_mconfig - device-specific machine
//  configuration addiitons
//-------------------------------------------------

READ16_MEMBER(sega_scu_device::scudsp_dma_r)
{
	//address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = offset;

//  printf("%08x\n",addr);

	return m_hostspace->read_word(addr,mem_mask);
}


WRITE16_MEMBER(sega_scu_device::scudsp_dma_w)
{
	//address_space &program = m_maincpu->space(AS_PROGRAM);
	offs_t addr = offset;

//  printf("%08x %02x\n",addr,data);

	m_hostspace->write_word(addr, data,mem_mask);
}

void sega_scu_device::device_add_mconfig(machine_config &config)
{
	SCUDSP(config, m_scudsp, XTAL(57'272'727)/4); // 14 MHz
	m_scudsp->out_irq_callback().set(DEVICE_SELF, FUNC(sega_scu_device::scudsp_end_w));
	m_scudsp->in_dma_callback().set(FUNC(sega_scu_device::scudsp_dma_r));
	m_scudsp->out_dma_callback().set(FUNC(sega_scu_device::scudsp_dma_w));
}


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

void sega_scu_device::device_start()
{
	save_item(NAME(m_ist));
	save_item(NAME(m_ism));
	save_item(NAME(m_t0c));
	save_item(NAME(m_t1s));
	save_item(NAME(m_t1md));

	save_item(NAME(m_dma[0].src));
	save_item(NAME(m_dma[0].dst));
	save_item(NAME(m_dma[0].src_add));
	save_item(NAME(m_dma[0].dst_add));
	save_item(NAME(m_dma[0].size));
	save_item(NAME(m_dma[0].index));
	save_item(NAME(m_dma[0].start_factor));
	save_item(NAME(m_dma[0].enable_mask));
	save_item(NAME(m_dma[0].indirect_mode));
	save_item(NAME(m_dma[0].rup));
	save_item(NAME(m_dma[0].wup));
	save_item(NAME(m_dma[1].src));
	save_item(NAME(m_dma[1].dst));
	save_item(NAME(m_dma[1].src_add));
	save_item(NAME(m_dma[1].dst_add));
	save_item(NAME(m_dma[1].size));
	save_item(NAME(m_dma[1].index));
	save_item(NAME(m_dma[1].start_factor));
	save_item(NAME(m_dma[1].enable_mask));
	save_item(NAME(m_dma[1].indirect_mode));
	save_item(NAME(m_dma[1].rup));
	save_item(NAME(m_dma[1].wup));
	save_item(NAME(m_dma[2].src));
	save_item(NAME(m_dma[2].dst));
	save_item(NAME(m_dma[2].src_add));
	save_item(NAME(m_dma[2].dst_add));
	save_item(NAME(m_dma[2].size));
	save_item(NAME(m_dma[2].index));
	save_item(NAME(m_dma[2].start_factor));
	save_item(NAME(m_dma[2].enable_mask));
	save_item(NAME(m_dma[2].indirect_mode));
	save_item(NAME(m_dma[2].rup));
	save_item(NAME(m_dma[2].wup));

	m_hostspace = &m_hostcpu->space(AS_PROGRAM);

	m_dma_timer[0] = timer_alloc(DMALV0_ID);
	m_dma_timer[1] = timer_alloc(DMALV1_ID);
	m_dma_timer[2] = timer_alloc(DMALV2_ID);
}


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

void sega_scu_device::device_reset()
{
	m_ism = 0xbfff;
	m_ist = 0;

	for(int i=0;i<3;i++)
	{
		m_dma[i].start_factor = 7;
		m_dma_timer[i]->reset();
	}

	m_status = 0;

}

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

void sega_scu_device::device_reset_after_children()
{
	m_scudsp->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
}


void sega_scu_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{

	switch(id)
	{
		case DMALV0_ID:
		case DMALV1_ID:
		case DMALV2_ID:
		{
			const int irqlevel = id == 0 ? 5 : 6;
			const int irqvector = 0x4b - id;
			const uint16_t irqmask = 1 << (11-id);

			if(!(m_ism & irqmask))
				m_hostcpu->set_input_line_and_vector(irqlevel, HOLD_LINE, irqvector); // SH2
			else
				m_ist |= (irqmask);

			update_dma_status(id,false);
			machine().scheduler().synchronize(); // force resync
			break;
		}
		default:
			break;
	}
}

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

//**************************************************************************
//  DMA
//**************************************************************************

uint32_t sega_scu_device::dma_common_r(uint8_t offset,uint8_t level)
{
	switch(offset)
	{
		case 0x00/4: // source
			return m_dma[level].src;
		case 0x04/4: // destination
			return m_dma[level].dst;
		case 0x08/4: // size
			return m_dma[level].size;
		case 0x0c/4:
			return 0; // DxAD, write only
		case 0x10/4:
			return 0; // DxEN / DxGO, write only
		case 0x14/4:
			return 0; // DxMOD / DxRUP / DxWUP / DxFT, write only
	}

	// can't happen anyway
	return 0;
}

void sega_scu_device::dma_common_w(uint8_t offset,uint8_t level,uint32_t data)
{
	switch(offset)
	{
		case 0x00/4: // source
			m_dma[level].src = data & 0x07ffffff;
			break;
		case 0x04/4: // destination
			m_dma[level].dst = data & 0x07ffffff;
			break;
		case 0x08/4: // size, lv0 is bigger than the others
			m_dma[level].size = data & ((level == 0) ? 0x000fffff : 0xfff);
			break;
		case 0x0c/4: // DxAD
			m_dma[level].src_add = (data & 0x100) ? 4 : 0;
			m_dma[level].dst_add = 1 << (data & 7);
			if(m_dma[level].dst_add == 1) { m_dma[level].dst_add = 0; }
			break;
		case 0x10/4: // DxEN / DxGO
			m_dma[level].enable_mask = BIT(data,8);

			// check if DxGO is enabled for start factor = 7
			if(m_dma[level].enable_mask == true && data & 1 && m_dma[level].start_factor == 7)
			{
				if(m_dma[level].indirect_mode == true)
					handle_dma_indirect(level);
				else
					handle_dma_direct(level);
			}
			break;
		case 0x14/4: // DxMOD / DxRUP / DxWUP / DxFT, write only
			m_dma[level].indirect_mode = BIT(data,24);
			m_dma[level].rup = BIT(data,16);
			m_dma[level].wup = BIT(data,8);
			m_dma[level].start_factor = data & 7;
			if(m_dma[level].indirect_mode == true)
			{
				//if(LOG_SCU) logerror("Indirect Mode DMA lv %d set\n",level);
				if(m_dma[level].wup == false)
					m_dma[level].index = m_dma[level].dst;
			}

			break;
	}
}

inline void sega_scu_device::update_dma_status(uint8_t level,bool state)
{
	if(state)
		m_status|=(0x10 << 4 * level);
	else
		m_status&=~(0x10 << 4 * level);
}

void sega_scu_device::handle_dma_direct(uint8_t level)
{
	uint32_t tmp_src,tmp_dst,total_size;
	uint8_t cd_transfer_flag;

	#if 0
	if(m_dma[level].src_add == 0 || (m_dma[level].dst_add != 2 && m_dma[level].dst_add != 4))
	{
	if(LOG_SCU) printf("DMA lv %d transfer START\n"
							"Start %08x End %08x Size %04x\n",dma_ch,m_scu.src[dma_ch],m_scu.dst[dma_ch],m_scu.size[dma_ch]);
	if(LOG_SCU) printf("Start Add %04x Destination Add %04x\n",m_scu.src_add[dma_ch],m_scu.dst_add[dma_ch]);
	}
	#endif

	// Game Basic, World Cup 98 and Batman Forever trips this, according to the docs the SCU can't transfer from BIOS area (can't communicate from/to that bus)
	if((m_dma[level].src & 0x07f00000) == 0)
	{
		//popmessage("Warning: SCU transfer from BIOS area, contact MAMEdev");
		if(!(m_ism & IRQ_DMAILL))
			m_hostcpu->set_input_line_and_vector(3, HOLD_LINE, 0x4c); // SH2
		else
			m_ist |= (IRQ_DMAILL);
		return;
	}

	update_dma_status(level,true);

	/* max size */
	if(m_dma[level].size == 0) { m_dma[level].size  = (level == 0) ? 0x00100000 : 0x1000; }

	tmp_src = tmp_dst = 0;

	total_size = m_dma[level].size ;
	if(m_dma[level].rup == false) tmp_src = m_dma[level].src;
	if(m_dma[level].wup == false) tmp_dst = m_dma[level].dst;

	cd_transfer_flag = m_dma[level].src_add == 0 && m_dma[level].src == 0x05818000;

	/* TODO: Many games directly accesses CD-ROM register 0x05818000, it must be a dword access with current implementation otherwise it won't work */
	if(cd_transfer_flag)
	{
		int i;
		if((m_dma[level].dst & 0x07000000) == 0x06000000)
			m_dma[level].dst_add = 4;
		else
			m_dma[level].dst_add <<= 1;

		for (i = 0; i < m_dma[level].size;i+=m_dma[level].dst_add)
		{
			m_hostspace->write_dword(m_dma[level].dst,m_hostspace->read_dword(m_dma[level].src));
			if(m_dma[level].dst_add == 8)
				m_hostspace->write_dword(m_dma[level].dst+4,m_hostspace->read_dword(m_dma[level].src));

			m_dma[level].src += m_dma[level].src_add;
			m_dma[level].dst += m_dma[level].dst_add;
		}
	}
	else
	{
		int i;
		uint8_t  src_shift;

		src_shift = ((m_dma[level].src & 2) >> 1) ^ 1;

		for (i = 0; i < m_dma[level].size; i+=2)
		{
			dma_single_transfer(m_dma[level].src, m_dma[level].dst, &src_shift);

			if(src_shift)
				m_dma[level].src+= m_dma[level].src_add ;

			/* if target is Work RAM H, the add value is fixed, behaviour confirmed by Final Romance 2, Virtual Mahjong and Burning Rangers */
			m_dma[level].dst += ((m_dma[level].dst & 0x07000000) == 0x06000000) ? 2 : m_dma[level].dst_add;
		}
	}

	/* Burning Rangers doesn't agree with this. */
//  m_scu.size[dma_ch] = 0;
	if(m_dma[level].rup == false) m_dma[level].src = tmp_src;
	if(m_dma[level].wup == false) m_dma[level].dst = tmp_dst;

	// TODO: Timing is a guess.
	m_dma_timer[level]->adjust(m_hostcpu->cycles_to_attotime(total_size/4));
}

void sega_scu_device::handle_dma_indirect(uint8_t level)
{
	/*Helper to get out of the cycle*/
	uint8_t job_done = 0;
	/*temporary storage for the transfer data*/
	uint32_t tmp_src;
	uint32_t indirect_src,indirect_dst;
	int32_t indirect_size;
	uint32_t total_size = 0;

	update_dma_status(level,true);

	m_dma[level].index = m_dma[level].dst;

	do{
		tmp_src = m_dma[level].index;

		indirect_size = m_hostspace->read_dword(m_dma[level].index);
		indirect_dst  = m_hostspace->read_dword(m_dma[level].index+4);
		indirect_src  = m_hostspace->read_dword(m_dma[level].index+8);

		/*Indirect Mode end factor*/
		if(indirect_src & 0x80000000)
			job_done = 1;

		#if 0
		if(m_dma[level].src_add == 0 || (m_dma[level].dst_add != 2))
		{
			if(LOG_SCU) printf("DMA lv %d indirect mode transfer START\n"
								"Index %08x Start %08x End %08x Size %04x\n",dma_ch,tmp_src,indirect_src,indirect_dst,indirect_size);
			if(LOG_SCU) printf("Start Add %04x Destination Add %04x\n",m_scu.src_add[dma_ch],m_scu.dst_add[dma_ch]);
		}
		#endif

		indirect_src &=0x07ffffff;
		indirect_dst &=0x07ffffff;
		indirect_size &= ((level == 0) ? 0xfffff : 0x3ffff); //TODO: Guardian Heroes sets up a 0x23000 transfer for the FMV?

		if(indirect_size == 0) { indirect_size = (level == 0) ? 0x00100000 : 0x2000; }

		{
			int i;
			uint8_t  src_shift;

			src_shift = ((indirect_src & 2) >> 1) ^ 1;

			for (i = 0; i < indirect_size;i+=2)
			{
				dma_single_transfer(indirect_src,indirect_dst,&src_shift);

				if(src_shift)
					indirect_src+=m_dma[level].src_add;

				indirect_dst += ((m_dma[level].dst & 0x07000000) == 0x06000000) ? 2 : m_dma[level].dst_add;
			}
		}

		/* Guess: Size + data acquire (1 cycle for src/dst/size) */
		total_size += indirect_size + 3*4;

		//if(DRUP(0)) space.write_dword(tmp_src+8,m_scu.src[0]|job_done ? 0x80000000 : 0);
		//if(DWUP(0)) space.write_dword(tmp_src+4,m_scu.dst[0]);

		m_dma[level].index = tmp_src+0xc;

	}while(job_done == 0);

	// TODO: Timing is a guess.
	m_dma_timer[level]->adjust(m_hostcpu->cycles_to_attotime(total_size/4));
}


inline void sega_scu_device::dma_single_transfer(uint32_t src, uint32_t dst,uint8_t *src_shift)
{
	uint32_t src_data;

	if(src & 1)
	{
		/* Road Blaster does a work ram h to color ram with offsetted source address, do some data rotation */
		src_data = ((m_hostspace->read_dword(src & 0x07fffffc) & 0x00ffffff)<<8);
		src_data |= ((m_hostspace->read_dword((src & 0x07fffffc)+4) & 0xff000000) >> 24);
		src_data >>= (*src_shift)*16;
	}
	else
		src_data = m_hostspace->read_dword(src & 0x07fffffc) >> (*src_shift)*16;

	m_hostspace->write_word(dst,src_data);

	*src_shift ^= 1;
}

inline void sega_scu_device::dma_start_factor_ack(uint8_t event)
{
	int i;

	for(i=0;i<3;i++)
	{
		if(m_dma[i].enable_mask == true && m_dma[i].start_factor == event)
		{
			if(m_dma[i].indirect_mode == true)      { handle_dma_indirect(i); }
			else                                    { handle_dma_direct(i); }
		}
	}
}

READ32_MEMBER(sega_scu_device::dma_lv0_r)  { return dma_common_r(offset,0); }
WRITE32_MEMBER(sega_scu_device::dma_lv0_w) { dma_common_w(offset,0,data); }
READ32_MEMBER(sega_scu_device::dma_lv1_r)  { return dma_common_r(offset,1); }
WRITE32_MEMBER(sega_scu_device::dma_lv1_w) { dma_common_w(offset,1,data); }
READ32_MEMBER(sega_scu_device::dma_lv2_r)  { return dma_common_r(offset,2); }
WRITE32_MEMBER(sega_scu_device::dma_lv2_w) { dma_common_w(offset,2,data); }

READ32_MEMBER(sega_scu_device::dma_status_r)
{
	return m_status;
}

//**************************************************************************
//  Timers
//**************************************************************************

WRITE32_MEMBER(sega_scu_device::t0_compare_w )
{
	COMBINE_DATA(&m_t0c);
}

WRITE32_MEMBER(sega_scu_device::t1_setdata_w )
{
	COMBINE_DATA(&m_t1s);
}

/*
 ---- ---x ---- ---- T1MD Timer 1 mode (0=each line, 1=only at timer 0 lines)
 ---- ---- ---- ---x TENB Timers enable
 */
WRITE16_MEMBER(sega_scu_device::t1_mode_w )
{
	m_t1md = BIT(data,8);
	m_tenb = BIT(data,0);
}


void sega_scu_device::check_scanline_timers(int scanline,int y_step)
{
	if(m_tenb == false)
		return;

	int timer0_compare = (m_t0c & 0x3ff)*y_step;

	// Timer 0
	if(scanline == timer0_compare)
	{
		if(!(m_ism & IRQ_TIMER_0))
		{
			m_hostcpu->set_input_line_and_vector(0xc, HOLD_LINE, 0x43 ); // SH2
			dma_start_factor_ack(3);
		}
		else
			m_ist |= (IRQ_TIMER_0);
	}

	// Timer 1
	// TODO: should happen after some time when hblank-in is received then counts down t1s
	if(
	  ((m_t1md == false) && ((scanline % y_step) == 0)) ||
	  ((m_t1md == true) && (scanline == timer0_compare)) )
	{
		if(!(m_ism & IRQ_TIMER_1))
		{
			m_hostcpu->set_input_line_and_vector(0xb, HOLD_LINE, 0x44 ); // SH2
			dma_start_factor_ack(4);
		}
		else
			m_ist |= (IRQ_TIMER_1);
	}

}


//**************************************************************************
//  Interrupt
//**************************************************************************

READ32_MEMBER(sega_scu_device::irq_mask_r)
{
	return m_ism;
}

READ32_MEMBER(sega_scu_device::irq_status_r)
{
	return m_ist;
}

WRITE32_MEMBER(sega_scu_device::irq_mask_w)
{
	COMBINE_DATA(&m_ism);
	test_pending_irqs();
}

WRITE32_MEMBER(sega_scu_device::irq_status_w)
{
	if(mem_mask != 0xffffffff)
		logerror("%s IST write %08x with %08x\n",this->tag(),data,mem_mask);

	m_ist &= data;
	test_pending_irqs();
}

void sega_scu_device::test_pending_irqs()
{
	int i;
	const int irq_level[32] = { 0xf, 0xe, 0xd, 0xc,
								0xb, 0xa, 0x9, 0x8,
								0x8, 0x6, 0x6, 0x5,
								0x3, 0x2,  -1,  -1,
								0x7, 0x7, 0x7, 0x7,
								0x4, 0x4, 0x4, 0x4,
								0x1, 0x1, 0x1, 0x1,
								0x1, 0x1, 0x1, 0x1  };

	for(i=0;i<32;i++)
	{
		if((!(m_ism & 1 << i)) && (m_ist & 1 << i))
		{
			if(irq_level[i] != -1) /* TODO: cheap check for undefined irqs */
			{
				m_hostcpu->set_input_line_and_vector(irq_level[i], HOLD_LINE, 0x40 + i); // SH2
				m_ist &= ~(1 << i);
				return; /* avoid spurious irqs, correct? */
			}
		}
	}
}

WRITE_LINE_MEMBER(sega_scu_device::vblank_out_w)
{
	if(!state)
		return;

	if(!(m_ism & IRQ_VBLANK_OUT))
	{
		m_hostcpu->set_input_line_and_vector(0xe, HOLD_LINE, 0x41); // SH2
		dma_start_factor_ack(1);
	}
	else
		m_ist |= (IRQ_VBLANK_OUT);
}

WRITE_LINE_MEMBER(sega_scu_device::vblank_in_w)
{
	if(!state)
		return;

	if(!(m_ism & IRQ_VBLANK_IN))
	{
		m_hostcpu->set_input_line_and_vector(0xf, HOLD_LINE ,0x40); // SH2
		dma_start_factor_ack(0);
	}
	else
		m_ist |= (IRQ_VBLANK_IN);
}

WRITE_LINE_MEMBER(sega_scu_device::hblank_in_w)
{
	if(!state)
		return;

	if(!(m_ism & IRQ_HBLANK_IN))
	{
		m_hostcpu->set_input_line_and_vector(0xd, HOLD_LINE, 0x42); // SH2
		dma_start_factor_ack(2);
	}
	else
		m_ist |= (IRQ_HBLANK_IN);
}

WRITE_LINE_MEMBER(sega_scu_device::vdp1_end_w)
{
	if(!state)
		return;

	if(!(m_ism & IRQ_VDP1_END))
	{
		m_hostcpu->set_input_line_and_vector(0x2, HOLD_LINE, 0x4d); // SH2
		dma_start_factor_ack(6);
	}
	else
		m_ist |= (IRQ_VDP1_END);
}

WRITE_LINE_MEMBER(sega_scu_device::sound_req_w)
{
	if(!state)
		return;

	if(!(m_ism & IRQ_SOUND_REQ))
	{
		m_hostcpu->set_input_line_and_vector(9, HOLD_LINE, 0x46); // SH2
		dma_start_factor_ack(5);
	}
	else
		m_ist |= (IRQ_SOUND_REQ);
}

WRITE_LINE_MEMBER(sega_scu_device::smpc_irq_w)
{
	if(!state)
		return;

	if(!(m_ism & IRQ_SMPC))
		m_hostcpu->set_input_line_and_vector(8, HOLD_LINE, 0x47); // SH2
	else
		m_ist |= (IRQ_SMPC);
}

WRITE_LINE_MEMBER(sega_scu_device::scudsp_end_w)
{
	if(!state)
		return;

	if(!(m_ism & IRQ_DSP_END))
		m_hostcpu->set_input_line_and_vector(0xa, HOLD_LINE, 0x45); // SH2
	else
		m_ist |= (IRQ_DSP_END);

}

//**************************************************************************
//  Miscellanea
//**************************************************************************

READ32_MEMBER(sega_scu_device::version_r)
{
	return 4; // correct for stock Saturn at least
}