/* Sega System 32 Protection related functions */ #include "driver.h" #include "segas32.h" /****************************************************************************** ****************************************************************************** Golden Axe 2 (Revenge of Death Adder) ****************************************************************************** ******************************************************************************/ #define xxxx 0x00 static const UINT8 ga2_v25_opcode_table[256] = { xxxx,xxxx,0xEA,xxxx,xxxx,0x8B,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xFA, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0x49,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,0xE8,xxxx,xxxx,0x75,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,xxxx,0x8D,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xBF,xxxx,0x88,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xBC, xxxx,xxxx,xxxx,0x8A,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0x83,xxxx,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xB8,0x26,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xEB, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xB2,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,0xC3,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xB9,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx, xxxx,xxxx,0x8E,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xBE,xxxx,xxxx,xxxx,xxxx }; #undef xxxx static void nec_v25_cpu_decrypt(void) { int i; UINT8 *rom = memory_region(REGION_CPU3); UINT8* decrypted = auto_malloc(0x100000); UINT8* temp = malloc_or_die(0x100000); // set CPU3 opcode base memory_set_decrypted_region(2, 0x00000, 0xfffff, decrypted); // make copy of ROM so original can be overwritten memcpy(temp, rom, 0x10000); for(i = 0; i < 0x10000; i++) { int j = BITSWAP16(i, 14, 11, 15, 12, 13, 4, 3, 7, 5, 10, 2, 8, 9, 6, 1, 0); // normal ROM data with address swap undone rom[i] = temp[j]; // decryped opcodes with address swap undone decrypted[i] = ga2_v25_opcode_table[ temp[j] ]; } memcpy(rom+0xf0000, rom, 0x10000); memcpy(decrypted+0xf0000, decrypted, 0x10000); free(temp); } void decrypt_ga2_protrom(void) { nec_v25_cpu_decrypt(); } WRITE16_HANDLER( ga2_dpram_w ) { /* does it ever actually write.. */ } READ16_HANDLER( ga2_dpram_r ) { return (ga2_dpram[offset])|(ga2_dpram[offset+1]<<8); } #if 0 // simulation READ16_HANDLER(ga2_sprite_protection_r) { static UINT32 prot[16] = { 0x0a, 0, 0xc5, 0, 0x11, 0, 0x11, 0, 0x18, 0, 0x18, 0, 0x1f, 0, 0xc6, 0, }; return prot[offset]; } READ16_HANDLER(ga2_wakeup_protection_r) { static const char *prot = "wake up! GOLDEN AXE The Revenge of Death-Adder! "; return prot[offset]; } #endif /****************************************************************************** ****************************************************************************** Sonic Arcade protection ****************************************************************************** ******************************************************************************/ // This code duplicates the actions of the protection device used in SegaSonic // arcade revision C, allowing the game to run correctly. #define CLEARED_LEVELS 0xE5C4 #define CURRENT_LEVEL 0xF06E #define CURRENT_LEVEL_STATUS 0xF0BC #define LEVEL_ORDER_ARRAY 0x263A WRITE16_HANDLER(sonic_level_load_protection) { UINT16 level; //Perform write system32_workram[CLEARED_LEVELS / 2] = (data & ~mem_mask) | (system32_workram[CLEARED_LEVELS / 2] & mem_mask); //Refresh current level if (system32_workram[CLEARED_LEVELS / 2] == 0) { level = 0x0007; } else { level = *((memory_region(REGION_CPU1) + LEVEL_ORDER_ARRAY) + (system32_workram[CLEARED_LEVELS / 2] * 2) - 1); level |= *((memory_region(REGION_CPU1) + LEVEL_ORDER_ARRAY) + (system32_workram[CLEARED_LEVELS / 2] * 2) - 2) << 8; } system32_workram[CURRENT_LEVEL / 2] = level; //Reset level status system32_workram[CURRENT_LEVEL_STATUS / 2] = 0x0000; system32_workram[(CURRENT_LEVEL_STATUS + 2) / 2] = 0x0000; } /****************************************************************************** ****************************************************************************** Burning Rival ****************************************************************************** ******************************************************************************/ // the protection board on many system32 games has full dma/bus access // and can write things into work RAM. we simulate that here for burning rival. READ16_HANDLER(brival_protection_r) { if (!mem_mask) // only trap on word-wide reads { switch (offset) { case 0: case 2: case 3: return 0; break; } } return system32_workram[0xba00/2 + offset]; } WRITE16_HANDLER(brival_protection_w) { static const int protAddress[6][2] = { { 0x109517, 0x00/2 }, { 0x109597, 0x10/2 }, { 0x109597, 0x20/2 }, { 0x109597, 0x30/2 }, { 0x109597, 0x40/2 }, { 0x109617, 0x50/2 }, }; char ret[32]; int curProtType; UINT8 *ROM = memory_region(REGION_CPU1); switch (offset) { case 0x800/2: curProtType = 0; break; case 0x802/2: curProtType = 1; break; case 0x804/2: curProtType = 2; break; case 0x806/2: curProtType = 3; break; case 0x808/2: curProtType = 4; break; case 0x80a/2: curProtType = 5; break; default: if (offset >= 0xa00/2 && offset < 0xc00/2) return; logerror("brival_protection_w: UNKNOWN WRITE: offset %x value %x\n", offset, data); return; break; } memcpy(ret, &ROM[protAddress[curProtType][0]], 16); ret[16] = '\0'; memcpy(&system32_protram[protAddress[curProtType][1]], ret, 16); } /****************************************************************************** ****************************************************************************** Dark Edge ****************************************************************************** ******************************************************************************/ void darkedge_fd1149_vblank(void) { program_write_word(0x20f072, 0); program_write_word(0x20f082, 0); if( program_read_byte(0x20a12c) != 0 ) { program_write_byte(0x20a12c, program_read_byte(0x20a12c)-1 ); if( program_read_byte(0x20a12c) == 0 ) program_write_byte(0x20a12e, 1); } } WRITE16_HANDLER( darkedge_protection_w ) { logerror("%06x:darkedge_prot_w(%06X) = %04X & %04X\n", activecpu_get_pc(), 0xa00000 + 2*offset, data, mem_mask ^ 0xffff); } READ16_HANDLER( darkedge_protection_r ) { logerror("%06x:darkedge_prot_r(%06X) & %04X\n", activecpu_get_pc(), 0xa00000 + 2*offset, mem_mask ^ 0xffff); return 0xffff; } /****************************************************************************** ****************************************************************************** DBZ VRVS ****************************************************************************** ******************************************************************************/ WRITE16_HANDLER( dbzvrvs_protection_w ) { program_write_word( 0x2080c8, program_read_word( 0x200044 ) ); } READ16_HANDLER( dbzvrvs_protection_r ) { return 0xffff; } /****************************************************************************** ****************************************************************************** Arabian Fight ****************************************************************************** ******************************************************************************/ // protection ram is 8-bits wide and only occupies every other address READ16_HANDLER(arabfgt_protection_r) { int PC = activecpu_get_pc(); int cmpVal; if (PC == 0xfe0325 || PC == 0xfe01e5 || PC == 0xfe035e || PC == 0xfe03cc) { cmpVal = activecpu_get_reg(1); // R0 always contains the value the protection is supposed to return (!) return cmpVal; } else { popmessage("UNKONWN ARF PROTECTION READ PC=%x\n", PC); } return 0; } WRITE16_HANDLER(arabfgt_protection_w) { } READ16_HANDLER(arf_wakeup_protection_r) { static const char *prot = "wake up! ARF! "; return prot[offset]; } /****************************************************************************** ****************************************************************************** The J.League 1994 (Japan) ****************************************************************************** ******************************************************************************/ WRITE16_HANDLER( jleague_protection_w ) { COMBINE_DATA( &system32_workram[0xf700/2 + offset ] ); switch( offset ) { // Map team browser selection to opponent browser selection // using same lookup table that V60 uses for sound sample mapping. case 0: program_write_byte( 0x20f708, program_read_word( 0x7bbc0 + data*2 ) ); break; // move on to team browser case 4/2: program_write_byte( 0x200016, data & 0xff ); break; default: break; } } /****************************************************************************** ****************************************************************************** Air Rescue ****************************************************************************** ******************************************************************************/ /* protection a00000 - a00002 dsp i/o a00004 - dsp int/ack dsp uses its p0/p1 for address select dsp.sr = ???0 read a00000 into dsp.a dsp.sr = ???1 read a00002 into dsp.b dsp.sr = ???2 write dsp.b in a00000 dsp.sr = ???3 write dsp.a in a00002 Use of p0/p1 means there's no other way for dsp to communicate with V60, unless it shares RAM. 99.99% of the dsp code is unused because the V60 ROM is hardcoded as part of a twin set, maybe the standalone board was for dev only? nop the 3 bytes at 0x06023A for standalone. (centred intro text) */ static UINT16 arescue_dsp_io[6] = {0,0,0,0,0,0}; READ16_HANDLER( arescue_dsp_r ) { if( offset == 4/2 ) { switch( arescue_dsp_io[0] ) { case 0: case 1: case 2: break; case 3: arescue_dsp_io[0] = 0x8000; arescue_dsp_io[2/2] = 0x0001; break; case 6: arescue_dsp_io[0] = 4 * arescue_dsp_io[2/2]; break; default: logerror("Unhandled DSP cmd %04x (%04x).\n", arescue_dsp_io[0], arescue_dsp_io[1] ); break; } } return arescue_dsp_io[offset]; } WRITE16_HANDLER( arescue_dsp_w ) { COMBINE_DATA(&arescue_dsp_io[offset]); }