/***************************************************************************
Nintendo 8080 sound emulation
***************************************************************************/
#include "driver.h"
#include "cpu/i8039/i8039.h"
#include "sound/sn76477.h"
#include "sound/dac.h"
#include "includes/n8080.h"
#include <math.h>
static int n8080_hardware;
static emu_timer* sound_timer[3];
static int helifire_dac_phase;
static const double ATTACK_RATE = 10e-6 * 500;
static const double DECAY_RATE = 10e-6 * 16000;
static double helifire_dac_volume;
static double helifire_dac_timing;
static UINT16 prev_sound_pins;
static UINT16 curr_sound_pins;
static int mono_flop[3];
struct SN76477interface sheriff_sn76477_interface =
{
RES_K(36) , /* 04 */
RES_K(100) , /* 05 */
CAP_N(1) , /* 06 */
RES_K(620) , /* 07 */
CAP_U(1) , /* 08 */
RES_K(20) , /* 10 */
RES_K(150) , /* 11 */
RES_K(47) , /* 12 */
0 , /* 16 */
CAP_N(1) , /* 17 */
RES_M(1.5) , /* 18 */
0 , /* 19 */
RES_M(1.5) , /* 20 */
CAP_N(47) , /* 21 */
CAP_N(47) , /* 23 */
RES_K(560) , /* 24 */
0, /* 22 vco */
0, /* 26 mixer A */
0, /* 25 mixer B */
0, /* 27 mixer C */
1, /* 1 envelope 1 */
0, /* 28 envelope 2 */
1 /* 9 enable */
};
struct SN76477interface spacefev_sn76477_interface =
{
RES_K(36) , /* 04 */
RES_K(150) , /* 05 */
CAP_N(1) , /* 06 */
RES_M(1) , /* 07 */
CAP_U(1) , /* 08 */
RES_K(20) , /* 10 */
RES_K(150) , /* 11 */
RES_K(47) , /* 12 */
0 , /* 16 */
CAP_N(1) , /* 17 */
RES_M(1.5) , /* 18 */
0 , /* 19 */
RES_M(1) , /* 20 */
CAP_N(47) , /* 21 */
CAP_N(47) , /* 23 */
RES_K(820) , /* 24 */
0, /* 22 vco */
0, /* 26 mixer A */
0, /* 25 mixer B */
0, /* 27 mixer C */
1, /* 1 envelope 1 */
0, /* 28 envelope 2 */
1 /* 9 enable */
};
static void spacefev_update_SN76477_status(void)
{
double dblR0 = RES_M(1.0);
double dblR1 = RES_M(1.5);
if (!mono_flop[0])
{
dblR0 = 1 / (1 / RES_K(150) + 1 / dblR0); /* ? */
}
if (!mono_flop[1])
{
dblR1 = 1 / (1 / RES_K(620) + 1 / dblR1); /* ? */
}
SN76477_decay_res_w(0, dblR0);
SN76477_vco_res_w(0, dblR1);
SN76477_enable_w(0,
!mono_flop[0] &&
!mono_flop[1] &&
!mono_flop[2]);
SN76477_vco_w(0, mono_flop[1]);
SN76477_mixer_b_w(0, mono_flop[0]);
}
static void sheriff_update_SN76477_status(void)
{
if (mono_flop[1])
{
SN76477_vco_voltage_w(0, 5);
}
else
{
SN76477_vco_voltage_w(0, 0);
}
SN76477_enable_w(0,
!mono_flop[0] &&
!mono_flop[1]);
SN76477_vco_w(0, mono_flop[0]);
SN76477_mixer_b_w(0, !mono_flop[0]);
}
static void update_SN76477_status(void)
{
if (n8080_hardware == 1)
{
spacefev_update_SN76477_status();
}
if (n8080_hardware == 2)
{
sheriff_update_SN76477_status();
}
}
static void start_mono_flop(int n, attotime expire)
{
mono_flop[n] = 1;
update_SN76477_status();
timer_adjust(sound_timer[n], expire, n, attotime_zero);
}
static void stop_mono_flop(int n)
{
mono_flop[n] = 0;
update_SN76477_status();
timer_adjust(sound_timer[n], attotime_never, n, attotime_never);
}
static TIMER_CALLBACK( stop_mono_flop_callback )
{
stop_mono_flop(param);
}
static void spacefev_sound_pins_changed(void)
{
UINT16 changes = ~curr_sound_pins & prev_sound_pins;
if (changes & (1 << 0x3))
{
stop_mono_flop(1);
}
if (changes & ((1 << 0x3) | (1 << 0x6)))
{
stop_mono_flop(2);
}
if (changes & (1 << 0x3))
{
start_mono_flop(0, ATTOTIME_IN_USEC(550 * 36 * 100));
}
if (changes & (1 << 0x6))
{
start_mono_flop(1, ATTOTIME_IN_USEC(550 * 22 * 33));
}
if (changes & (1 << 0x4))
{
start_mono_flop(2, ATTOTIME_IN_USEC(550 * 22 * 33));
}
if (changes & ((1 << 0x2) | (1 << 0x3) | (1 << 0x5)))
{
cpunum_set_input_line(1, 0, PULSE_LINE);
}
}
static void sheriff_sound_pins_changed(void)
{
UINT16 changes = ~curr_sound_pins & prev_sound_pins;
if (changes & (1 << 0x6))
{
stop_mono_flop(1);
}
if (changes & (1 << 0x6))
{
start_mono_flop(0, ATTOTIME_IN_USEC(550 * 33 * 33));
}
if (changes & (1 << 0x4))
{
start_mono_flop(1, ATTOTIME_IN_USEC(550 * 33 * 33));
}
if (changes & ((1 << 0x2) | (1 << 0x3) | (1 << 0x5)))
{
cpunum_set_input_line(1, 0, PULSE_LINE);
}
}
static void helifire_sound_pins_changed(void)
{
UINT16 changes = ~curr_sound_pins & prev_sound_pins;
/* ((curr_sound_pins >> 0xA) & 1) not emulated */
/* ((curr_sound_pins >> 0xB) & 1) not emulated */
/* ((curr_sound_pins >> 0xC) & 1) not emulated */
if (changes & (1 << 6))
{
cpunum_set_input_line(1, 0, PULSE_LINE);
}
}
static void sound_pins_changed(void)
{
if (n8080_hardware == 1)
{
spacefev_sound_pins_changed();
}
if (n8080_hardware == 2)
{
sheriff_sound_pins_changed();
}
if (n8080_hardware == 3)
{
helifire_sound_pins_changed();
}
prev_sound_pins = curr_sound_pins;
}
static void delayed_sound_1(int data)
{
static UINT8 prev_data = 0;
curr_sound_pins &= ~(
(1 << 0x7) |
(1 << 0x5) |
(1 << 0x6) |
(1 << 0x3) |
(1 << 0x4) |
(1 << 0x1));
if (~data & 0x01) curr_sound_pins |= 1 << 0x7;
if (~data & 0x02) curr_sound_pins |= 1 << 0x5; /* pulse */
if (~data & 0x04) curr_sound_pins |= 1 << 0x6; /* pulse */
if (~data & 0x08) curr_sound_pins |= 1 << 0x3; /* pulse (except in Helifire) */
if (~data & 0x10) curr_sound_pins |= 1 << 0x4; /* pulse (except in Helifire) */
if (~data & 0x20) curr_sound_pins |= 1 << 0x1;
if (n8080_hardware == 1)
{
if (data & ~prev_data & 0x10)
{
spacefev_start_red_cannon();
}
spacefev_red_screen = data & 0x08;
}
sound_pins_changed();
prev_data = data;
}
static TIMER_CALLBACK( delayed_sound_1_callback )
{
delayed_sound_1(param);
}
static void delayed_sound_2(int data)
{
curr_sound_pins &= ~(
(1 << 0x8) |
(1 << 0x9) |
(1 << 0xA) |
(1 << 0xB) |
(1 << 0x2) |
(1 << 0xC));
if (~data & 0x01) curr_sound_pins |= 1 << 0x8;
if (~data & 0x02) curr_sound_pins |= 1 << 0x9;
if (~data & 0x04) curr_sound_pins |= 1 << 0xA;
if (~data & 0x08) curr_sound_pins |= 1 << 0xB;
if (~data & 0x10) curr_sound_pins |= 1 << 0x2; /* pulse */
if (~data & 0x20) curr_sound_pins |= 1 << 0xC;
if (n8080_hardware == 1)
{
flip_screen = data & 0x20;
}
if (n8080_hardware == 3)
{
helifire_flash = data & 0x20;
}
sound_pins_changed();
}
static TIMER_CALLBACK( delayed_sound_2_callback )
{
delayed_sound_2(param);
}
WRITE8_HANDLER( n8080_sound_1_w )
{
timer_call_after_resynch(data, delayed_sound_1_callback); /* force CPUs to sync */
}
WRITE8_HANDLER( n8080_sound_2_w )
{
timer_call_after_resynch(data, delayed_sound_2_callback); /* force CPUs to sync */
}
static READ8_HANDLER( n8080_8035_p1_r )
{
UINT8 val = 0;
if ((curr_sound_pins >> 0xB) & 1) val |= 0x01;
if ((curr_sound_pins >> 0xA) & 1) val |= 0x02;
if ((curr_sound_pins >> 0x9) & 1) val |= 0x04;
if ((curr_sound_pins >> 0x8) & 1) val |= 0x08;
if ((curr_sound_pins >> 0x5) & 1) val |= 0x10;
if ((curr_sound_pins >> 0x3) & 1) val |= 0x20;
if ((curr_sound_pins >> 0x2) & 1) val |= 0x40;
if ((curr_sound_pins >> 0x1) & 1) val |= 0x80;
return val;
}
static READ8_HANDLER( n8080_8035_t0_r )
{
return (curr_sound_pins >> 0x7) & 1;
}
static READ8_HANDLER( n8080_8035_t1_r )
{
return (curr_sound_pins >> 0xC) & 1;
}
static READ8_HANDLER( helifire_8035_t0_r )
{
return (curr_sound_pins >> 0x3) & 1;
}
static READ8_HANDLER( helifire_8035_t1_r )
{
return (curr_sound_pins >> 0x4) & 1;
}
static READ8_HANDLER( helifire_8035_external_ram_r )
{
UINT8 val = 0;
if ((curr_sound_pins >> 0x7) & 1) val |= 0x01;
if ((curr_sound_pins >> 0x8) & 1) val |= 0x02;
if ((curr_sound_pins >> 0x9) & 1) val |= 0x04;
if ((curr_sound_pins >> 0x1) & 1) val |= 0x08;
return val;
}
static READ8_HANDLER( helifire_8035_p2_r )
{
return ((curr_sound_pins >> 0xC) & 1) ? 0x10 : 0x00; /* not used */
}
static WRITE8_HANDLER( n8080_dac_w )
{
DAC_data_w(0, data & 0x80);
}
static WRITE8_HANDLER( helifire_dac_w )
{
DAC_data_w(0, data * helifire_dac_volume);
}
static WRITE8_HANDLER( helifire_sound_ctrl_w )
{
helifire_dac_phase = data & 0x80;
/* data & 0x40 not emulated */
/* data & 0x20 not emulated */
if (helifire_dac_phase)
{
helifire_dac_timing = ATTACK_RATE * log(1 - helifire_dac_volume);
}
else
{
helifire_dac_timing = DECAY_RATE * log(helifire_dac_volume);
}
helifire_dac_timing += attotime_to_double(timer_get_time());
}
static TIMER_CALLBACK( spacefev_vco_voltage_timer )
{
double voltage = 0;
if (mono_flop[2])
{
voltage = 5 * (1 - exp(- attotime_to_double(timer_timeelapsed(sound_timer[2])) / 0.22));
}
SN76477_vco_voltage_w(0, voltage);
}
static TIMER_CALLBACK( helifire_dac_volume_timer )
{
double t = helifire_dac_timing - attotime_to_double(timer_get_time());
if (helifire_dac_phase)
{
helifire_dac_volume = 1 - exp(t / ATTACK_RATE);
}
else
{
helifire_dac_volume = exp(t / DECAY_RATE);
}
}
static MACHINE_RESET( spacefev_sound )
{
n8080_hardware = 1;
timer_pulse(ATTOTIME_IN_HZ(1000), 0, spacefev_vco_voltage_timer);
sound_timer[0] = timer_alloc(stop_mono_flop_callback);
sound_timer[1] = timer_alloc(stop_mono_flop_callback);
sound_timer[2] = timer_alloc(stop_mono_flop_callback);
mono_flop[0] = 0;
mono_flop[1] = 0;
mono_flop[2] = 0;
delayed_sound_1(0);
delayed_sound_2(0);
}
static MACHINE_RESET( sheriff_sound )
{
n8080_hardware = 2;
sound_timer[0] = timer_alloc(stop_mono_flop_callback);
sound_timer[1] = timer_alloc(stop_mono_flop_callback);
mono_flop[0] = 0;
mono_flop[1] = 0;
delayed_sound_1(0);
delayed_sound_2(0);
}
static MACHINE_RESET( helifire_sound )
{
n8080_hardware = 3;
timer_pulse(ATTOTIME_IN_HZ(1000), 0, helifire_dac_volume_timer);
helifire_dac_volume = 1;
helifire_dac_timing = 0;
delayed_sound_1(0);
delayed_sound_2(0);
helifire_dac_phase = 0;
}
static ADDRESS_MAP_START( n8080_sound_cpu_map, ADDRESS_SPACE_PROGRAM, 8 )
ADDRESS_MAP_FLAGS( AMEF_ABITS(10) )
AM_RANGE(0x0000, 0x03ff) AM_ROM
ADDRESS_MAP_END
static ADDRESS_MAP_START( n8080_sound_io_map, ADDRESS_SPACE_IO, 8 )
AM_RANGE(I8039_t0, I8039_t0) AM_READ(n8080_8035_t0_r)
AM_RANGE(I8039_t1, I8039_t1) AM_READ(n8080_8035_t1_r)
AM_RANGE(I8039_p1, I8039_p1) AM_READ(n8080_8035_p1_r)
AM_RANGE(I8039_p2, I8039_p2) AM_WRITE(n8080_dac_w)
ADDRESS_MAP_END
static ADDRESS_MAP_START( helifire_sound_io_map, ADDRESS_SPACE_IO, 8 )
AM_RANGE(I8039_t0, I8039_t0) AM_READ(helifire_8035_t0_r)
AM_RANGE(I8039_t1, I8039_t1) AM_READ(helifire_8035_t1_r)
AM_RANGE(I8039_p2, I8039_p2) AM_READ(helifire_8035_p2_r)
AM_RANGE(0x00, 0x7f) AM_READ(helifire_8035_external_ram_r)
AM_RANGE(I8039_p1, I8039_p1) AM_WRITE(helifire_dac_w)
AM_RANGE(I8039_p2, I8039_p2) AM_WRITE(helifire_sound_ctrl_w)
ADDRESS_MAP_END
MACHINE_DRIVER_START( spacefev_sound )
/* basic machine hardware */
MDRV_CPU_ADD(I8035, 6000000 / I8039_CLOCK_DIVIDER)
/* audio CPU */
MDRV_CPU_PROGRAM_MAP(n8080_sound_cpu_map, 0)
MDRV_CPU_IO_MAP(n8080_sound_io_map, 0)
MDRV_MACHINE_RESET(spacefev_sound)
/* sound hardware */
MDRV_SPEAKER_STANDARD_MONO("mono")
MDRV_SOUND_ADD(DAC, 0)
MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.30)
MDRV_SOUND_ADD(SN76477, 0)
MDRV_SOUND_CONFIG(spacefev_sn76477_interface)
MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.35)
MACHINE_DRIVER_END
MACHINE_DRIVER_START( sheriff_sound )
/* basic machine hardware */
MDRV_CPU_ADD(I8035, 6000000 / I8039_CLOCK_DIVIDER)
/* audio CPU */
MDRV_CPU_PROGRAM_MAP(n8080_sound_cpu_map, 0)
MDRV_CPU_IO_MAP(n8080_sound_io_map, 0)
MDRV_MACHINE_RESET(sheriff_sound)
/* sound hardware */
MDRV_SPEAKER_STANDARD_MONO("mono")
MDRV_SOUND_ADD(DAC, 0)
MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.30)
MDRV_SOUND_ADD(SN76477, 0)
MDRV_SOUND_CONFIG(sheriff_sn76477_interface)
MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.35)
MACHINE_DRIVER_END
MACHINE_DRIVER_START( helifire_sound )
/* basic machine hardware */
MDRV_CPU_ADD(I8035, 6000000 / I8039_CLOCK_DIVIDER)
/* audio CPU */
MDRV_CPU_PROGRAM_MAP(n8080_sound_cpu_map, 0)
MDRV_CPU_IO_MAP(helifire_sound_io_map, 0)
MDRV_MACHINE_RESET(helifire_sound)
/* sound hardware */
MDRV_SPEAKER_STANDARD_MONO("mono")
MDRV_SOUND_ADD(DAC, 0)
MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.30)
MACHINE_DRIVER_END