// license:BSD-3-Clause // copyright-holders:Phil Stroffolino, David Haywood /* Common code for the later Namco System 21 DSP board 5 TMS320C25 DSPs with custom Namco programming (marked C67) in a 1x Master, 4x Slave configuration used by Star Blade, Cybersled TODO: handle protection properly and with callbacks handle splitting of workload across slaves remove hacks! some of the list processing should probably be in the 3d device, split it out */ #include "emu.h" #include "namcos21_dsp_c67.h" DEFINE_DEVICE_TYPE(NAMCOS21_DSP_C67, namcos21_dsp_c67_device, "namcos21_dsp_c67_device", "Namco System 21 DSP Setup (5x C67 type)") namcos21_dsp_c67_device::namcos21_dsp_c67_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, NAMCOS21_DSP_C67, tag, owner, clock), m_renderer(*this, finder_base::DUMMY_TAG), m_c67master(*this, "dspmaster"), m_c67slave(*this, "dspslave%u", 0U), m_ptrom24(*this,"point24"), m_master_dsp_ram(*this,"master_dsp_ram"), m_gametype(0), m_yield_hack_cb(*this), m_irq_enable(false) { } void namcos21_dsp_c67_device::device_start() { m_dspram16.resize(0x10000/2); // 0x8000 16-bit words std::fill(std::begin(m_dspram16), std::end(m_dspram16), 0x0000); m_yield_hack_cb.resolve_safe(); m_pointram = std::make_unique(PTRAM_SIZE); m_mpDspState = make_unique_clear(); save_item(NAME(m_dspram16)); } void namcos21_dsp_c67_device::device_reset() { m_poly_frame_width = m_renderer->get_width(); m_poly_frame_height = m_renderer->get_height(); /* DSP startup hacks */ m_mbNeedsKickstart = 20; if (m_gametype == NAMCOS21_CYBERSLED) { m_mbNeedsKickstart = 200; } /* Wipe the framebuffers */ m_renderer->swap_and_clear_poly_framebuffer(); m_renderer->swap_and_clear_poly_framebuffer(); //reset_dsps(ASSERT_LINE); m_mpDspState->masterSourceAddr = 0; m_mpDspState->slaveBytesAvailable = 0; m_mpDspState->slaveBytesAdvertised = 0; m_mpDspState->slaveInputStart = 0; m_mpDspState->slaveOutputSize = 0; m_mpDspState->masterDirectDrawSize = 0; m_mpDspState->masterFinished = 0; m_mpDspState->slaveActive = 0; m_pointram_idx = 0; m_pointram_control = 0; m_pointrom_idx = 0; m_mPointRomMSB = 0; m_mbPointRomDataAvailable = 0; m_irq_enable = 0; // clear these? //m_depthcue[2][0x400]; //m_pointram //m_mpDspState->slaveInputBuffer[DSP_BUF_MAX]; //m_mpDspState->slaveOutputBuffer[DSP_BUF_MAX]; //m_mpDspState->masterDirectDrawBuffer[256]; } void namcos21_dsp_c67_device::reset_dsps(int state) { if (m_c67master) m_c67master->set_input_line(INPUT_LINE_RESET, state); if (m_c67slave[0]) m_c67slave[0]->set_input_line(INPUT_LINE_RESET, state); } void namcos21_dsp_c67_device::reset_kickstart() { //printf( "dspkick=0x%x\n", data ); namcos21_kickstart_hacks(1); } void namcos21_dsp_c67_device::device_add_mconfig(machine_config &config) { namco_c67_device& dspmaster(NAMCO_C67(config, m_c67master, 24000000)); /* 24 MHz? overclocked */ dspmaster.set_addrmap(AS_PROGRAM, &namcos21_dsp_c67_device::master_dsp_program); dspmaster.set_addrmap(AS_DATA, &namcos21_dsp_c67_device::master_dsp_data); dspmaster.set_addrmap(AS_IO, &namcos21_dsp_c67_device::master_dsp_io); dspmaster.hold_in_cb().set_constant(0); dspmaster.hold_ack_out_cb().set_nop(); dspmaster.xf_out_cb().set(FUNC(namcos21_dsp_c67_device::dsp_xf_w)); for (int i = 0; i < 4; i++) { namco_c67_device& dspslave(NAMCO_C67(config, m_c67slave[i], 24000000)); /* 24 MHz? overclocked */ dspslave.set_addrmap(AS_PROGRAM, &namcos21_dsp_c67_device::slave_dsp_program); dspslave.set_addrmap(AS_DATA, &namcos21_dsp_c67_device::slave_dsp_data); dspslave.set_addrmap(AS_IO, &namcos21_dsp_c67_device::slave_dsp_io); dspslave.hold_in_cb().set_constant(0); dspslave.hold_ack_out_cb().set_nop(); dspslave.xf_out_cb().set(FUNC(namcos21_dsp_c67_device::slave_XF_output_w)); // the emulation currently only uses one slave DSP clocked at 4x the normal rate instead of the master splitting the workload across the 4 slaves if (i!=0) dspslave.set_disable(); else dspslave.set_clock(24000000*4); } } WRITE16_MEMBER(namcos21_dsp_c67_device::dspcuskey_w) { /* TODO: proper cuskey emulation */ } READ16_MEMBER(namcos21_dsp_c67_device::dspcuskey_r) { uint16_t result = 0; if( m_gametype == NAMCOS21_SOLVALOU ) { switch( m_c67master->pc() ) { case 0x805e: result = 0x0000; break; case 0x805f: result = 0xfeba; break; case 0x8067: result = 0xffff; break; case 0x806e: result = 0x0145; break; default: logerror( "unk cuskey_r; pc=0x%x\n", m_c67master->pc() ); break; } } else if( m_gametype == NAMCOS21_CYBERSLED ) { switch( m_c67master->pc() ) { case 0x8061: result = 0xfe95; break; case 0x8069: result = 0xffff; break; case 0x8070: result = 0x016A; break; default: break; } } else if( m_gametype == NAMCOS21_AIRCOMBAT ) { switch( m_c67master->pc() ) { case 0x8062: result = 0xfeb9; break; case 0x806a: result = 0xffff; break; case 0x8071: result = 0x0146; break; default: break; } } return result; } void namcos21_dsp_c67_device::transmit_word_to_slave(uint16_t data) { unsigned offs = m_mpDspState->slaveInputStart+m_mpDspState->slaveBytesAvailable++; m_mpDspState->slaveInputBuffer[offs%DSP_BUF_MAX] = data; if (ENABLE_LOGGING) logerror( "+%04x(#%04x)\n", data, m_mpDspState->slaveBytesAvailable ); m_mpDspState->slaveActive = 1; if( m_mpDspState->slaveBytesAvailable >= DSP_BUF_MAX ) { fatalerror( "IDC overflow\n" ); } } void namcos21_dsp_c67_device::transfer_dsp_data() { uint16_t addr = m_mpDspState->masterSourceAddr; int mode = addr&0x8000; addr&=0x7fff; if( addr ) { for(;;) { int i; uint16_t old = addr; uint16_t code = m_dspram16[addr++]; if( code == 0xffff ) { if( mode ) { addr = m_dspram16[addr]; m_mpDspState->masterSourceAddr = addr; if (ENABLE_LOGGING) logerror( "LOOP:0x%04x\n", addr ); addr&=0x7fff; if( old==addr ) { return; } } else { m_mpDspState->masterSourceAddr = 0; return; } } else if( mode==0 ) { /* direct data transfer */ if (ENABLE_LOGGING) logerror( "DATA TFR(0x%x)\n", code ); transmit_word_to_slave(code); for( i=0; i2 ) { transmit_word_to_slave(0); /* pad1 */ transmit_word_to_slave(len+1); for( i=0; iswap_and_clear_poly_framebuffer(); m_mpDspState->masterSourceAddr = 0; m_mpDspState->slaveOutputSize = 0; m_mpDspState->masterFinished = 0; m_mpDspState->slaveActive = 0; m_c67master->set_input_line(0, HOLD_LINE); m_c67slave[0]->pulse_input_line(INPUT_LINE_RESET, attotime::zero); } uint16_t namcos21_dsp_c67_device::read_word_from_slave_input() { uint16_t data = 0; if( m_mpDspState->slaveBytesAvailable>0 ) { data = m_mpDspState->slaveInputBuffer[m_mpDspState->slaveInputStart++]; m_mpDspState->slaveInputStart %= DSP_BUF_MAX; m_mpDspState->slaveBytesAvailable--; if( m_mpDspState->slaveBytesAdvertised>0 ) { m_mpDspState->slaveBytesAdvertised--; } if (ENABLE_LOGGING) logerror( "%s:-%04x(0x%04x)\n", machine().describe_context(), data, m_mpDspState->slaveBytesAvailable ); } return data; } uint16_t namcos21_dsp_c67_device::get_input_bytes_advertised_for_slave() { if( m_mpDspState->slaveBytesAdvertised < m_mpDspState->slaveBytesAvailable ) { m_mpDspState->slaveBytesAdvertised++; } else if( m_mpDspState->slaveActive && m_mpDspState->masterFinished && m_mpDspState->masterSourceAddr ) { namcos21_kickstart_hacks(0); } return m_mpDspState->slaveBytesAdvertised; } READ16_MEMBER(namcos21_dsp_c67_device::dspram16_r) { return m_dspram16[offset]; } WRITE16_MEMBER(namcos21_dsp_c67_device::dspram16_hack_w) { COMBINE_DATA(&m_dspram16[offset]); if (m_mpDspState->masterSourceAddr && offset == 1 + (m_mpDspState->masterSourceAddr & 0x7fff)) { if (ENABLE_LOGGING) logerror("IDC-CONTINUE\n"); transfer_dsp_data(); } else if (m_gametype == NAMCOS21_SOLVALOU && offset == 0x103) { // HACK: synchronization for solvalou - is this really needed? m_yield_hack_cb(1); } } WRITE16_MEMBER(namcos21_dsp_c67_device::dspram16_w) { COMBINE_DATA(&m_dspram16[offset]); if (m_mpDspState->masterSourceAddr && offset == 1 + (m_mpDspState->masterSourceAddr & 0x7fff)) { if (ENABLE_LOGGING) logerror("IDC-CONTINUE\n"); transfer_dsp_data(); } } /***********************************************************/ int32_t namcos21_dsp_c67_device::read_pointrom_data(unsigned offset) { return m_ptrom24[offset]; } READ16_MEMBER(namcos21_dsp_c67_device::dsp_port0_r) { int32_t data = read_pointrom_data(m_pointrom_idx++); m_mPointRomMSB = (uint8_t)(data>>16); m_mbPointRomDataAvailable = 1; return (uint16_t)data; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_port0_w) { /* unused? */ if (ENABLE_LOGGING) logerror( "PTRAM_LO(0x%04x)\n", data ); } READ16_MEMBER(namcos21_dsp_c67_device::dsp_port1_r) { if( m_mbPointRomDataAvailable ) { m_mbPointRomDataAvailable = 0; return m_mPointRomMSB; } return 0x8000; /* IDC ack? */ } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_port1_w) { /* unused? */ if (ENABLE_LOGGING) logerror( "PTRAM_HI(0x%04x)\n", data ); } READ16_MEMBER(namcos21_dsp_c67_device::dsp_port2_r) { /* IDC TRANSMIT ENABLE? */ return 0; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_port2_w) { if (ENABLE_LOGGING) logerror( "IDC ADDR INIT(0x%04x)\n", data ); m_mpDspState->masterSourceAddr = data; transfer_dsp_data(); } READ16_MEMBER(namcos21_dsp_c67_device::dsp_port3_idc_rcv_enable_r) { /* IDC RECEIVE ENABLE? */ return 0; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_port3_w) { m_pointrom_idx<<=16; m_pointrom_idx|=data; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_port4_w) { /* receives $0B<<4 prior to IDC setup */ } READ16_MEMBER(namcos21_dsp_c67_device::dsp_port8_r) { /* SMU status */ return 1; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_port8_w) { if (ENABLE_LOGGING) logerror( "port8_w(%d)\n", data ); if( data ) { m_mpDspState->masterFinished = 1; } m_irq_enable = data; } READ16_MEMBER(namcos21_dsp_c67_device::dsp_port9_r) { /* render-device-busy; used for direct-draw */ return 0; } READ16_MEMBER(namcos21_dsp_c67_device::dsp_porta_r) { /* config */ return 0; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_porta_w) { /* boot: 1 */ /* IRQ0 end: 0 */ /* INT2 begin: 1 */ /* direct-draw begin: 0 */ /* INT1 begin: 1 */ // if (ENABLE_LOGGING) logerror( "dsp_porta_w(0x%04x)\n", data ); } READ16_MEMBER(namcos21_dsp_c67_device::dsp_portb_r) { /* config */ return 1; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_portb_w) { if( data==0 ) { /* only 0->1 transition triggers */ return; } if( m_mpDspState->masterDirectDrawSize == 13 ) { int i; int sx[4], sy[4], zcode[4]; int color = m_mpDspState->masterDirectDrawBuffer[0]; for( i=0; i<4; i++ ) { sx[i] = m_poly_frame_width/2 + (int16_t)m_mpDspState->masterDirectDrawBuffer[i*3+1]; sy[i] = m_poly_frame_height/2 + (int16_t)m_mpDspState->masterDirectDrawBuffer[i*3+2]; zcode[i] = m_mpDspState->masterDirectDrawBuffer[i*3+3]; } if( color&0x8000 ) { m_renderer->draw_quad(sx, sy, zcode, color); } else { logerror( "indirection used w/ direct draw?\n" ); } } else if( m_mpDspState->masterDirectDrawSize ) { logerror( "unexpected masterDirectDrawSize=%d!\n",m_mpDspState->masterDirectDrawSize ); } m_mpDspState->masterDirectDrawSize = 0; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_portc_w) { if( m_mpDspState->masterDirectDrawSize < DSP_BUF_MAX ) { m_mpDspState->masterDirectDrawBuffer[m_mpDspState->masterDirectDrawSize++] = data; } else { logerror( "portc overflow\n" ); } } READ16_MEMBER(namcos21_dsp_c67_device::dsp_portf_r) { /* informs BIOS that this is Master DSP */ return 0; } WRITE16_MEMBER(namcos21_dsp_c67_device::dsp_xf_w) { if (ENABLE_LOGGING) logerror("xf(%d)\n",data); } void namcos21_dsp_c67_device::master_dsp_program(address_map &map) { map(0x8000, 0xbfff).ram().share("master_dsp_ram"); } void namcos21_dsp_c67_device::master_dsp_data(address_map &map) { map(0x2000, 0x200f).rw(FUNC(namcos21_dsp_c67_device::dspcuskey_r), FUNC(namcos21_dsp_c67_device::dspcuskey_w)); map(0x8000, 0xffff).rw(FUNC(namcos21_dsp_c67_device::dspram16_r), FUNC(namcos21_dsp_c67_device::dspram16_w)); /* 0x8000 words */ } void namcos21_dsp_c67_device::master_dsp_io(address_map &map) { map(0x00, 0x00).rw(FUNC(namcos21_dsp_c67_device::dsp_port0_r), FUNC(namcos21_dsp_c67_device::dsp_port0_w)); map(0x01, 0x01).rw(FUNC(namcos21_dsp_c67_device::dsp_port1_r), FUNC(namcos21_dsp_c67_device::dsp_port1_w)); map(0x02, 0x02).rw(FUNC(namcos21_dsp_c67_device::dsp_port2_r), FUNC(namcos21_dsp_c67_device::dsp_port2_w)); map(0x03, 0x03).rw(FUNC(namcos21_dsp_c67_device::dsp_port3_idc_rcv_enable_r), FUNC(namcos21_dsp_c67_device::dsp_port3_w)); map(0x04, 0x04).w(FUNC(namcos21_dsp_c67_device::dsp_port4_w)); map(0x08, 0x08).rw(FUNC(namcos21_dsp_c67_device::dsp_port8_r), FUNC(namcos21_dsp_c67_device::dsp_port8_w)); map(0x09, 0x09).r(FUNC(namcos21_dsp_c67_device::dsp_port9_r)); map(0x0a, 0x0a).rw(FUNC(namcos21_dsp_c67_device::dsp_porta_r), FUNC(namcos21_dsp_c67_device::dsp_porta_w)); map(0x0b, 0x0b).rw(FUNC(namcos21_dsp_c67_device::dsp_portb_r), FUNC(namcos21_dsp_c67_device::dsp_portb_w)); map(0x0c, 0x0c).w(FUNC(namcos21_dsp_c67_device::dsp_portc_w)); map(0x0f, 0x0f).r(FUNC(namcos21_dsp_c67_device::dsp_portf_r)); } /************************************************************************************/ void namcos21_dsp_c67_device::render_slave_output(uint16_t data) { if( m_mpDspState->slaveOutputSize >= 4096 ) { fatalerror( "SLAVE OVERFLOW (0x%x)\n",m_mpDspState->slaveOutputBuffer[0] ); } /* append word to slave output buffer */ m_mpDspState->slaveOutputBuffer[m_mpDspState->slaveOutputSize++] = data; { uint16_t *pSource = m_mpDspState->slaveOutputBuffer; uint16_t count = *pSource++; if( count && m_mpDspState->slaveOutputSize > count ) { uint16_t color = *pSource++; int sx[4], sy[4],zcode[4]; int j; if( color&0x8000 ) { if( count!=13 ) logerror( "?!direct-draw(%d)\n", count ); for( j=0; j<4; j++ ) { sx[j] = m_poly_frame_width/2 + (int16_t)pSource[3*j+0]; sy[j] = m_poly_frame_height/2 + (int16_t)pSource[3*j+1]; zcode[j] = pSource[3*j+2]; } m_renderer->draw_quad(sx, sy, zcode, color&0x7fff); } else { int quad_idx = color*6; for(;;) { uint8_t code = m_pointram[quad_idx++]; color = m_pointram[quad_idx++]|(code<<8); for( j=0; j<4; j++ ) { uint8_t vi = m_pointram[quad_idx++]; sx[j] = m_poly_frame_width/2 + (int16_t)pSource[vi*3+0]; sy[j] = m_poly_frame_height/2 + (int16_t)pSource[vi*3+1]; zcode[j] = pSource[vi*3+2]; } m_renderer->draw_quad(sx, sy, zcode, color&0x7fff); if( code&0x80 ) { /* end-of-quadlist marker */ break; } } } m_mpDspState->slaveOutputSize = 0; } else if( count==0 ) { fatalerror( "RenderSlaveOutput\n" ); } } } READ16_MEMBER(namcos21_dsp_c67_device::slave_port0_r) { return read_word_from_slave_input(); } WRITE16_MEMBER(namcos21_dsp_c67_device::slave_port0_w) { render_slave_output(data); } READ16_MEMBER(namcos21_dsp_c67_device::slave_port2_r) { return get_input_bytes_advertised_for_slave(); } READ16_MEMBER(namcos21_dsp_c67_device::slave_port3_r) { /* render-device queue size */ /* up to 0x1fe bytes? * slave blocks until free &space exists */ return 0; } WRITE16_MEMBER(namcos21_dsp_c67_device::slave_port3_w) { /* 0=busy, 1=ready? */ } WRITE16_MEMBER(namcos21_dsp_c67_device::slave_XF_output_w) { if (ENABLE_LOGGING) logerror( "%s :slaveXF(%d)\n", machine().describe_context(), data ); } READ16_MEMBER(namcos21_dsp_c67_device::slave_portf_r) { /* informs BIOS that this is Slave DSP */ return 1; } void namcos21_dsp_c67_device::slave_dsp_program(address_map &map) { map(0x8000, 0x8fff).ram(); } void namcos21_dsp_c67_device::slave_dsp_data(address_map &map) { /* no external data memory */ } void namcos21_dsp_c67_device::slave_dsp_io(address_map &map) { map(0x00, 0x00).rw(FUNC(namcos21_dsp_c67_device::slave_port0_r), FUNC(namcos21_dsp_c67_device::slave_port0_w)); map(0x02, 0x02).r(FUNC(namcos21_dsp_c67_device::slave_port2_r)); map(0x03, 0x03).rw(FUNC(namcos21_dsp_c67_device::slave_port3_r), FUNC(namcos21_dsp_c67_device::slave_port3_w)); map(0x0f, 0x0f).r(FUNC(namcos21_dsp_c67_device::slave_portf_r)); } /************************************************************************************/ /** * 801f->800f : prepare for master access to point ram * 801f : done * * #bits data line * 8 1a0 4 * 7 0f8 4 * 7 0ff 4 * 1 001 4 * 7 00a 2 * a 0fe 8 * * line #bits data * 0003 000A 000004FE * 0001 0007 0000000A * 0002 001A 03FFF1A0 */ WRITE16_MEMBER(namcos21_dsp_c67_device::pointram_control_w) { // uint16_t prev = m_pointram_control; COMBINE_DATA( &m_pointram_control ); /* m_pointram_control&0x20 : bank for depthcue data */ #if 0 logerror( "%s dsp_control_w:[%x]:=%04x ", machine().describe_context(), offset, m_pointram_control ); uint16_t delta = (prev^m_pointram_control)&m_pointram_control; if( delta&0x10 ) { logerror( " [reset]" ); } if( delta&2 ) { logerror( " send(A)%x", m_pointram_control&1 ); } if( delta&4 ) { logerror( " send(B)%x", m_pointram_control&1 ); } if( delta&8 ) { logerror( " send(C)%x", m_pointram_control&1 ); } logerror( "\n" ); #endif m_pointram_idx = 0; /* HACK */ } READ16_MEMBER(namcos21_dsp_c67_device::pointram_data_r) { return m_pointram[m_pointram_idx]; } WRITE16_MEMBER(namcos21_dsp_c67_device::pointram_data_w) { if( ACCESSING_BITS_0_7 ) { // if( (m_pointram_idx%6)==0 ) logerror("\n" ); // logerror( " %02x", data ); m_pointram[m_pointram_idx++] = data; m_pointram_idx &= (PTRAM_SIZE-1); } } READ16_MEMBER(namcos21_dsp_c67_device::namcos21_depthcue_r) { int bank = (m_pointram_control&0x20)?1:0; return m_depthcue[bank][offset]; } WRITE16_MEMBER(namcos21_dsp_c67_device::namcos21_depthcue_w) { if( ACCESSING_BITS_0_7 ) { int bank = (m_pointram_control&0x20)?1:0; m_depthcue[bank][offset] = data; // if( (offset&0xf)==0 ) logerror( "\n depthcue: " ); // logerror( " %02x", data ); } }