MAME/src/mame/williams/midyunit_m.cpp
Vas Crabb a9f017e86c Split midway project into bally and williams according to brand owner.
Videa/Sente stuff from the brief period before it was acquired by Bally
is still in the bally project.
2026-05-24 01:35:01 +10:00

716 lines
17 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Alex Pasadyn, Zsolt Vasvari, Ernesto Corvi, Aaron Giles
// thanks-to:Kurt Mahan
/*************************************************************************
Williams/Midway Y/Z-unit system
**************************************************************************/
#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "midyunit.h"
#define LOG_CMOS (1U << 1)
#define LOG_PROT (1U << 1)
#define LOG_ALL (LOG_CMOS | LOG_PROT)
#define VERBOSE (0)
#include "logmacro.h"
#define LOGCMOS(...) LOGMASKED(LOG_CMOS, __VA_ARGS__)
#define LOGPROT(...) LOGMASKED(LOG_PROT, __VA_ARGS__)
// constant definitions
enum
{
SOUND_CVSD_SMALL = 0,
SOUND_CVSD
};
/*************************************
*
* CMOS reads/writes
*
*************************************/
void midyunit_base_state::cmos_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
LOGCMOS("%08x:CMOS Write @ %05X\n", m_maincpu->pc(), offset);
COMBINE_DATA(&m_cmos_ram[offset + m_cmos_page]);
}
uint16_t midyunit_base_state::cmos_r(offs_t offset)
{
return m_cmos_ram[offset + m_cmos_page];
}
/*************************************
*
* CMOS enable and protection
*
*************************************/
void midyunit_base_state::cmos_enable_w(address_space &space, uint16_t data)
{
m_cmos_w_enable = BIT(~data, 9);
LOGPROT("%08x:Protection write = %04X\n", m_maincpu->pc(), data);
// only go down this path if we have a data structure
if (m_prot_data)
{
// mask off the data
data &= 0x0f00;
// update the FIFO
m_prot_sequence[0] = m_prot_sequence[1];
m_prot_sequence[1] = m_prot_sequence[2];
m_prot_sequence[2] = data;
// special case: sequence entry 1234 means Strike Force, which is different
if (m_prot_data->reset_sequence[0] == 0x1234)
{
if (data == 0x500)
{
m_prot_result = space.read_word(0x10a4390) << 4;
LOGPROT(" desired result = %04X\n", m_prot_result);
}
}
// all other games use the same pattern
else
{
// look for a reset
if (m_prot_sequence[0] == m_prot_data->reset_sequence[0] &&
m_prot_sequence[1] == m_prot_data->reset_sequence[1] &&
m_prot_sequence[2] == m_prot_data->reset_sequence[2])
{
LOGPROT("Protection reset\n");
m_prot_index = 0;
}
// look for a clock
if ((m_prot_sequence[1] & 0x0800) != 0 && (m_prot_sequence[2] & 0x0800) == 0)
{
m_prot_result = m_prot_data->data_sequence[m_prot_index++];
LOGPROT("Protection clock (new data = %04X)\n", m_prot_result);
}
}
}
}
uint16_t midyunit_base_state::protection_r()
{
// return the most recently clocked value
if (!machine().side_effects_disabled())
LOGPROT("%08X:Protection read = %04X\n", m_maincpu->pc(), m_prot_result);
return m_prot_result;
}
/*************************************
*
* Generic input ports
*
*************************************/
uint16_t midyunit_base_state::input_r(offs_t offset)
{
return m_ports[offset]->read();
}
/*************************************
*
* Special Terminator 2 input ports
*
*************************************/
uint16_t term2_state::term2_input_r(offs_t offset)
{
if (offset != 2)
return m_ports[offset]->read();
return m_adc->read() | 0xff00;
}
void term2_state::term2_sound_w(offs_t offset, uint16_t data)
{
// Flash Lamp Output Data
if ( ((data & 0x800) != 0x800) && ((data & 0x400) == 0x400 ) )
{
for (int i = 0; i < 4; i++)
m_left_flash[i] = BIT(data, i);
for (int i = 0; i < 4; i++)
m_right_flash[i] = BIT(data, i + 4);
}
// Gun Output Data
if ( ((data & 0x800) == 0x800) && ((data & 0x400) != 0x400 ) )
{
m_left_gun_recoil = BIT(data, 0);
m_right_gun_recoil = BIT(data, 1);
m_left_gun_green_led = BIT(~data, 5);
m_left_gun_red_led = BIT(~data, 4);
m_right_gun_green_led = BIT(~data, 7);
m_right_gun_red_led = BIT(~data, 6);
}
if (offset == 0)
m_adc->write(((data >> 12) & 3) | 4);
m_adpcm_sound->reset_write((~data & 0x100) >> 1);
m_adpcm_sound->write(data);
}
/*************************************
*
* Special Terminator 2 hack
*
*************************************/
void term2_state::term2_hack_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
if (offset == 1 && m_maincpu->pc() == 0xffce6520)
{
m_t2_hack_mem[offset] = 0;
return;
}
COMBINE_DATA(&m_t2_hack_mem[offset]);
}
void term2_state::term2la3_hack_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
if (offset == 0 && m_maincpu->pc() == 0xffce5230)
{
m_t2_hack_mem[offset] = 0;
return;
}
COMBINE_DATA(&m_t2_hack_mem[offset]);
}
void term2_state::term2la2_hack_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
if (offset == 0 && m_maincpu->pc() == 0xffce4b80)
{
m_t2_hack_mem[offset] = 0;
return;
}
COMBINE_DATA(&m_t2_hack_mem[offset]);
}
void term2_state::term2la1_hack_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
if (offset == 0 && m_maincpu->pc() == 0xffce33f0)
{
m_t2_hack_mem[offset] = 0;
return;
}
COMBINE_DATA(&m_t2_hack_mem[offset]);
}
/*************************************
*
* Generic driver init
*
*************************************/
void midyunit_cvsd_state::cvsd_protection_w(offs_t offset, uint8_t data)
{
// because the entire CVSD ROM is banked, we have to make sure that writes
// go to the proper location (i.e., bank 0); currently bank 0 always lives
// in the 0x10000-0x17fff space, so we just need to add 0x8000 to get the
// proper offset
m_cvsd_protection_base[offset] = data;
}
void midyunit_base_state::install_hidden_ram(mc6809e_device &cpu, int prot_start, int prot_end)
{
size_t size = 1 + prot_end - prot_start;
m_hidden_ram = std::make_unique<uint8_t[]>(size);
save_pointer(NAME(m_hidden_ram), size);
cpu.space(AS_PROGRAM).install_ram(prot_start, prot_end, m_hidden_ram.get());
}
void midyunit_base_state::init_gfxrom(int bpp)
{
offs_t const gfx_chunk = m_gfx_rom.bytes() / 4;
uint8_t d1, d2, d3, d4, d5, d6;
// load graphics ROMs
uint8_t const *const base = memregion("gfx")->base();
switch (bpp)
{
case 4:
for (int i = 0; i < m_gfx_rom.bytes(); i += 2)
{
d1 = ((base[0 * gfx_chunk + (i + 0) / 4]) >> (2 * ((i + 0) % 4))) & 3;
d2 = ((base[1 * gfx_chunk + (i + 0) / 4]) >> (2 * ((i + 0) % 4))) & 3;
d3 = ((base[0 * gfx_chunk + (i + 1) / 4]) >> (2 * ((i + 1) % 4))) & 3;
d4 = ((base[1 * gfx_chunk + (i + 1) / 4]) >> (2 * ((i + 1) % 4))) & 3;
m_gfx_rom[i + 0] = d1 | (d2 << 2);
m_gfx_rom[i + 1] = d3 | (d4 << 2);
}
break;
case 6:
for (int i = 0; i < m_gfx_rom.bytes(); i += 2)
{
d1 = ((base[0 * gfx_chunk + (i + 0) / 4]) >> (2 * ((i + 0) % 4))) & 3;
d2 = ((base[1 * gfx_chunk + (i + 0) / 4]) >> (2 * ((i + 0) % 4))) & 3;
d3 = ((base[2 * gfx_chunk + (i + 0) / 4]) >> (2 * ((i + 0) % 4))) & 3;
d4 = ((base[0 * gfx_chunk + (i + 1) / 4]) >> (2 * ((i + 1) % 4))) & 3;
d5 = ((base[1 * gfx_chunk + (i + 1) / 4]) >> (2 * ((i + 1) % 4))) & 3;
d6 = ((base[2 * gfx_chunk + (i + 1) / 4]) >> (2 * ((i + 1) % 4))) & 3;
m_gfx_rom[i + 0] = d1 | (d2 << 2) | (d3 << 4);
m_gfx_rom[i + 1] = d4 | (d5 << 2) | (d6 << 4);
}
break;
case 8:
for (int i = 0; i < m_gfx_rom.bytes(); i += 4)
{
m_gfx_rom[i + 0] = base[0 * gfx_chunk + i / 4];
m_gfx_rom[i + 1] = base[1 * gfx_chunk + i / 4];
m_gfx_rom[i + 2] = base[2 * gfx_chunk + i / 4];
m_gfx_rom[i + 3] = base[3 * gfx_chunk + i / 4];
}
break;
}
}
void midyunit_cvsd_state::init_generic(int bpp, int sound, int prot_start, int prot_end)
{
init_gfxrom(bpp);
// load sound ROMs and set up sound handlers
switch (sound)
{
case SOUND_CVSD_SMALL:
m_cvsd_sound->get_cpu()->space(AS_PROGRAM).install_write_handler(prot_start, prot_end, write8sm_delegate(*this, FUNC(midyunit_cvsd_state::cvsd_protection_w)));
m_cvsd_protection_base = memregion("cvsd:cpu")->base() + 0x10000 + (prot_start - 0x8000);
break;
case SOUND_CVSD:
install_hidden_ram(*m_cvsd_sound->get_cpu(), prot_start, prot_end);
break;
}
}
void midyunit_adpcm_state::init_generic(int bpp, int prot_start, int prot_end)
{
init_gfxrom(bpp);
install_hidden_ram(*m_adpcm_sound->get_cpu(), prot_start, prot_end);
}
/*************************************
*
* Z-unit init
*
* music: 6809 driving YM2151, DAC
* effects: 6809 driving CVSD, DAC
*
*************************************/
void midzunit_state::init_narc()
{
// common init
init_gfxrom(8);
install_hidden_ram(*m_narc_sound->get_cpu(), 0xcdff, 0xce29);
}
/*************************************
*
* Y-unit init (CVSD)
*
* music: 6809 driving YM2151, DAC, and CVSD
*
*************************************/
/********************** Trog **************************/
void midyunit_cvsd_state::init_trog()
{
// protection
static const struct protection_data trog_protection_data =
{
{ 0x0f00, 0x0f00, 0x0f00 },
{ 0x3000, 0x1000, 0x2000, 0x0000,
0x2000, 0x3000,
0x3000, 0x1000,
0x0000, 0x0000, 0x2000, 0x3000, 0x1000, 0x1000, 0x2000 }
};
m_prot_data = &trog_protection_data;
// common init
init_generic(4, SOUND_CVSD_SMALL, 0x9eaf, 0x9ed9);
}
/********************** Smash TV **********************/
void midyunit_cvsd_state::init_smashtv()
{
// common init
init_generic(6, SOUND_CVSD_SMALL, 0x9cf6, 0x9d21);
m_prot_data = nullptr;
}
/********************** High Impact Football **********************/
void midyunit_cvsd_state::init_hiimpact()
{
// protection
static const struct protection_data hiimpact_protection_data =
{
{ 0x0b00, 0x0b00, 0x0b00 },
{ 0x2000, 0x4000, 0x4000, 0x0000, 0x6000, 0x6000, 0x2000, 0x4000,
0x2000, 0x4000, 0x2000, 0x0000, 0x4000, 0x6000, 0x2000 }
};
m_prot_data = &hiimpact_protection_data;
// common init
init_generic(6, SOUND_CVSD, 0x9b79, 0x9ba3);
}
/********************** Super High Impact Football **********************/
void midyunit_cvsd_state::init_shimpact()
{
// protection
static const struct protection_data shimpact_protection_data =
{
{ 0x0f00, 0x0e00, 0x0d00 },
{ 0x0000, 0x4000, 0x2000, 0x5000, 0x2000, 0x1000, 0x4000, 0x6000,
0x3000, 0x0000, 0x2000, 0x5000, 0x5000, 0x5000, 0x2000 }
};
m_prot_data = &shimpact_protection_data;
// common init
init_generic(6, SOUND_CVSD, 0x9c06, 0x9c15);
}
/********************** Strike Force **********************/
void midyunit_cvsd_state::init_strkforc()
{
// protection
static const struct protection_data strkforc_protection_data =
{
{ 0x1234 }
};
m_prot_data = &strkforc_protection_data;
// common init
init_generic(4, SOUND_CVSD_SMALL, 0x9f7d, 0x9fa7);
}
/*************************************
*
* Y-unit init (ADPCM)
*
* music: 6809 driving YM2151, DAC, and OKIM6295
*
*************************************/
/********************** Mortal Kombat **********************/
void midyunit_adpcm_state::init_mkyunit()
{
// protection
static const struct protection_data mk_protection_data =
{
{ 0x0d00, 0x0c00, 0x0900 },
{ 0x4600, 0xf600, 0xa600, 0x0600, 0x2600, 0x9600, 0xc600, 0xe600,
0x8600, 0x7600, 0x8600, 0x8600, 0x9600, 0xd600, 0x6600, 0xb600,
0xd600, 0xe600, 0xf600, 0x7600, 0xb600, 0xa600, 0x3600 }
};
m_prot_data = &mk_protection_data;
// common init
init_generic(6, 0xfb9c, 0xfbc6);
}
void mkyawdim_state::init_mkyawdim()
{
// common init
init_gfxrom(6);
}
/*************************************
*
* MK Turbo Ninja protection
*
*************************************/
uint16_t midyunit_adpcm_state::mkturbo_prot_r()
{
/* the security GAL overlays a counter of some sort at 0xfffff400 in ROM space.
* A startup protection check expects to read back two different values in succession */
return machine().rand();
}
void midyunit_adpcm_state::init_mkyturbo()
{
// protection
m_maincpu->space(AS_PROGRAM).install_read_handler(0xfffff400, 0xfffff40f, read16smo_delegate(*this, FUNC(midyunit_adpcm_state::mkturbo_prot_r)));
init_mkyunit();
}
void midyunit_adpcm_state::init_mkrep()
{
// patch out protection for now
uint16_t *rom = (uint16_t *)memregion("maindata")->base();
rom[0x94234 / 2] = 0x05a0;
rom[0x94236 / 2] = 0xe660;
rom[0x94238 / 2] = 0x0104;
rom[0x9637c / 2] = 0x0d3f;
rom[0x9637e / 2] = 0xf343;
rom[0x96380 / 2] = 0x4c00;
rom[0x9c476 / 2] = 0xb5a0;
rom[0x9c478 / 2] = 0x0030;
rom[0x9c47a / 2] = 0x1420;
rom[0x9c47c / 2] = 0xb00d;
rom[0x9c47e / 2] = 0x0030;
init_mkyunit();
}
void midyunit_adpcm_state::init_mkla3bl()
{
// rearrange GFX so the driver can deal with them
uint8_t *gfxrom = memregion("gfx")->base();
std::vector<uint8_t> buffer(0x400000);
memcpy(&buffer[0], gfxrom, 0x400000);
for (int i = 0x000000; i < 0x100000; i++)
gfxrom[i] = buffer[0x200000 | ((i & 0xfffff) * 2 + 1)];
for (int i = 0x100000; i < 0x200000; i++)
gfxrom[i] = buffer[(i & 0xfffff) * 2 + 1];
for (int i = 0x200000; i < 0x300000; i++)
gfxrom[i] = buffer[0x200000 | ((i & 0xfffff) * 2)];
for (int i = 0x300000; i < 0x400000; i++)
gfxrom[i] = buffer[(i & 0xfffff) * 2];
// 0x400000 - 0x5fffff range is already ok
init_mkyunit();
}
/********************** Terminator 2 **********************/
void term2_state::term2_init_common(write16s_delegate hack_w)
{
// protection
static constexpr struct protection_data term2_protection_data =
{
{ 0x0f00, 0x0f00, 0x0f00 },
{ 0x4000, 0xf000, 0xa000 }
};
m_prot_data = &term2_protection_data;
// common init
init_generic(6, 0xfa8d, 0xfa9c);
// HACK: this prevents the freeze on the movies
// until we figure what's causing it, this is better than nothing
m_maincpu->space(AS_PROGRAM).install_write_handler(0x010aa0e0, 0x010aa0ff, hack_w);
m_t2_hack_mem = m_mainram + (0xaa0e0>>4);
}
void term2_state::init_term2() { term2_init_common(write16s_delegate(*this, FUNC(term2_state::term2_hack_w))); }
void term2_state::init_term2la3() { term2_init_common(write16s_delegate(*this, FUNC(term2_state::term2la3_hack_w))); }
void term2_state::init_term2la2() { term2_init_common(write16s_delegate(*this, FUNC(term2_state::term2la2_hack_w))); }
void term2_state::init_term2la1() { term2_init_common(write16s_delegate(*this, FUNC(term2_state::term2la1_hack_w))); }
/********************** Total Carnage **********************/
void midyunit_adpcm_state::init_totcarn()
{
// protection
static const struct protection_data totcarn_protection_data =
{
{ 0x0f00, 0x0f00, 0x0f00 },
{ 0x4a00, 0x6a00, 0xda00, 0x6a00, 0x9a00, 0x4a00, 0x2a00, 0x9a00, 0x1a00,
0x8a00, 0xaa00 }
};
m_prot_data = &totcarn_protection_data;
// common init
init_generic(6, 0xfc04, 0xfc2e);
}
/*************************************
*
* Machine init
*
*************************************/
void midyunit_base_state::machine_start()
{
// allocate memory
m_cmos_ram = std::make_unique<uint16_t[]>((0x2000 * 4)/2);
m_nvram->set_base(m_cmos_ram.get(), 0x2000 * 4);
// reset all the globals
m_cmos_page = 0;
save_item(NAME(m_cmos_page));
save_item(NAME(m_prot_result));
save_item(NAME(m_prot_sequence));
save_item(NAME(m_prot_index));
save_item(NAME(m_cmos_w_enable));
save_pointer(NAME(m_cmos_ram), (0x2000 * 4)/2);
}
void term2_state::machine_start()
{
midyunit_adpcm_state::machine_start();
m_left_flash.resolve();
m_right_flash.resolve();
m_left_gun_recoil.resolve();
m_right_gun_recoil.resolve();
m_left_gun_green_led.resolve();
m_left_gun_red_led.resolve();
m_right_gun_green_led.resolve();
m_right_gun_red_led.resolve();
}
void midzunit_state::machine_reset()
{
midyunit_base_state::machine_reset();
m_narc_sound->reset_write(1);
m_narc_sound->reset_write(0);
}
void midyunit_cvsd_state::machine_reset()
{
midyunit_base_state::machine_reset();
m_cvsd_sound->reset_write(1);
m_cvsd_sound->reset_write(0);
}
void midyunit_adpcm_state::machine_reset()
{
midyunit_base_state::machine_reset();
m_adpcm_sound->reset_write(1);
m_adpcm_sound->reset_write(0);
}
/*************************************
*
* Sound write handlers
*
*************************************/
void midzunit_state::narc_sound_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
// check for out-of-bounds accesses
if (offset)
{
logerror("%08X:Unexpected write to sound (hi) = %04X\n", m_maincpu->pc(), data);
return;
}
// call through based on the sound type
if (ACCESSING_BITS_0_7 && ACCESSING_BITS_8_15)
m_narc_sound->write(data);
}
void midyunit_cvsd_state::cvsd_sound_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
// check for out-of-bounds accesses
if (offset)
{
logerror("%08X:Unexpected write to sound (hi) = %04X\n", m_maincpu->pc(), data);
return;
}
// call through based on the sound type
if (ACCESSING_BITS_0_7 && ACCESSING_BITS_8_15)
{
m_cvsd_sound->reset_write(BIT(~data, 8));
m_cvsd_sound->write((data & 0xff) | ((data & 0x200) >> 1));
}
}
void midyunit_adpcm_state::adpcm_sound_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
// check for out-of-bounds accesses
if (offset)
{
logerror("%08X:Unexpected write to sound (hi) = %04X\n", m_maincpu->pc(), data);
return;
}
// call through based on the sound type
if (ACCESSING_BITS_0_7 && ACCESSING_BITS_8_15)
{
m_adpcm_sound->reset_write(BIT(~data, 8));
m_adpcm_sound->write(data);
}
}
void mkyawdim_state::yawdim_sound_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
// call through based on the sound type
if (ACCESSING_BITS_0_7 && ACCESSING_BITS_8_15)
{
m_soundlatch->write(data);
m_audiocpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
}
}
void mkyawdim_state::yawdim2_sound_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
// call through based on the sound type
if (ACCESSING_BITS_0_7 && ACCESSING_BITS_8_15)
{
m_soundlatch->write(data);
}
}