// 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 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 40 00a0 Interrupt Mask Register 41 00a4 Interrupt Status Register 42 00a8 A-Bus Interrupt Acknowledge 43 00ac 44 00b0 A-Bus Set Register 45 00b4 46 00b8 A-Bus Refresh Register 47 00bc 48 00c0 49 00c4 SCU SDRAM Select Register 50 00c8 SCU Version Register 51 00cc 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 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)); // AM_RANGE(0x0060, 0x0063) AM_WRITE(dma_force_stop_w) map(0x007c, 0x007f).r(FUNC(sega_scu_device::dma_status_r)); map(0x0080, 0x0083).rw("scudsp", FUNC(scudsp_cpu_device::program_control_r), FUNC(scudsp_cpu_device::program_control_w)); map(0x0084, 0x0087).w("scudsp", FUNC(scudsp_cpu_device::program_w)); map(0x0088, 0x008b).w("scudsp", FUNC(scudsp_cpu_device::ram_address_control_w)); map(0x008c, 0x008f).rw("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)); // 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) 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") { } //------------------------------------------------- // 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); } MACHINE_CONFIG_START(sega_scu_device::device_add_mconfig) MCFG_DEVICE_ADD("scudsp", SCUDSP, XTAL(57'272'727)/4) // 14 MHz MCFG_SCUDSP_OUT_IRQ_CB(WRITELINE(DEVICE_SELF, sega_scu_device, scudsp_end_w)) MCFG_SCUDSP_IN_DMA_CB(READ16(*this, sega_scu_device, scudsp_dma_r)) MCFG_SCUDSP_OUT_DMA_CB(WRITE16(*this, 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)); 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_hostcpu = 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; } //------------------------------------------------- // 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); 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 }