diff options
Diffstat (limited to 'trunk/src/mame/audio/segag80r.c')
-rw-r--r-- | trunk/src/mame/audio/segag80r.c | 1016 |
1 files changed, 1016 insertions, 0 deletions
diff --git a/trunk/src/mame/audio/segag80r.c b/trunk/src/mame/audio/segag80r.c new file mode 100644 index 00000000000..c06ecb9b704 --- /dev/null +++ b/trunk/src/mame/audio/segag80r.c @@ -0,0 +1,1016 @@ +/*************************************************************************** + + Sega G-80 raster hardware + + Across these games, there's a mixture of discrete sound circuitry, + speech boards, ADPCM samples, and a TMS3617 music chip. + +***************************************************************************/ + +#include "emu.h" +#include "cpu/mcs48/mcs48.h" +#include "includes/segag80r.h" +#include "machine/8255ppi.h" +#include "machine/i8243.h" +#include "sound/samples.h" +#include "sound/tms36xx.h" +#include "sound/dac.h" + + + +/************************************* + * + * Constants + * + *************************************/ + +#define SEGA005_555_TIMER_FREQ (1.44 / ((15000 + 2 * 4700) * 1.5e-6)) +#define SEGA005_COUNTER_FREQ (100000) /* unknown, just a guess */ + +DEVICE_GET_INFO( sega005_sound ); + +DECLARE_LEGACY_SOUND_DEVICE(SEGA005, sega005_sound); +DEFINE_LEGACY_SOUND_DEVICE(SEGA005, sega005_sound); + + + +/************************************* + * + * Astro Blaster sound hardware + * + *************************************/ + +static SOUND_START( astrob ); + +/* + Description of Astro Blaster sounds (in the hope of future discrete goodness): + + CD4017 = decade counter with one output per decoded stage (10 outputs altogether) + CD4024 = 7-bit counter with 7 outputs + + + "V" signal + ---------- + CD4017 @ U15: + reset by RATE RESET signal = 1 + clocked by falling edge of ATTACK signal + +12V output from here goes through a diode and one of 10 resistors: + 0 = 120k + 1 = 82k + 2 = 62k + 3 = 56k + 4 = 47k + 5 = 39k + 6 = 35k + 7 = 27k + 8 = 24k + 9 = 22k + and then in series through a 22k resistor + + Op-amp @ U6 takes the WARP signal and the output of CD4017 @ U15 + and forms the signal "V" which is used to control the invader + sounds + + + How to calculate the output voltage at U16 labeled (V). + (Derrick Renaud) + + First you have an inverting amp. To get the gain you + use G=-Rf/Ri, where Rf=R178=22k. Ri is the selected + resistor on the output of U15. + + The input voltage to the amp (pin 6) will always be + about 12V - 0.5V (diode drop in low current circuit) = + 11.5V. + + Now you need to calculate the reference voltage on the + + input (pin 5). Depending on the state of WARP... + + If the warp data is 0, then U31 inverts it to an Open + Collector high, meaning WARP is out of circuit. So: + Vref = 12V * (R163)/(R162+R163) + = 12V * 10k/(10K+4.7k) + = 8.163V + + When warp data is 1, then U31 inverts it to low, + grounding R164 putting it in parallel with R163, + giving: + Vref = 12V * (R163||R164)/(R163||R164 +R162) + = 12V * 5k/(5k+4.7k) + = 6.186V + + Now to get the control voltage V: + V = (Vi - Vref) * G + Vref + = (11.5V - Vref) * G + Vref + + That gives you the control voltage at V. From there I + would have to millman the voltage with the internal + voltage/resistors of the 555 to get the actual used + control voltage. + + But it seems you just want a range, so just use the + above info to get the highest and lowest voltages + generated, and create the frequency shift you desire. + Remember as the control voltage (V) lowers, the + frequency increases. + + + + INVADER-1 output + ---------------- + + + + + INVADER-2 output + ---------------- + 555 timer @ U13 in astable mode with the following parameters: + R1 = 10k + R2 = 100k + C = 0.0022u + CV = "V" signal + Reset = (PORT076 & 0x02) + Output goes to CD4024 @ U12 + + CD4024 @ U12: + reset through some unknown means + clocked by 555 timer @ U13 + +12 output from here goes through a resistor ladder: + Q1 -> 82k + Q2 -> 39k + Q3 -> 22k + Q4 -> 10k + Summed output from here is INVADER-2 + + + INVADER-3 output + ---------------- + 555 timer at U17 in astable mode with the following parameters: + R1 = 10k + R2 = 68k + C = 0.1u + CV = some combination of "V" and "W" signals + Reset = (PORT076 & 0x04) + Output from here is INVADER-3 + +*/ + +static const char *const astrob_sample_names[] = +{ + "*astrob", + "invadr1.wav", /* 0 */ + "winvadr1.wav", /* 1 */ + "invadr2.wav", /* 2 */ + "winvadr2.wav", /* 3 */ + "invadr3.wav", /* 4 */ + "winvadr3.wav", /* 5 */ + "invadr4.wav", /* 6 */ + "winvadr4.wav", /* 7 */ + "asteroid.wav", /* 8 */ + "refuel.wav", /* 9 */ + "pbullet.wav", /* 10 */ + "ebullet.wav", /* 11 */ + "eexplode.wav", /* 12 */ + "pexplode.wav", /* 13 */ + "deedle.wav", /* 14 */ + "sonar.wav", /* 15 */ + 0 +}; + + +static const samples_interface astrob_samples_interface = +{ + 11, + astrob_sample_names +}; + + +MACHINE_CONFIG_FRAGMENT( astrob_sound_board ) + + MCFG_SOUND_START(astrob) + + /* sound hardware */ + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SOUND_CONFIG(astrob_samples_interface) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) +MACHINE_CONFIG_END + + + +/************************************* + * + * Startup configuration + * + *************************************/ + +static SOUND_START( astrob ) +{ + segag80r_state *state = machine.driver_data<segag80r_state>(); + state_save_register_global_array(machine, state->m_sound_state); + state_save_register_global(machine, state->m_sound_rate); +} + + + +/************************************* + * + * Astro Blaster sound triggers + * + *************************************/ + +WRITE8_HANDLER( astrob_sound_w ) +{ + segag80r_state *state = space->machine().driver_data<segag80r_state>(); + static const float attack_resistor[10] = + { + 120.0f, 82.0f, 62.0f, 56.0f, 47.0f, 39.0f, 33.0f, 27.0f, 24.0f, 22.0f + }; + device_t *samples = space->machine().device("samples"); + float freq_factor; + + UINT8 diff = data ^ state->m_sound_state[offset]; + state->m_sound_state[offset] = data; + + switch (offset) + { + case 0: + /* INVADER-1: channel 0 */ + if ((diff & 0x01) && !(data & 0x01)) sample_start(samples, 0, (data & 0x80) ? 0 : 1, TRUE); + if ((data & 0x01) && sample_playing(samples, 0)) sample_stop(samples, 0); + + /* INVADER-2: channel 1 */ + if ((diff & 0x02) && !(data & 0x02)) sample_start(samples, 1, (data & 0x80) ? 2 : 3, TRUE); + if ((data & 0x02) && sample_playing(samples, 1)) sample_stop(samples, 1); + + /* INVADER-3: channel 2 */ + if ((diff & 0x04) && !(data & 0x04)) sample_start(samples, 2, (data & 0x80) ? 4 : 5, TRUE); + if ((data & 0x04) && sample_playing(samples, 2)) sample_stop(samples, 2); + + /* INVADER-4: channel 3 */ + if ((diff & 0x08) && !(data & 0x08)) sample_start(samples, 3, (data & 0x80) ? 6 : 7, TRUE); + if ((data & 0x08) && sample_playing(samples, 3)) sample_stop(samples, 3); + + /* ASTROIDS: channel 4 */ + if ((diff & 0x10) && !(data & 0x10)) sample_start(samples, 4, 8, TRUE); + if ((data & 0x10) && sample_playing(samples, 4)) sample_stop(samples, 4); + + /* MUTE */ + space->machine().sound().system_mute(data & 0x20); + + /* REFILL: channel 5 */ + if (!(data & 0x40) && !sample_playing(samples, 5)) sample_start(samples, 5, 9, FALSE); + if ( (data & 0x40) && sample_playing(samples, 5)) sample_stop(samples, 5); + + /* WARP: changes which sample is played for the INVADER samples above */ + if (diff & 0x80) + { + if (sample_playing(samples, 0)) sample_start(samples, 0, (data & 0x80) ? 0 : 1, TRUE); + if (sample_playing(samples, 1)) sample_start(samples, 1, (data & 0x80) ? 2 : 3, TRUE); + if (sample_playing(samples, 2)) sample_start(samples, 2, (data & 0x80) ? 4 : 5, TRUE); + if (sample_playing(samples, 3)) sample_start(samples, 3, (data & 0x80) ? 6 : 7, TRUE); + } + break; + + case 1: + /* LASER #1: channel 6 */ + if ((diff & 0x01) && !(data & 0x01)) sample_start(samples, 6, 10, FALSE); + + /* LASER #2: channel 7 */ + if ((diff & 0x02) && !(data & 0x02)) sample_start(samples, 7, 11, FALSE); + + /* SHORT EXPL: channel 8 */ + if ((diff & 0x04) && !(data & 0x04)) sample_start(samples, 8, 12, FALSE); + + /* LONG EXPL: channel 8 */ + if ((diff & 0x08) && !(data & 0x08)) sample_start(samples, 8, 13, FALSE); + + /* ATTACK RATE */ + if ((diff & 0x10) && !(data & 0x10)) state->m_sound_rate = (state->m_sound_rate + 1) % 10; + + /* RATE RESET */ + if (!(data & 0x20)) state->m_sound_rate = 0; + + /* BONUS: channel 9 */ + if ((diff & 0x40) && !(data & 0x40)) sample_start(samples, 9, 14, FALSE); + + /* SONAR: channel 10 */ + if ((diff & 0x80) && !(data & 0x80)) sample_start(samples, 10, 15, FALSE); + break; + } + + /* the samples were recorded with sound_rate = 0, so we need to scale */ + /* the frequency as a fraction of that; these equations come from */ + /* Derrick's analysis above; we compute the inverted scale factor to */ + /* account for the fact that frequency goes up as CV goes down */ + /* WARP is already taken into account by the differing samples above */ + freq_factor = (11.5f - 8.163f) * (-22.0f / attack_resistor[0]) + 8.163f; + freq_factor /= (11.5f - 8.163f) * (-22.0f / attack_resistor[state->m_sound_rate]) + 8.163f; + + /* adjust the sample rate of invader sounds based the sound_rate */ + /* this is an approximation */ + if (sample_playing(samples, 0)) sample_set_freq(samples, 0, sample_get_base_freq(samples, 0) * freq_factor); + if (sample_playing(samples, 1)) sample_set_freq(samples, 1, sample_get_base_freq(samples, 1) * freq_factor); + if (sample_playing(samples, 2)) sample_set_freq(samples, 2, sample_get_base_freq(samples, 2) * freq_factor); + if (sample_playing(samples, 3)) sample_set_freq(samples, 3, sample_get_base_freq(samples, 3) * freq_factor); +} + + + +/************************************* + * + * 005 sound hardware + * + *************************************/ + +static SOUND_START( sega005 ); +static STREAM_UPDATE( sega005_stream_update ); +static TIMER_CALLBACK( sega005_auto_timer ); +static WRITE8_DEVICE_HANDLER( sega005_sound_a_w ); +static WRITE8_DEVICE_HANDLER( sega005_sound_b_w ); + +/* + 005 + + The Sound Board consists of the following: + + An 8255: + Port A controls the sounds that use discrete circuitry + A0 - Large Expl. Sound Trig + A1 - Small Expl. Sound Trig + A2 - Drop Sound Bomb Trig + A3 - Shoot Sound Pistol Trig + A4 - Missile Sound Trig + A5 - Helicopter Sound Trig + A6 - Whistle Sound Trig + A7 - <unused> + + Port B controls the melody generator (described below) + + Port C is apparently unused + + + Melody Generator: + + 555 timer frequency = 1.44/((R1 + 2R2)*C) + R1 = 15e3 + R2 = 4.7e3 + C=1.5e-6 + Frequency = 39.344 Hz + + Auto timer is enabled if port B & 0x20 == 1 + Auto timer is reset if 2716 value & 0x20 == 0 + + Manual timer is enabled if port B & 0x20 == 0 + Manual timer is clocked if port B & 0x40 goes from 0 to 1 + + Both auto and manual timers clock LS393 counter + Counter is held to 0 if port B & 0x10 == 1 + + Output of LS393 >> 1 selects low 7 bits of lookup in 2716. + High 4 bits come from port B bits 0-3. + + Low 5 bits of output from 2716 look up value in 6331 PROM at U8 (32x8) + + 8-bit output of 6331 at U8 is loaded into pair of LS161 counters whenever they overflow. + LS161 counters are clocked somehow (not clear how) + + Carry output from LS161 counters (overflowing 8 bits) goes to the B + input on the LS293 counter at U14. + Rising edge of B input clocks bit 1 of counter (effectively adding 2). + Output B (bit 1) is mixed with output D (bit 3) with different weights + through a small RC circuit and fed into the 4391 input at U32. + + The 4391 output is the final output. +*/ + +static const char *const sega005_sample_names[] = +{ + "*005", + "lexplode.wav", /* 0 */ + "sexplode.wav", /* 1 */ + "dropbomb.wav", /* 2 */ + "shoot.wav", /* 3 */ + "missile.wav", /* 4 */ + "helicopt.wav", /* 5 */ + "whistle.wav", /* 6 */ + 0 +}; + + +static const samples_interface sega005_samples_interface = +{ + 7, + sega005_sample_names +}; + + +static const ppi8255_interface ppi8255_005_intf = +{ + DEVCB_NULL, + DEVCB_NULL, + DEVCB_NULL, + DEVCB_HANDLER(sega005_sound_a_w), + DEVCB_HANDLER(sega005_sound_b_w), + DEVCB_NULL +}; + + +MACHINE_CONFIG_FRAGMENT( 005_sound_board ) + + MCFG_PPI8255_ADD( "ppi8255", ppi8255_005_intf ) + + /* sound hardware */ + MCFG_SOUND_START(sega005) + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SOUND_CONFIG(sega005_samples_interface) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) + + MCFG_SOUND_ADD("005", SEGA005, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) +MACHINE_CONFIG_END + + + +/************************************* + * + * Startup configuration + * + *************************************/ + +static SOUND_START( sega005 ) +{ + segag80r_state *state = machine.driver_data<segag80r_state>(); + state_save_register_global_array(machine, state->m_sound_state); + state_save_register_global(machine, state->m_sound_addr); + state_save_register_global(machine, state->m_sound_data); + state_save_register_global(machine, state->m_square_state); + state_save_register_global(machine, state->m_square_count); +} + + + +/************************************* + * + * 005 sound triggers + * + *************************************/ + +static WRITE8_DEVICE_HANDLER( sega005_sound_a_w ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + device_t *samples = device->machine().device("samples"); + UINT8 diff = data ^ state->m_sound_state[0]; + state->m_sound_state[0] = data; + + /* LARGE EXPL: channel 0 */ + if ((diff & 0x01) && !(data & 0x01)) sample_start(samples, 0, 0, FALSE); + + /* SMALL EXPL: channel 1 */ + if ((diff & 0x02) && !(data & 0x02)) sample_start(samples, 1, 1, FALSE); + + /* DROP BOMB: channel 2 */ + if ((diff & 0x04) && !(data & 0x04)) sample_start(samples, 2, 2, FALSE); + + /* SHOOT PISTOL: channel 3 */ + if ((diff & 0x08) && !(data & 0x08)) sample_start(samples, 3, 3, FALSE); + + /* MISSILE: channel 4 */ + if ((diff & 0x10) && !(data & 0x10)) sample_start(samples, 4, 4, FALSE); + + /* HELICOPTER: channel 5 */ + if ((diff & 0x20) && !(data & 0x20) && !sample_playing(samples, 5)) sample_start(samples, 5, 5, TRUE); + if ((diff & 0x20) && (data & 0x20)) sample_stop(samples, 5); + + /* WHISTLE: channel 6 */ + if ((diff & 0x40) && !(data & 0x40) && !sample_playing(samples, 6)) sample_start(samples, 6, 6, TRUE); + if ((diff & 0x40) && (data & 0x40)) sample_stop(samples, 6); +} + + +INLINE void sega005_update_sound_data(running_machine &machine) +{ + segag80r_state *state = machine.driver_data<segag80r_state>(); + UINT8 newval = machine.region("005")->base()[state->m_sound_addr]; + UINT8 diff = newval ^ state->m_sound_data; + + //mame_printf_debug(" [%03X] = %02X\n", state->m_sound_addr, newval); + + /* latch the new value */ + state->m_sound_data = newval; + + /* if bit 5 goes high, we reset the timer */ + if ((diff & 0x20) && !(newval & 0x20)) + { + //mame_printf_debug("Stopping timer\n"); + state->m_sega005_sound_timer->adjust(attotime::never); + } + + /* if bit 5 goes low, we start the timer again */ + if ((diff & 0x20) && (newval & 0x20)) + { + //mame_printf_debug("Starting timer\n"); + state->m_sega005_sound_timer->adjust(attotime::from_hz(SEGA005_555_TIMER_FREQ), 0, attotime::from_hz(SEGA005_555_TIMER_FREQ)); + } +} + + +static WRITE8_DEVICE_HANDLER( sega005_sound_b_w ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + /* + D6: manual timer clock (0->1) + D5: 0 = manual timer, 1 = auto timer + D4: 1 = hold/reset address counter to 0 + D3-D0: upper 4 bits of ROM address + */ + UINT8 diff = data ^ state->m_sound_state[1]; + state->m_sound_state[1] = data; + + //mame_printf_debug("sound[%d] = %02X\n", 1, data); + + /* force a stream update */ + state->m_sega005_stream->update(); + + /* ROM address */ + state->m_sound_addr = ((data & 0x0f) << 7) | (state->m_sound_addr & 0x7f); + + /* reset both sound address and square wave counters */ + if (data & 0x10) + { + state->m_sound_addr &= 0x780; + state->m_square_state = 0; + } + + /* manual clock */ + if ((diff & 0x40) && (data & 0x40) && !(data & 0x20) && !(data & 0x10)) + state->m_sound_addr = (state->m_sound_addr & 0x780) | ((state->m_sound_addr + 1) & 0x07f); + + /* update the sound data */ + sega005_update_sound_data(device->machine()); +} + + + +/************************************* + * + * 005 custom sound generation + * + *************************************/ + +static DEVICE_START( sega005_sound ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + running_machine &machine = device->machine(); + + /* create the stream */ + state->m_sega005_stream = device->machine().sound().stream_alloc(*device, 0, 1, SEGA005_COUNTER_FREQ, NULL, sega005_stream_update); + + /* create a timer for the 555 */ + state->m_sega005_sound_timer = machine.scheduler().timer_alloc(FUNC(sega005_auto_timer)); + + /* set the initial sound data */ + state->m_sound_data = 0x00; + sega005_update_sound_data(machine); +} + + +DEVICE_GET_INFO( sega005_sound ) +{ + switch (state) + { + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(sega005_sound); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "005 Custom"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + } +} + + +static STREAM_UPDATE( sega005_stream_update ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + const UINT8 *sound_prom = device->machine().region("proms")->base(); + int i; + + /* no implementation yet */ + for (i = 0; i < samples; i++) + { + if (!(state->m_sound_state[1] & 0x10) && (++state->m_square_count & 0xff) == 0) + { + state->m_square_count = sound_prom[state->m_sound_data & 0x1f]; + + /* hack - the RC should filter this out */ + if (state->m_square_count != 0xff) + state->m_square_state += 2; + } + + outputs[0][i] = (state->m_square_state & 2) ? 0x7fff : 0x0000; + } +} + + +static TIMER_CALLBACK( sega005_auto_timer ) +{ + segag80r_state *state = machine.driver_data<segag80r_state>(); + /* force an update then clock the sound address if not held in reset */ + state->m_sega005_stream->update(); + if ((state->m_sound_state[1] & 0x20) && !(state->m_sound_state[1] & 0x10)) + { + state->m_sound_addr = (state->m_sound_addr & 0x780) | ((state->m_sound_addr + 1) & 0x07f); + sega005_update_sound_data(machine); + } +} + + + +/************************************* + * + * Space Odyssey sound hardware + * + *************************************/ + +static SOUND_START( spaceod ); + +static const char *const spaceod_sample_names[] = +{ + "*spaceod", + "fire.wav", /* 0 */ + "bomb.wav", /* 1 */ + "eexplode.wav", /* 2 */ + "pexplode.wav", /* 3 */ + "warp.wav", /* 4 */ + "birth.wav", /* 5 */ + "scoreup.wav", /* 6 */ + "ssound.wav", /* 7 */ + "accel.wav", /* 8 */ + "damaged.wav", /* 9 */ + "erocket.wav", /* 10 */ + 0 +}; + + +static const samples_interface spaceod_samples_interface = +{ + 11, + spaceod_sample_names +}; + + +MACHINE_CONFIG_FRAGMENT( spaceod_sound_board ) + + /* sound hardware */ + MCFG_SOUND_START(spaceod) + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SOUND_CONFIG(spaceod_samples_interface) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) +MACHINE_CONFIG_END + + + +/************************************* + * + * Startup configuration + * + *************************************/ + +static SOUND_START( spaceod ) +{ + segag80r_state *state = machine.driver_data<segag80r_state>(); + state_save_register_global_array(machine, state->m_sound_state); +} + + + +/************************************* + * + * Space Odyssey sound triggers + * + *************************************/ + +WRITE8_HANDLER( spaceod_sound_w ) +{ + segag80r_state *state = space->machine().driver_data<segag80r_state>(); + device_t *samples = space->machine().device("samples"); + UINT8 diff = data ^ state->m_sound_state[offset]; + state->m_sound_state[offset] = data; + + switch (offset) + { + case 0: + /* BACK G: channel 0 */ + if ((diff & 0x01) && !(data & 0x01) && !sample_playing(samples, 0)) sample_start(samples, 0, 7, TRUE); + if ((diff & 0x01) && (data & 0x01)) sample_stop(samples, 0); + + /* SHORT EXP: channel 1 */ + if ((diff & 0x04) && !(data & 0x04)) sample_start(samples, 1, 2, FALSE); + + /* ACCELERATE: channel 2 */ + if ((diff & 0x10) && !(data & 0x10)) sample_start(samples, 2, 8, FALSE); + + /* BATTLE STAR: channel 3 */ + if ((diff & 0x20) && !(data & 0x20)) sample_start(samples, 3, 10, FALSE); + + /* D BOMB: channel 4 */ + if ((diff & 0x40) && !(data & 0x40)) sample_start(samples, 4, 1, FALSE); + + /* LONG EXP: channel 5 */ + if ((diff & 0x80) && !(data & 0x80)) sample_start(samples, 5, 3, FALSE); + break; + + case 1: + /* SHOT: channel 6 */ + if ((diff & 0x01) && !(data & 0x01)) sample_start(samples, 6, 0, FALSE); + + /* BONUS UP: channel 7 */ + if ((diff & 0x02) && !(data & 0x02)) sample_start(samples, 7, 6, FALSE); + + /* WARP: channel 8 */ + if ((diff & 0x08) && !(data & 0x08)) sample_start(samples, 8, 4, FALSE); + + /* APPEARANCE UFO: channel 9 */ + if ((diff & 0x40) && !(data & 0x40)) sample_start(samples, 9, 5, FALSE); + + /* BLACK HOLE: channel 10 */ + if ((diff & 0x80) && !(data & 0x80)) sample_start(samples, 10, 9, FALSE); + break; + } +} + + + +/************************************* + * + * Monster Bash sound hardware + * + *************************************/ + +static SOUND_START( monsterb ); +static WRITE8_DEVICE_HANDLER( monsterb_sound_a_w ); +static WRITE8_DEVICE_HANDLER( monsterb_sound_b_w ); +static READ8_DEVICE_HANDLER( n7751_status_r ); +static WRITE8_DEVICE_HANDLER( n7751_command_w ); +static WRITE8_DEVICE_HANDLER( n7751_rom_control_w ); +static READ8_HANDLER( n7751_rom_r ); +static READ8_HANDLER( n7751_command_r ); +static WRITE8_DEVICE_HANDLER( n7751_p2_w ); +static READ8_HANDLER( n7751_t1_r ); + +/* + Monster Bash + + The Sound Board is a fairly complex mixture of different components. + An 8255A-5 controls the interface to/from the sound board. + Port A connects to a TMS3617 (basic music synthesizer) circuit. + Port B connects to two sounds generated by discrete circuitry. + Port C connects to a NEC7751 (8048 CPU derivative) to control four "samples". +*/ + + +static const char *const monsterb_sample_names[] = +{ + "*monsterb", + "zap.wav", + "jumpdown.wav", + 0 +}; + + +static const samples_interface monsterb_samples_interface = +{ + 2, + monsterb_sample_names +}; + + +static const tms36xx_interface monsterb_tms3617_interface = +{ + TMS3617, + {0.5,0.5,0.5,0.5,0.5,0.5} /* decay times of voices */ +}; + + + +/************************************* + * + * N7751 memory maps + * + *************************************/ + +static ADDRESS_MAP_START( monsterb_7751_portmap, AS_IO, 8 ) + AM_RANGE(MCS48_PORT_T1, MCS48_PORT_T1) AM_READ(n7751_t1_r) + AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_READ(n7751_command_r) + AM_RANGE(MCS48_PORT_BUS, MCS48_PORT_BUS) AM_READ(n7751_rom_r) + AM_RANGE(MCS48_PORT_P1, MCS48_PORT_P1) AM_DEVWRITE("dac", dac_w) + AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_DEVWRITE("audio_8243", n7751_p2_w) + AM_RANGE(MCS48_PORT_PROG, MCS48_PORT_PROG) AM_DEVWRITE("audio_8243", i8243_prog_w) +ADDRESS_MAP_END + + + +/************************************* + * + * Machine driver + * + *************************************/ + +static const ppi8255_interface monsterb_ppi_intf = +{ + DEVCB_NULL, + DEVCB_NULL, + DEVCB_HANDLER(n7751_status_r), + DEVCB_HANDLER(monsterb_sound_a_w), + DEVCB_HANDLER(monsterb_sound_b_w), + DEVCB_HANDLER(n7751_command_w) +}; + + +MACHINE_CONFIG_FRAGMENT( monsterb_sound_board ) + + MCFG_PPI8255_ADD( "ppi8255", monsterb_ppi_intf ) + + /* basic machine hardware */ + MCFG_CPU_ADD("audiocpu", N7751, 6000000) + MCFG_CPU_IO_MAP(monsterb_7751_portmap) + + MCFG_I8243_ADD("audio_8243", NULL, n7751_rom_control_w) + + /* sound hardware */ + MCFG_SOUND_START(monsterb) + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SOUND_CONFIG(monsterb_samples_interface) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) + + MCFG_SOUND_ADD("music", TMS36XX, 247) + MCFG_SOUND_CONFIG(monsterb_tms3617_interface) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) + + MCFG_SOUND_ADD("dac", DAC, 0) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0) +MACHINE_CONFIG_END + + + +/************************************* + * + * Startup configuration + * + *************************************/ + +static SOUND_START( monsterb ) +{ + segag80r_state *state = machine.driver_data<segag80r_state>(); + state_save_register_global_array(machine, state->m_sound_state); + state_save_register_global(machine, state->m_sound_addr); + state_save_register_global(machine, state->m_n7751_command); + state_save_register_global(machine, state->m_n7751_busy); +} + + + +/************************************* + * + * TMS3617 access + * + *************************************/ + +static WRITE8_DEVICE_HANDLER( monsterb_sound_a_w ) +{ + device_t *tms = device->machine().device("music"); + int enable_val; + + /* Lower four data lines get decoded into 13 control lines */ + tms36xx_note_w(tms, 0, data & 15); + + /* Top four data lines address an 82S123 ROM that enables/disables voices */ + enable_val = device->machine().region("prom")->base()[(data & 0xF0) >> 4]; + tms3617_enable_w(tms, enable_val >> 2); +} + + + +/************************************* + * + * Discrete sound triggers + * + *************************************/ + +static WRITE8_DEVICE_HANDLER( monsterb_sound_b_w ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + device_t *samples = device->machine().device("samples"); + UINT8 diff = data ^ state->m_sound_state[1]; + state->m_sound_state[1] = data; + + /* SHOT: channel 0 */ + if ((diff & 0x01) && !(data & 0x01)) sample_start(samples, 0, 0, FALSE); + + /* DIVE: channel 1 */ + if ((diff & 0x02) && !(data & 0x02)) sample_start(samples, 1, 1, FALSE); + + /* TODO: D7 on Port B might affect TMS3617 output (mute?) */ +} + + + +/************************************* + * + * N7751 connections + * + *************************************/ + +static READ8_DEVICE_HANDLER( n7751_status_r ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + return state->m_n7751_busy << 4; +} + + +static WRITE8_DEVICE_HANDLER( n7751_command_w ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + /* + Z80 7751 control port + + D0-D2 = connected to 7751 port C + D3 = /INT line + */ + state->m_n7751_command = data & 0x07; + cputag_set_input_line(device->machine(), "audiocpu", 0, ((data & 0x08) == 0) ? ASSERT_LINE : CLEAR_LINE); + device->machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(100)); +} + + +static WRITE8_DEVICE_HANDLER( n7751_rom_control_w ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + /* P4 - address lines 0-3 */ + /* P5 - address lines 4-7 */ + /* P6 - address lines 8-11 */ + /* P7 - ROM selects */ + switch (offset) + { + case 0: + state->m_sound_addr = (state->m_sound_addr & ~0x00f) | ((data & 0x0f) << 0); + break; + + case 1: + state->m_sound_addr = (state->m_sound_addr & ~0x0f0) | ((data & 0x0f) << 4); + break; + + case 2: + state->m_sound_addr = (state->m_sound_addr & ~0xf00) | ((data & 0x0f) << 8); + break; + + case 3: + state->m_sound_addr &= 0xfff; + { + int numroms = device->machine().region("n7751")->bytes() / 0x1000; + if (!(data & 0x01) && numroms >= 1) state->m_sound_addr |= 0x0000; + if (!(data & 0x02) && numroms >= 2) state->m_sound_addr |= 0x1000; + if (!(data & 0x04) && numroms >= 3) state->m_sound_addr |= 0x2000; + if (!(data & 0x08) && numroms >= 4) state->m_sound_addr |= 0x3000; + } + break; + } +} + + +static READ8_HANDLER( n7751_rom_r ) +{ + segag80r_state *state = space->machine().driver_data<segag80r_state>(); + /* read from BUS */ + return space->machine().region("n7751")->base()[state->m_sound_addr]; +} + + +static READ8_HANDLER( n7751_command_r ) +{ + segag80r_state *state = space->machine().driver_data<segag80r_state>(); + /* read from P2 - 8255's PC0-2 connects to 7751's S0-2 (P24-P26 on an 8048) */ + /* bit 0x80 is an alternate way to control the sample on/off; doesn't appear to be used */ + return 0x80 | ((state->m_n7751_command & 0x07) << 4); +} + + +static WRITE8_DEVICE_HANDLER( n7751_p2_w ) +{ + segag80r_state *state = device->machine().driver_data<segag80r_state>(); + /* write to P2; low 4 bits go to 8243 */ + i8243_p2_w(device, offset, data & 0x0f); + + /* output of bit $80 indicates we are ready (1) or busy (0) */ + /* no other outputs are used */ + state->m_n7751_busy = data >> 7; +} + + +static READ8_HANDLER( n7751_t1_r ) +{ + /* T1 - labelled as "TEST", connected to ground */ + return 0; +} |