// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/* Sega System 32 Protection related functions */
#include "emu.h"
#include "includes/segas32.h"
/******************************************************************************
******************************************************************************
Golden Axe 2 (Revenge of Death Adder)
******************************************************************************
******************************************************************************/
#define xxxx 0x00
const uint8_t segas32_v25_state::ga2_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,0x3B,xxxx,0x49,xxxx,xxxx,xxxx,
xxxx,xxxx,xxxx,xxxx,xxxx,0xE8,xxxx,xxxx,0x75,xxxx,xxxx,xxxx,xxxx,0x3A,xxxx,xxxx,
xxxx,xxxx,xxxx,xxxx,0x8D,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xBF,xxxx,0x88,xxxx,
xxxx,xxxx,xxxx,0x81,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,
0x02,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,0xB5,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,0xBB,xxxx,0x43,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,0x80,xxxx,xxxx
};
#undef xxxx
void segas32_v25_state::decrypt_protrom()
{
int i;
uint8_t *rom = memregion("mcu")->base();
std::vector<uint8_t> temp(0x100000);
// make copy of ROM so original can be overwritten
memcpy(&temp[0], rom, 0x10000);
// unscramble the address lines
for(i = 0; i < 0x10000; i++)
rom[i] = temp[bitswap<16>(i, 14, 11, 15, 12, 13, 4, 3, 7, 5, 10, 2, 8, 9, 6, 1, 0)];
}
#if 0 // simulation
READ16_MEMBER(segas32_state::ga2_sprite_protection_r)
{
static const uint16_t prot[16] =
{
0x0a, 0,
0xc5, 0,
0x11, 0,
0x11, 0,
0x18, 0,
0x18, 0,
0x1f, 0,
0xc6, 0,
};
return prot[offset];
}
READ16_MEMBER(segas32_state::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_MEMBER(segas32_state::sonic_level_load_protection)
{
uint16_t level;
//Perform write
COMBINE_DATA(&m_system32_workram[CLEARED_LEVELS / 2]);
//Refresh current level
if (m_system32_workram[CLEARED_LEVELS / 2] == 0)
{
level = 0x0007;
}
else
{
const uint8_t *ROM = m_maincpu_region->base();
level = *((ROM + LEVEL_ORDER_ARRAY) + (m_system32_workram[CLEARED_LEVELS / 2] * 2) - 1);
level |= *((ROM + LEVEL_ORDER_ARRAY) + (m_system32_workram[CLEARED_LEVELS / 2] * 2) - 2) << 8;
}
m_system32_workram[CURRENT_LEVEL / 2] = level;
//Reset level status
m_system32_workram[CURRENT_LEVEL_STATUS / 2] = 0x0000;
m_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_MEMBER(segas32_state::brival_protection_r)
{
if (mem_mask == 0xffff) // only trap on word-wide reads
{
switch (offset)
{
case 0:
case 2:
case 3:
return 0;
}
}
return m_system32_workram[0xba00/2 + offset];
}
WRITE16_MEMBER(segas32_state::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_t *ROM = m_maincpu_region->base();
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;
}
memcpy(ret, &ROM[protAddress[curProtType][0]], 16);
ret[16] = '\0';
memcpy(&m_system32_protram[protAddress[curProtType][1]], ret, 16);
}
/******************************************************************************
******************************************************************************
Dark Edge
******************************************************************************
******************************************************************************/
void segas32_state::darkedge_fd1149_vblank()
{
address_space &space = m_maincpu->space(AS_PROGRAM);
space.write_word(0x20f072, 0);
space.write_word(0x20f082, 0);
if( space.read_byte(0x20a12c) != 0 )
{
space.write_byte(0x20a12c, space.read_byte(0x20a12c)-1 );
if( space.read_byte(0x20a12c) == 0 )
space.write_byte(0x20a12e, 1);
}
}
WRITE16_MEMBER(segas32_state::darkedge_protection_w)
{
logerror("%s:darkedge_prot_w(%06X) = %04X & %04X\n",
machine().describe_context(), 0xa00000 + 2*offset, data, mem_mask);
}
READ16_MEMBER(segas32_state::darkedge_protection_r)
{
logerror("%s:darkedge_prot_r(%06X) & %04X\n",
machine().describe_context(), 0xa00000 + 2*offset, mem_mask);
return 0xffff;
}
/******************************************************************************
******************************************************************************
F1 Super Lap
******************************************************************************
******************************************************************************/
void segas32_state::f1lap_fd1149_vblank()
{
address_space &space = m_maincpu->space(AS_PROGRAM);
space.write_byte(0x20F7C6, 0);
// needed to start a game
uint8_t val = space.read_byte(0x20EE81);
if (val == 0xff) space.write_byte(0x20EE81,0);
}
/******************************************************************************
******************************************************************************
DBZ VRVS
******************************************************************************
******************************************************************************/
WRITE16_MEMBER(segas32_state::dbzvrvs_protection_w)
{
space.write_word( 0x2080c8, space.read_word( 0x200044 ) );
}
READ16_MEMBER(segas32_state::dbzvrvs_protection_r)
{
return 0xffff;
}
/******************************************************************************
******************************************************************************
Arabian Fight
******************************************************************************
******************************************************************************/
#define xxxx 0x00
const uint8_t segas32_v25_state::arf_opcode_table[256] = {
xxxx,xxxx,0x43,xxxx,xxxx,xxxx,0x83,xxxx,xxxx,xxxx,0xEA,xxxx,xxxx,0xBC,0x73,xxxx,
xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,
0x3A,xxxx,xxxx,0xBE,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0x80,xxxx,
xxxx,0xB5,xxxx,xxxx,xxxx,xxxx,xxxx,0x26,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,
xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xE8,0x8D,xxxx,0x8B,xxxx,
xxxx,xxxx,xxxx,0xFA,xxxx,0x8A,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,0xBA,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,0xBB,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,
xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0x75,xxxx,0xBF,
xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0x03,0x3B,0x8E,0x74,xxxx,xxxx,0x81,xxxx,
xxxx,xxxx,xxxx,0xC3,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,
xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xB9,0xB2,xxxx,xxxx,xxxx,xxxx,0x49,xxxx,xxxx,
xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0xEB,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,
xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,xxxx,0x02,0xB8
};
#undef xxxx
#if 0 // old simulation
READ16_MEMBER(segas32_state::arabfgt_protection_r)
{
int PC = m_maincpu->pc();
int cmpVal;
if (PC == 0xfe0325 || PC == 0xfe01e5 || PC == 0xfe035e || PC == 0xfe03cc)
{
cmpVal = m_maincpu->state_int(V60_R0);
// 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_MEMBER(segas32_state::arabfgt_protection_w)
{
}
READ16_MEMBER(segas32_state::arf_wakeup_protection_r)
{
static const char prot[] =
"wake up! ARF! ";
return prot[offset];
}
#endif
/******************************************************************************
******************************************************************************
The J.League 1994 (Japan)
******************************************************************************
******************************************************************************/
WRITE16_MEMBER(segas32_state::jleague_protection_w)
{
COMBINE_DATA( &m_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:
space.write_byte( 0x20f708, space.read_word( 0x7bbc0 + data*2 ) );
break;
// move on to team browser
case 4/2:
space.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)
*/
READ16_MEMBER(segas32_state::arescue_dsp_r)
{
if( offset == 4/2 )
{
switch( m_arescue_dsp_io[0] )
{
case 0:
case 1:
case 2:
break;
case 3:
m_arescue_dsp_io[0] = 0x8000;
m_arescue_dsp_io[2/2] = 0x0001;
break;
case 6:
m_arescue_dsp_io[0] = 4 * m_arescue_dsp_io[2/2];
break;
default:
logerror("Unhandled DSP cmd %04x (%04x).\n", m_arescue_dsp_io[0], m_arescue_dsp_io[1] );
break;
}
}
return m_arescue_dsp_io[offset];
}
WRITE16_MEMBER(segas32_state::arescue_dsp_w)
{
COMBINE_DATA(&m_arescue_dsp_io[offset]);
}