summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/sega_scu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/sega_scu.cpp')
-rw-r--r--src/devices/machine/sega_scu.cpp776
1 files changed, 776 insertions, 0 deletions
diff --git a/src/devices/machine/sega_scu.cpp b/src/devices/machine/sega_scu.cpp
new file mode 100644
index 00000000000..91154a9f775
--- /dev/null
+++ b/src/devices/machine/sega_scu.cpp
@@ -0,0 +1,776 @@
+// 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
+//**************************************************************************
+
+//AM_RANGE(0x0000, 0x0003) src
+//AM_RANGE(0x0004, 0x0007) dst
+//AM_RANGE(0x0008, 0x000b) size
+//AM_RANGE(0x000c, 0x000f) src/dst add values
+//AM_RANGE(0x0010, 0x0013) DMA enable
+//AM_RANGE(0x0014, 0x0017) DMA start factor
+
+DEVICE_ADDRESS_MAP_START( regs_map, 32, sega_scu_device )
+ AM_RANGE(0x0000, 0x0017) AM_READWRITE(dma_lv0_r,dma_lv0_w)
+ AM_RANGE(0x0020, 0x0037) AM_READWRITE(dma_lv1_r,dma_lv1_w)
+ AM_RANGE(0x0040, 0x0057) AM_READWRITE(dma_lv2_r,dma_lv2_w)
+ // Super Major League and Shin Megami Tensei - Akuma Zensho reads from there (undocumented), DMA status mirror?
+ AM_RANGE(0x005c, 0x005f) AM_READ(dma_status_r)
+// AM_RANGE(0x0060, 0x0063) AM_WRITE(dma_force_stop_w)
+ AM_RANGE(0x007c, 0x007f) AM_READ(dma_status_r)
+ AM_RANGE(0x0080, 0x0083) AM_DEVREADWRITE("scudsp",scudsp_cpu_device, program_control_r,program_control_w)
+ AM_RANGE(0x0084, 0x0087) AM_DEVWRITE("scudsp", scudsp_cpu_device, program_w)
+ AM_RANGE(0x0088, 0x008b) AM_DEVWRITE("scudsp", scudsp_cpu_device, ram_address_control_w)
+ AM_RANGE(0x008c, 0x008f) AM_DEVREADWRITE("scudsp", scudsp_cpu_device, ram_address_r, ram_address_w)
+ AM_RANGE(0x0090, 0x0093) AM_WRITE(t0_compare_w)
+ AM_RANGE(0x0094, 0x0097) AM_WRITE(t1_setdata_w)
+ AM_RANGE(0x0098, 0x009b) AM_WRITE16(t1_mode_w,0x0000ffff)
+ AM_RANGE(0x00a0, 0x00a3) AM_READWRITE(irq_mask_r,irq_mask_w)
+ AM_RANGE(0x00a4, 0x00a7) AM_READWRITE(irq_status_r,irq_status_w)
+// AM_RANGE(0x00a8, 0x00ab) AM_WRITE(abus_irqack_w)
+// AM_RANGE(0x00b0, 0x00b7) AM_READWRITE(abus_set_r,abus_set_w)
+// AM_RANGE(0x00b8, 0x00bb) AM_READWRITE(abus_refresh_r,abus_refresh_w)
+// AM_RANGE(0x00c4, 0x00c7) AM_READWRITE(sdram_r,sdram_w)
+ AM_RANGE(0x00c8, 0x00cb) AM_READ(version_r) // returns 4 for stock Saturn
+ADDRESS_MAP_END
+
+//-------------------------------------------------
+// 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")
+{
+}
+
+void sega_scu_device::static_set_hostcpu(device_t &device, const char *cputag)
+{
+ sega_scu_device &dev = downcast<sega_scu_device &>(device);
+ dev.m_hostcpu_tag = cputag;
+}
+
+
+//-------------------------------------------------
+// add_device_mconfig - device-specific machine
+// configuration addiitons
+//-------------------------------------------------
+
+static ADDRESS_MAP_START( scudsp_mem, AS_PROGRAM, 32, sega_scu_device )
+ AM_RANGE(0x00, 0xff) AM_RAM
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( scudsp_data, AS_DATA, 32, sega_scu_device )
+ AM_RANGE(0x00, 0xff) AM_RAM
+ADDRESS_MAP_END
+
+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);
+}
+
+MACHINE_CONFIG_MEMBER(sega_scu_device::device_add_mconfig)
+ MCFG_CPU_ADD("scudsp", SCUDSP, XTAL_57_2727MHz/4) // 14 MHz
+ MCFG_CPU_PROGRAM_MAP(scudsp_mem)
+ MCFG_CPU_DATA_MAP(scudsp_data)
+ MCFG_SCUDSP_OUT_IRQ_CB(DEVWRITELINE(DEVICE_SELF, sega_scu_device, scudsp_end_w))
+ MCFG_SCUDSP_IN_DMA_CB(READ16(sega_scu_device, scudsp_dma_r))
+ MCFG_SCUDSP_OUT_DMA_CB(WRITE16(sega_scu_device, scudsp_dma_w))
+MACHINE_CONFIG_END
+
+
+//-------------------------------------------------
+// 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));
+
+ m_hostcpu = machine().device<sh2_device>(m_hostcpu_tag);//machine().device(m_hostcpu_tag);
+ 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;
+ 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);
+ 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);
+ 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 );
+ 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 );
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ else
+ m_ist |= (IRQ_DSP_END);
+
+}
+
+//**************************************************************************
+// Miscellanea
+//**************************************************************************
+
+READ32_MEMBER(sega_scu_device::version_r)
+{
+ return 4; // correct for stock Saturn at least
+}