diff options
Diffstat (limited to 'src/emu/cpu/scudsp/scudsp.c')
-rw-r--r-- | src/emu/cpu/scudsp/scudsp.c | 620 |
1 files changed, 587 insertions, 33 deletions
diff --git a/src/emu/cpu/scudsp/scudsp.c b/src/emu/cpu/scudsp/scudsp.c index ccd331c3cf4..1f4cc455ccb 100644 --- a/src/emu/cpu/scudsp/scudsp.c +++ b/src/emu/cpu/scudsp/scudsp.c @@ -1,8 +1,98 @@ /***************************************************************************** * - * SCUDSP CPU core + * scudsp.c + * Sega SCUDSP emulator version 1.00 + * + * copyright Angelo Salese & Mariusz Wojcieszek, all rights reserved + * + * - This source code is released as freeware for non-commercial purposes. + * - You are free to use and redistribute this code in modified or + * unmodified form, provided you list me in the credits. + * - If you modify this source code, you must add a notice to each modified + * source file that it has been changed. If you're a nice person, you + * will clearly mark each change too. :) + * - If you wish to use this for commercial purposes, please contact me at + * lordkale@libero.it or <insert_marusz_wojcieszek_mail_here> + * - This entire notice must remain in the source code. + * + * + * Changelog: + * 131010: Angelo Salese + * - Converted to CPU structure + * + * 110807: Angelo Salese + * - Allow the Program Counter to be read-backable from SH-2, needed by Virtua Fighter to not + * get stuck on "round 1" announcement; + * + * 110806: Angelo Salese + * - Allows reading from non-work ram h areas; + * - Fixed DMA add values; + * - Fixed a MVI condition shift flag bug, now Sega Saturn produces sound during splash screen; + * - Removed left-over IRQ; + * + * 110722: Angelo Salese + * - Added DSP IRQ command, tested with "The King of Boxing" + * + * 110527: Angelo Salese + * - Fixed incorrectly setted execute flag clearance, allows animation of the Sega Saturn + * splash screen; + * + * 051129: Mariusz Wojcieszek + * - Fixed parallel instructions which increment CT registers to update CT register only + * once, after dsp operation is finished. This fixes instructions like + * MOV MC0,X MOV MC0,Y used by vfremix + * - Changed ALU 32bit instructions to not sign extend their result when loaded to ALU. + * This matches Sega's dspsim behaviour. + * - Changed DMA addnumber handling to match Sega's dspsim. + * + * 050813: Mariusz Wojcieszek + * - Fixed add number in DSP DMA + * + * 050412: Angelo Salese + * - Fixed the T0F behaviour in the DMA operation,it was causing an hang in Treasure Hunt + * due of that. + * - Removed the dsp.log file creation when you are not using the debug build + * + * 041114: Angelo Salese + * - Finished flags in ALU opcodes + * - SR opcode: MSB does not change. + * + * 040328: Mariusz Wojcieszek + * - rewritten ALU and MUL operations using signed arithmetics + * - improved DMA + * - fixed MOV ALH,x + * + * 031211: Mariusz Wojcieszek + * - result of ALU command is stored into ALU register + * - X-Bus command: MOV [s],X can be executed in parallel to other X-Bus commands + * - Y-Bus command: MOV [s],Y can be executed in parallel to other Y-Bus commands + * - Jump and LPS/BTM support: + * jump addresses are absolute, + * prefetched instructions are executed before jump is taken + * - after each instruction, X and Y is multiplied and contents are loaded into MUL register + * - fixed RL8 + * - fixed MVI + * - flags computation in MVI and JMP is partly guessed (because of errors in docs) + * - added reading DSP mem from SH2 side + * - overworked disassembler + * + * TODO: + * - Add control flags + * - Croc: has a bug somewhere that never allows it to trip the ENDI opcode. + * Snippet of interest is: + * 08 00823500 CLR A MOV M0,PL + * 09 08040000 OR MOV ALU,A + * 0A D208000D JMP NZ,$D + * 0B 00000000 NOP + * 0C F8000000 ENDI + * + * 40 00863502 MOV M0,A MOV M2,PL + * 41 10003009 ADD MOV ALL,MC0 + * 42 D3400042 JMP T0,$42 + * 43 00000000 NOP + * 44 D0000007 JMP $7 + * * - * skeleton for now ... * *****************************************************************************/ @@ -17,21 +107,50 @@ const device_type SCUDSP = &device_creator<scudsp_cpu_device>; #define PRF m_flags & 0x04000000 #define EPF m_flags & 0x02000000 #define T0F m_flags & 0x00800000 -#define SF m_flags & 0x00400000 -#define ZF m_flags & 0x00200000 +#define SF (m_flags & 0x00400000) +#define ZF (m_flags & 0x00200000) #define CF m_flags & 0x00100000 #define VF m_flags & 0x00080000 #define EF m_flags & 0x00040000 #define ESF m_flags & 0x00020000 #define EXF m_flags & 0x00010000 // execute flag (basically tied to RESET pin) #define LEF m_flags & 0x00008000 // change PC value +#define T0F_1 m_flags|=0x00800000 +#define T0F_0 m_flags&=~0x00800000 +#define EXF_0 m_flags&=~0x00010000 +#define EF_1 m_flags|=0x00040000 + +#define SET_C(_val) (m_flags = ((m_flags & ~0x00100000) | ((_val) ? 0x00100000 : 0))) +#define SET_S(_val) (m_flags = ((m_flags & ~0x00400000) | ((_val) ? 0x00400000 : 0))) +#define SET_Z(_val) (m_flags = ((m_flags & ~0x00200000) | ((_val) ? 0x00200000 : 0))) +#define SET_V(_val) (m_flags = ((m_flags & ~0x00080000) | ((_val) ? 0x00080000 : 0))) + #define FLAGS_MASK 0x06ff8000 #define scudsp_readop(A) m_program->read_dword(A << 2) #define scudsp_writeop(A, B) m_program->write_dword(A << 2, B) -#define scudsp_readmem(A,MD) m_data->read_dword((A | MD << 6) << 2) -#define scudsp_writemem(A,MD,B) m_data->write_dword((A | MD << 6) << 2, B) +#define scudsp_readmem(A,MD) m_data->read_dword(((A | MD) << 6) << 2) +#define scudsp_writemem(A,MD,B) m_data->write_dword(((A | MD) << 6) << 2, B) + +UINT32 scudsp_cpu_device::scudsp_get_source_mem_reg_value( UINT32 mode ) +{ + if ( mode < 0x8 ) + { + return scudsp_get_source_mem_value( mode ); + } + else + { + switch( mode ) + { + case 0x9: + return (UINT32)((m_alu & U64(0x00000000ffffffff)) >> 0); + case 0xA: + return (UINT32)((m_alu & U64(0x0000ffffffff0000)) >> 16); + } + } + return 0; +} UINT32 scudsp_cpu_device::scudsp_get_source_mem_value(UINT8 mode) { @@ -92,45 +211,121 @@ void scudsp_cpu_device::scudsp_set_dest_mem_reg( UINT32 mode, UINT32 value ) scudsp_writemem(m_ct3++,3,value); m_ct3 &= 0x3f; break; -#if 0 case 0x4: /* RX */ - dsp_reg.rx.ui = value; - update_mul = 1; + m_rx.ui = value; break; case 0x5: /* PL */ - dsp_reg.pl.ui = value; - dsp_reg.ph.si = (dsp_reg.pl.si < 0) ? -1 : 0; + m_pl.ui = value; + m_ph.si = (m_pl.si < 0) ? -1 : 0; break; case 0x6: /* RA0 */ - dsp_reg.ra0 = value; + m_ra0 = value; break; case 0x7: /* WA0 */ - dsp_reg.wa0 = value; + m_wa0 = value; break; case 0x8: case 0x9: /* ??? */ break; case 0xa: /* LOP */ - dsp_reg.lop = value; + m_lop = value; break; case 0xb: /* TOP */ - dsp_reg.top = value; + m_top = value; break; case 0xc: /* CT0 */ - dsp_reg.ct0 = value & 0x3f; + m_ct0 = value & 0x3f; break; case 0xd: /* CT1 */ - dsp_reg.ct1 = value & 0x3f; + m_ct1 = value & 0x3f; break; case 0xe: /* CT2 */ - dsp_reg.ct2 = value & 0x3f; + m_ct2 = value & 0x3f; break; case 0xf: /* CT3 */ - dsp_reg.ct3 = value & 0x3f; + m_ct3 = value & 0x3f; + break; + } +} + +void scudsp_cpu_device::scudsp_set_dest_mem_reg_2( UINT32 mode, UINT32 value ) +{ + if ( mode < 0xb ) + { + scudsp_set_dest_mem_reg( mode, value ); + } + else + { + switch( mode ) + { + case 0xc: /* PC */ + m_delay = m_pc; /* address next after this command will be executed twice */ + m_top = m_pc; + m_pc = value; + break; + } + } +} + +UINT32 scudsp_cpu_device::scudsp_compute_condition( UINT32 condition ) +{ + UINT32 result = 0; + + switch( condition & 0xf ) + { + case 0x1: /* Z */ + result = ZF; break; -#endif + case 0x2: /* S */ + result = SF; + break; + case 0x3: /* ZS */ + result = ZF | SF; + break; + case 0x4: /* C */ + result = CF; + break; + case 0x8: /* T0 */ + result = T0F; + break; + } + if ( !(condition & 0x20) ) + { + result = !result; } + + return result; +} + +void scudsp_cpu_device::scudsp_set_dest_dma_mem( UINT32 memcode, UINT32 value, UINT32 counter ) +{ + if ( memcode < 4 ) + { + scudsp_set_dest_mem_reg( memcode, value ); + } + else if ( memcode == 4 ) + { + fatalerror("scudsp_set_dest_dma_mem == 4"); + /* caused a stack overflow for sure ... */ + //dsp_reg.internal_prg[ counter & 0x100 ] = value; + } +} + +UINT32 scudsp_cpu_device::scudsp_get_mem_source_dma( UINT32 memcode, UINT32 counter ) +{ + switch( memcode & 0x3 ) + { + case 0x0: + return scudsp_readmem(((m_ct0 + counter) & 0x3f),0); + case 0x1: + return scudsp_readmem(((m_ct1 + counter) & 0x3f),1); + case 0x2: + return scudsp_readmem(((m_ct2 + counter) & 0x3f),2); + case 0x3: + return scudsp_readmem(((m_ct3 + counter) & 0x3f),3); + } + return 0; } @@ -190,12 +385,300 @@ WRITE32_MEMBER( scudsp_cpu_device::ram_address_w ) scudsp_set_dest_mem_reg( (m_ra & 0xc0) >> 6, data ); } -/*********************************** - * illegal opcodes - ***********************************/ -void scudsp_cpu_device::scudsp_illegal() +void scudsp_cpu_device::scudsp_operation(UINT32 opcode) +{ + INT64 i1,i2; + INT32 i3; + int update_ct[4] = {0,0,0,0}; + int dsp_mem; + + + /* ALU */ + switch( (opcode & 0x3c000000) >> 26 ) + { + case 0x0: /* NOP */ + break; + case 0x1: /* AND */ + i3 = m_acl.si & m_pl.si; + m_alu = (UINT64)(UINT32)i3; + SET_Z(i3 == 0); + SET_C(0); + SET_S(i3 < 0); + break; + case 0x2: /* OR */ + i3 = m_acl.si | m_pl.si; + m_alu = (UINT64)(UINT32)i3; + SET_C(0); + SET_S(i3 < 0); + /* TODO: Croc and some early Psygnosis games wants Z to be 1 when the result of this one is negative. + Needs HW tests ... */ + if(i3 < 0) + i3 = 0; + SET_Z(i3 == 0); + break; + case 0x3: /* XOR */ + i3 = m_acl.si ^ m_pl.si; + m_alu = (UINT64)(UINT32)i3; + SET_Z(i3 == 0); + SET_C(0); + SET_S(i3 < 0); + break; + case 0x4: /* ADD */ + i3 = m_acl.si + m_pl.si; + m_alu = (UINT64)(UINT32)i3; + //SET_Z(i3 == 0); + SET_Z( (i3 & S64(0xffffffffffff)) == 0 ); + //SET_S(i3 < 0); + SET_S( i3 & S64(0x1000000000000)); + SET_C(i3 & S64(0x100000000)); + SET_V(((i3) ^ (m_acl.si)) & ((i3) ^ (m_pl.si)) & 0x80000000); + break; + case 0x5: /* SUB */ + i3 = m_acl.si - m_pl.si; + m_alu = (UINT64)(UINT32)i3; + SET_Z(i3 == 0); + SET_C(i3 & S64(0x100000000)); + SET_S(i3 < 0); + SET_V(((m_pl.si) ^ (m_acl.si)) & ((m_pl.si) ^ (i3)) & 0x80000000); + break; + case 0x6: /* AD2 */ + i1 = CONCAT_64((INT32)m_ph.si,m_pl.si); + i2 = CONCAT_64((INT32)m_ach.si,m_acl.si); + m_alu = i1 + i2; + SET_Z((m_alu & S64(0xffffffffffff)) == 0); + SET_S((m_alu & S64(0x800000000000)) > 0); + SET_C((m_alu) & S64(0x1000000000000)); + SET_V(((m_alu) ^ (i1)) & ((m_alu) ^ (i2)) & S64(0x800000000000)); + break; + case 0x7: /* ??? */ + /* Unrecognized opcode */ + break; + case 0x8: /* SR */ + i3 = (m_acl.si >> 1) | (m_acl.si & 0x80000000);/*MSB does not change*/ + m_alu = (UINT64)(UINT32)i3; + SET_Z(i3 == 0); + SET_S(i3 < 0); + SET_C(m_acl.ui & 0x80000000); + break; + case 0x9: /* RR */ + i3 = ((m_acl.ui >> 1) & 0x7fffffff) | ((m_acl.ui << 31) & 0x80000000); + m_alu = (UINT64)(UINT32)i3; + SET_Z( i3 == 0 ); + SET_S( i3 < 0 ); + SET_C( m_acl.ui & 0x1 ); + break; + case 0xa: /* SL */ + i3 = m_acl.si << 1; + m_alu = (UINT64)(UINT32)i3; + SET_Z( i3 == 0 ); + SET_S( i3 < 0 ); + SET_C( m_acl.ui & 0x80000000 ); + break; + case 0xB: /* RL */ + i3 = ((m_acl.si << 1) & 0xfffffffe) | ((m_acl.si >> 31) & 0x1); + m_alu = (UINT64)(UINT32)i3; + SET_Z( i3 == 0 ); + SET_S( i3 < 0 ); + SET_C( m_acl.ui & 0x80000000 ); + break; + case 0xc: + case 0xd: + case 0xe: + /* Unrecognized opcode */ + break; + case 0xF: /* RL8 */ + i3 = ((m_acl.si << 8) & 0xffffff00) | ((m_acl.si >> 24) & 0xff); + m_alu = i3; + SET_Z( i3 == 0 ); + SET_S( i3 < 0 ); + SET_C( m_acl.si & 0x01000000 ); + break; + } + + /* X-Bus */ + if ( opcode & 0x2000000 ) + { + /* MOV [s],X */ + dsp_mem = (opcode & 0x700000) >> 20; + if ( dsp_mem & 4 ) + { + dsp_mem &= 3; + update_ct[dsp_mem] = 1; + } + m_rx.ui = scudsp_get_source_mem_value( dsp_mem ); + m_update_mul = 1; + } + switch( (opcode & 0x1800000) >> 23 ) + { + case 0x0: /* NOP */ + case 0x1: /* NOP ? */ + break; + case 0x2: /* MOV MUL,P */ + m_ph.ui = (UINT16)((m_mul & U64(0x0000ffff00000000)) >> 32); + m_pl.ui = (UINT32)((m_mul & U64(0x00000000ffffffff)) >> 0); + break; + case 0x3: /* MOV [s],P */ + dsp_mem = (opcode & 0x700000) >> 20; + if ( dsp_mem & 4 ) + { + dsp_mem &= 3; + update_ct[dsp_mem] = 1; + } + m_pl.ui = scudsp_get_source_mem_value( dsp_mem ); + m_ph.si = (m_pl.si < 0) ? -1 : 0; + break; + } + + /* Y-Bus */ + if ( opcode & 0x80000 ) + { + /* MOV [s],Y */ + dsp_mem = (opcode & 0x1C000 ) >> 14; + if (dsp_mem & 4) + { + dsp_mem &= 3; + update_ct[dsp_mem] = 1; + } + m_ry.ui = scudsp_get_source_mem_value( dsp_mem ); + m_update_mul = 1; + } + switch( (opcode & 0x60000) >> 17 ) + { + case 0x0: /* NOP */ + break; + case 0x1: /* CLR A */ + m_acl.ui = 0; + m_ach.ui = 0; + break; + case 0x2: /* MOV ALU,A */ + m_ach.ui = (UINT16)((m_alu & U64(0x0000ffff00000000)) >> 32); + m_acl.ui = (UINT32)((m_alu & U64(0x00000000ffffffff)) >> 0); + break; + case 0x3: /* MOV [s], A */ + dsp_mem = (opcode & 0x1C000 ) >> 14; + if (dsp_mem & 4) + { + dsp_mem &= 3; + update_ct[dsp_mem] = 1; + } + m_acl.ui = scudsp_get_source_mem_value( dsp_mem ); + m_ach.si = ((m_acl.si < 0) ? -1 : 0); + break; + } + + /* update CT registers */ + if ( update_ct[0] ) { m_ct0++; m_ct0 &= 0x3f; }; + if ( update_ct[1] ) { m_ct1++; m_ct1 &= 0x3f; }; + if ( update_ct[2] ) { m_ct2++; m_ct2 &= 0x3f; }; + if ( update_ct[3] ) { m_ct3++; m_ct3 &= 0x3f; }; + + + /* D1-Bus */ + switch( (opcode & 0x3000) >> 12 ) + { + case 0x0: /* NOP */ + break; + case 0x1: /* MOV SImm,[d] */ + scudsp_set_dest_mem_reg( (opcode & 0xf00) >> 8, (INT32)(INT8)(opcode & 0xff) ); + break; + case 0x2: + /* ??? */ + break; + case 0x3: /* MOV [s],[d] */ + scudsp_set_dest_mem_reg( (opcode & 0xf00) >> 8, scudsp_get_source_mem_reg_value( opcode & 0xf ) ); + break; + } + + m_icount -= 1; +} + +void scudsp_cpu_device::scudsp_move_immediate( UINT32 opcode ) +{ + UINT32 value; + + if ( opcode & 0x2000000 ) + { + if ( scudsp_compute_condition( (opcode & 0x3F80000 ) >> 19 ) ) + { + value = opcode & 0x7ffff; + if ( value & 0x40000 ) value |= 0xfff80000; + scudsp_set_dest_mem_reg_2( (opcode & 0x3C000000) >> 26, value ); + } + } + else + { + value = opcode & 0x1ffffff; + if ( value & 0x1000000 ) value |= 0xfe000000; + scudsp_set_dest_mem_reg_2( (opcode & 0x3C000000) >> 26, value ); + } + m_icount -= 1; +} + +void scudsp_cpu_device::scudsp_dma( UINT32 opcode ) +{ + m_icount -= 1; +} + +void scudsp_cpu_device::scudsp_jump( UINT32 opcode ) +{ + if ( opcode & 0x3f80000 ) + { + if ( scudsp_compute_condition( (opcode & 0x3f80000) >> 19 ) ) + { + m_delay = m_pc; + m_pc = opcode & 0xff; + } + } + else + { + m_delay = m_pc; + m_pc = opcode & 0xff; + } + + m_icount -= 1; +} + +void scudsp_cpu_device::scudsp_loop(UINT32 opcode) { - //logerror("scudsp illegal opcode at 0x%04x\n", m_pc); + if ( opcode & 0x8000000 ) + { + /* LPS */ + if ( m_lop != 0 ) + { + m_lop--; + m_delay = m_pc; + m_pc--; + } + } + else + { + /* BTM */ + if ( m_lop != 0 ) + { + m_lop--; + m_delay = m_pc; + m_pc = m_top; + } + } + m_icount -= 1; +} + +void scudsp_cpu_device::scudsp_end(UINT32 opcode) +{ + if(opcode & 0x08000000) + { + /*ENDI*/ + //TODO: irq signal + } + + EXF_0; /* END / ENDI */ + set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + m_icount -= 1; +} + +void scudsp_cpu_device::scudsp_illegal(UINT32 opcode) +{ + fatalerror("scudsp illegal opcode at 0x%04x\n", m_pc); m_icount -= 1; } @@ -206,18 +689,58 @@ void scudsp_cpu_device::execute_run() do { - debugger_instruction_hook(this, m_pc); + m_update_mul = 0; - opcode = scudsp_readop(m_pc); - m_pc++; + if ( m_delay ) + { + debugger_instruction_hook(this, m_delay); + opcode = scudsp_readop(m_delay); + m_delay = 0; + } + else + { + debugger_instruction_hook(this, m_pc); + opcode = scudsp_readop(m_pc); + m_pc++; + } - switch( opcode ) + switch( (opcode & 0xc0000000) >> 30 ) { - default: - scudsp_illegal(); + case 0x00: /* 00 */ + scudsp_operation(opcode); + break; + case 0x01: /* 01 */ + scudsp_illegal(opcode); + break; + case 0x02: /* 10 */ + scudsp_move_immediate(opcode); break; + case 0x03: /* 11 */ + switch( (opcode & 0x30000000) >> 28 ) + { + case 0x00: + scudsp_dma(opcode); + break; + case 0x01: + scudsp_jump(opcode); + break; + case 0x02: + scudsp_loop(opcode); + break; + case 0x03: + scudsp_end(opcode); + break; + } + break; + } + + if ( m_update_mul == 1 ) + { + m_mul = (INT64)m_rx.si * (INT64)m_ry.si; + m_update_mul = 0; } + } while( m_icount > 0 ); } @@ -229,16 +752,47 @@ void scudsp_cpu_device::device_start() save_item(NAME(m_pc)); save_item(NAME(m_ra)); + save_item(NAME(m_ct0)); save_item(NAME(m_ct1)); save_item(NAME(m_ct2)); save_item(NAME(m_ct3)); + save_item(NAME(m_flags)); - save_item(NAME(m_reset_state)); + save_item(NAME(m_delay)); + + save_item(NAME(m_top)); + save_item(NAME(m_lop)); + save_item(NAME(m_rx.ui)); + + save_item(NAME(m_mul)); + + save_item(NAME(m_ry.ui)); + + save_item(NAME(m_alu)); + save_item(NAME(m_ph.ui)); + save_item(NAME(m_pl.ui)); + save_item(NAME(m_ach.ui)); + save_item(NAME(m_acl.ui)); + save_item(NAME(m_ra0)); + save_item(NAME(m_wa0)); // Register state for debugger state_add( SCUDSP_PC, "PC", m_pc ).formatstr("%02X"); state_add( SCUDSP_FLAGS, "SR", m_flags ).formatstr("%08X"); + state_add( SCUDSP_DELAY, "DELAY", m_delay ).formatstr("%02X").noshow(); + state_add( SCUDSP_TOP, "TOP", m_top).formatstr("%02X"); + state_add( SCUDSP_LOP, "LOP", m_lop).formatstr("%03X"); + state_add( SCUDSP_RX, "RX", m_rx).formatstr("%08X"); + state_add( SCUDSP_MUL, "MUL", m_mul).formatstr("%012X"); + state_add( SCUDSP_RY, "RY", m_ry).formatstr("%08X"); + state_add( SCUDSP_ALU, "ALU", m_alu).formatstr("%012X"); + state_add( SCUDSP_PH, "PH", m_ph).formatstr("%04X"); + state_add( SCUDSP_PL, "PL", m_pl).formatstr("%08X"); + state_add( SCUDSP_ACH, "ACH", m_ach).formatstr("%04X"); + state_add( SCUDSP_ACL, "ACL", m_acl).formatstr("%08X"); + state_add( SCUDSP_RA0, "RA0", m_ra0).formatstr("%08X"); + state_add( SCUDSP_WA0, "WA0", m_wa0).formatstr("%08X"); state_add( SCUDSP_RA, "RA", m_ra ).formatstr("%02X"); state_add( SCUDSP_CT0, "CT0", m_ct0 ).formatstr("%02X"); state_add( SCUDSP_CT1, "CT1", m_ct1 ).formatstr("%02X"); @@ -260,7 +814,7 @@ void scudsp_cpu_device::execute_set_input(int irqline, int state) switch(irqline) { case SCUDSP_RESET: - m_reset_state = state; + //m_reset_state = state; break; } } |