/*
    namcoc7x.c - sound hardware for mid-90s Namco systems

    System      MCU Synthesizer
    ------------------------------------
    NB-1        C351    C352
    NB-2        C351    C352
    System 22   C74 C352
    FL      C75 C352
    System 11   C76 C352

    All of these MCUs are Mitsubishi M377xx family (65c816 based)
    with 16k of internal BIOS ROM.  The BIOS is the same for all
    chips of the same number.  We currently use the BIOS from a
    Super System 22 game (Prop Cycle) to substitute for the
    BIOS in these chips.  This is compatible enough to play
    the sound and music, but not enough to read the inputs or
    handle coinage (the other task these MCUs have).

    These systems have 48k of shared RAM with the host.  Due to
    the MCU's work variables being moved around we cannot enable
    full sharing or else the main CPU can overwrite important
    MCU work variables and crash it.

    Emulation by R. Belmont.
    Thanks to Cync and Cap for invaluable assistance.
*/

#include "driver.h"
#include "cpu/m37710/m37710.h"
#include "deprecat.h"
#include "audio/namcoc7x.h"

static UINT16 *namcoc7x_mcuram;
static UINT16 su_82;
static UINT32 *namcoc7x_hostram;

static READ16_HANDLER( speedup_r )
{
	if ((cpu_get_pc(space->cpu) == 0xc12d) && (!(su_82 & 0xff00)))
	{
		cpu_spinuntil_int(space->cpu);
	}

	return su_82;
}

static WRITE16_HANDLER( speedup_w )
{
	COMBINE_DATA(&su_82);
}

WRITE16_HANDLER(namcoc7x_soundram16_w)
{
	COMBINE_DATA(namcoc7x_mcuram+offset);
}

READ16_HANDLER(namcoc7x_soundram16_r)
{
	return namcoc7x_mcuram[offset];
}

WRITE32_HANDLER(namcoc7x_soundram32_w)
{
	namcoc7x_soundram16_w(space, offset*2, data >> 16, mem_mask >> 16);
	namcoc7x_soundram16_w(space, offset*2+1, data, mem_mask);
}

READ32_HANDLER(namcoc7x_soundram32_r)
{
	return (namcoc7x_soundram16_r(space, offset*2, mem_mask >> 16) << 16) |
		namcoc7x_soundram16_r(space, offset*2+1, mem_mask);
}

void namcoc7x_sound_write16(UINT16 command, UINT32 offset)
{
	namcoc7x_mcuram[offset] = command;
}

void namcoc7x_on_driver_init(running_machine *machine)
{
	UINT8 *pROM = (UINT8 *)memory_region(machine, "c7x");
	const device_config *cpu;

	// clear the two 16-bits magic values at the start of the rom
	// (prevents external init routines from getting called - they assume a
	//  ROM layout for a different BIOS and crash ours)
	memset(pROM, 0, 4);

	// install speedup cheat
	for (cpu = cputag_get_cpu(machine, "maincpu"); cpu != NULL; cpu = cpu->typenext)
		if (cpu_get_type(cpu) == CPU_M37702)
			memory_install_readwrite16_handler(cpu_get_address_space(cpu, ADDRESS_SPACE_PROGRAM), 0x82, 0x83, 0, 0, speedup_r, speedup_w);
}

void namcoc7x_set_host_ram(UINT32 *hostram)
{
	namcoc7x_hostram = hostram;
}

// Only share the sound work area
static READ16_HANDLER( c7x_shared_r )
{
	UINT16 *share16 = (UINT16 *)namcoc7x_hostram;

	if (offset >= 0x400/2)
	{
		return namcoc7x_mcuram[offset];
	}

	return share16[BYTE_XOR_LE(offset)];
}

static WRITE16_HANDLER( c7x_shared_w )
{
	UINT16 *share16 = (UINT16 *)namcoc7x_hostram;

	if (offset >= 0x400/2)
	{
		COMBINE_DATA(&namcoc7x_mcuram[offset]);
	}
	else
	{
		COMBINE_DATA(&share16[BYTE_XOR_LE(offset)]);
	}
}

ADDRESS_MAP_START( namcoc7x_mcu_map, ADDRESS_SPACE_PROGRAM, 16 )
	AM_RANGE(0x002000, 0x002fff) AM_DEVREADWRITE("c352", c352_r, c352_w)
	AM_RANGE(0x004000, 0x00bfff) AM_RAM AM_BASE(&namcoc7x_mcuram)
	AM_RANGE(0x00c000, 0x00ffff) AM_ROM AM_REGION("c7x", 0x8c000)
	AM_RANGE(0x080000, 0x0fffff) AM_ROM AM_REGION("c7x", 0)
	AM_RANGE(0x200000, 0x27ffff) AM_ROM AM_REGION("c7x", 0)
	AM_RANGE(0x280000, 0x2fffff) AM_ROM AM_REGION("c7x", 0)
	AM_RANGE(0x301000, 0x301001) AM_NOP	// watchdog? LEDs?
	AM_RANGE(0x308000, 0x308003) AM_NOP	// volume control IC?
ADDRESS_MAP_END

ADDRESS_MAP_START( namcoc7x_mcu_share_map, ADDRESS_SPACE_PROGRAM, 16 )
	AM_RANGE(0x002000, 0x002fff) AM_DEVREADWRITE("c352", c352_r, c352_w )
	AM_RANGE(0x004000, 0x00bfff) AM_READWRITE( c7x_shared_r, c7x_shared_w ) AM_BASE(&namcoc7x_mcuram)
	AM_RANGE(0x00c000, 0x00ffff) AM_ROM AM_REGION("c7x", 0x8c000)
	AM_RANGE(0x080000, 0x0fffff) AM_ROM AM_REGION("c7x", 0)
	AM_RANGE(0x200000, 0x27ffff) AM_ROM AM_REGION("c7x", 0)
	AM_RANGE(0x280000, 0x2fffff) AM_ROM AM_REGION("c7x", 0)
	AM_RANGE(0x301000, 0x301001) AM_NOP	// watchdog? LEDs?
	AM_RANGE(0x308000, 0x308003) AM_NOP	// volume control IC?
ADDRESS_MAP_END

static int p4;

static READ8_HANDLER( mcu_port5_r )
{
	return 0xff;
}

static WRITE8_HANDLER( mcu_port4_w )
{
	p4 = data;
}

static READ8_HANDLER( mcu_port4_r )
{
	return p4;
}

static WRITE8_HANDLER( mcu_port5_w )
{
}

static READ8_HANDLER( mcu_port6_r )
{
	return 0;
}

static READ8_HANDLER( mcu_port7_r )
{
	return 0;
}

ADDRESS_MAP_START( namcoc7x_mcu_io, ADDRESS_SPACE_IO, 8 )
	AM_RANGE(M37710_PORT4, M37710_PORT4) AM_READ( mcu_port4_r ) AM_WRITE( mcu_port4_w )
	AM_RANGE(M37710_PORT5, M37710_PORT5) AM_READ( mcu_port5_r ) AM_WRITE( mcu_port5_w )
	AM_RANGE(M37710_PORT6, M37710_PORT6) AM_READ( mcu_port6_r ) AM_WRITENOP
	AM_RANGE(M37710_PORT7, M37710_PORT7) AM_READ( mcu_port7_r )
	AM_RANGE(0x10, 0x1f) AM_NOP
ADDRESS_MAP_END

INTERRUPT_GEN( namcoc7x_interrupt )
{
	if (cpu_getiloops(device) == 0)
 		cpu_set_input_line(device, M37710_LINE_IRQ0, HOLD_LINE);
	else
		cpu_set_input_line(device, M37710_LINE_IRQ2, HOLD_LINE);
}

