// license:BSD-3-Clause // copyright-holders:Aaron Giles /************************************************************************* Sega Z80-3D system *************************************************************************/ #include "emu.h" #include "includes/turbo.h" #include "sound/samples.h" #include "speaker.h" #define DISCRETE_TEST (0) /************************************* * * Turbo shared state updates * *************************************/ void turbo_state::turbo_update_samples() { /* accelerator sounds */ /* BSEL == 3 --> off */ /* BSEL == 2 --> standard */ /* BSEL == 1 --> tunnel */ /* BSEL == 0 --> ??? */ if (m_turbo_bsel == 3 && m_samples->playing(5)) m_samples->stop(5); else if (m_turbo_bsel != 3 && !m_samples->playing(5)) m_samples->start(5, 7, true); if (m_samples->playing(5)) m_samples->set_frequency(5, m_samples->base_frequency(5) * ((m_turbo_accel & 0x3f) / 5.25 + 1)); } #if (DISCRETE_TEST) TIMER_CALLBACK_MEMBER(turbo_state::update_sound_a) { discrete_device *discrete = machine.device("discrete"); int data = param; /* missing short crash sample, but I've never seen it triggered */ discrete->write(0, !(data & 0x01)); discrete->write(1, (data >> 1) & 1); discrete->write(2, (data >> 2) & 1); discrete->write(3, (data >> 3) & 1); discrete->write(4, (data >> 4) & 1); discrete->write(5, !(data & 0x20)); discrete->write(6, !(data & 0x40)); if (!((data >> 1) & 1)) osd_printf_debug("/TRIG1\n"); if (!((data >> 2) & 1)) osd_printf_debug("/TRIG2\n"); if (!((data >> 3) & 1)) osd_printf_debug("/TRIG3\n"); if (!((data >> 4) & 1)) osd_printf_debug("/TRIG4\n"); // osel = (osel & 6) | ((data >> 5) & 1); // turbo_update_samples(samples); } #endif /************************************* * * Turbo PPI write handlers * *************************************/ WRITE8_MEMBER(turbo_state::turbo_sound_a_w) { #if (!DISCRETE_TEST) #endif #if (!DISCRETE_TEST) uint8_t diff = data ^ m_sound_state[0]; #endif m_sound_state[0] = data; #if (!DISCRETE_TEST) /* /CRASH.S: channel 0 */ if ((diff & 0x01) && !(data & 0x01)) m_samples->start(0, 5); /* /TRIG1: channel 1 */ if ((diff & 0x02) && !(data & 0x02)) m_samples->start(1, 0); /* /TRIG2: channel 1 */ if ((diff & 0x04) && !(data & 0x04)) m_samples->start(1, 1); /* /TRIG3: channel 1 */ if ((diff & 0x08) && !(data & 0x08)) m_samples->start(1, 2); /* /TRIG4: channel 1 */ if ((diff & 0x10) && !(data & 0x10)) m_samples->start(1, 3); /* OSEL0 */ m_turbo_osel = (m_turbo_osel & 6) | ((data >> 5) & 1); /* /SLIP: channel 2 */ if ((diff & 0x40) && !(data & 0x40)) m_samples->start(2, 4); /* /CRASH.L: channel 3 */ if ((diff & 0x80) && !(data & 0x80)) m_samples->start(3, 5); /* update any samples */ turbo_update_samples(); #else if (((data ^ m_last_sound_a) & 0x1e) && (m_last_sound_a & 0x1e) != 0x1e) machine().scheduler().timer_set(attotime::from_hz(20000), FUNC(update_sound_a), data); else update_sound_a(data); m_last_sound_a = data; #endif } WRITE8_MEMBER(turbo_state::turbo_sound_b_w) { uint8_t diff = data ^ m_sound_state[1]; m_sound_state[1] = data; /* ACC0-ACC5 */ m_turbo_accel = data & 0x3f; output().set_value("tachometer", m_turbo_accel); /* /AMBU: channel 4 */ if ((diff & 0x40) && !(data & 0x40) && !m_samples->playing(4)) m_samples->start(4, 8, true); if ((diff & 0x40) && (data & 0x40)) m_samples->stop(4); /* /SPIN: channel 2 */ if ((diff & 0x80) && !(data & 0x80)) m_samples->start(2, 6); /* update any samples */ turbo_update_samples(); } WRITE8_MEMBER(turbo_state::turbo_sound_c_w) { /* OSEL1-2 */ m_turbo_osel = (m_turbo_osel & 1) | ((data & 3) << 1); /* BSEL0-1 */ m_turbo_bsel = (data >> 2) & 3; /* SPEED0-3 */ output().set_value("speed", (data >> 4) & 0x0f); /* update any samples */ turbo_update_samples(); } /************************************* * * Turbo sound hardware description * *************************************/ static const char *const turbo_sample_names[] = { "*turbo", "01", /* 0: Trig1 */ "02", /* 1: Trig2 */ "03", /* 2: Trig3 */ "04", /* 3: Trig4 */ "05", /* 4: Screech */ "06", /* 5: Crash */ "skidding", /* 6: Spin */ "idle", /* 7: Idle */ "ambulanc", /* 8: Ambulance */ nullptr }; void turbo_state::turbo_samples(machine_config &config) { /* this is the cockpit speaker configuration */ SPEAKER(config, "fspeaker", 0.0, 0.0, 1.0); // front SPEAKER(config, "bspeaker", 0.0, 0.0, -0.5); // back SPEAKER(config, "lspeaker", -0.2, 0.0, 1.0); // left SPEAKER(config, "rspeaker", 0.2, 0.0, 1.0); // right SAMPLES(config, m_samples); m_samples->set_channels(10); m_samples->set_samples_names(turbo_sample_names); /* channel 0 = CRASH.S -> CRASH.S/SM */ m_samples->add_route(0, "fspeaker", 0.25); /* channel 1 = TRIG1-4 -> ALARM.M/F/R/L */ m_samples->add_route(1, "fspeaker", 0.25); m_samples->add_route(1, "rspeaker", 0.25); m_samples->add_route(1, "lspeaker", 0.25); /* channel 2 = SLIP/SPIN -> SKID.F/R/L/M */ m_samples->add_route(2, "fspeaker", 0.25); m_samples->add_route(2, "rspeaker", 0.25); m_samples->add_route(2, "lspeaker", 0.25); /* channel 3 = CRASH.L -> CRASH.L/LM */ m_samples->add_route(3, "bspeaker", 0.25); /* channel 4 = AMBU -> AMBULANCE/AMBULANCE.M */ m_samples->add_route(4, "fspeaker", 0.25); /* channel 5 = ACCEL+BSEL -> MYCAR.F/W/M + MYCAR0.F/M + MYCAR1.F/M */ m_samples->add_route(5, "fspeaker", 0.25); m_samples->add_route(5, "bspeaker", 0.25); /* channel 6 = OSEL -> OCAR.F/FM */ m_samples->add_route(6, "fspeaker", 0.25); /* channel 7 = OSEL -> OCAR.L/LM */ m_samples->add_route(7, "lspeaker", 0.25); /* channel 8 = OSEL -> OCAR.R/RM */ m_samples->add_route(8, "rspeaker", 0.25); /* channel 9 = OSEL -> OCAR.W/WM */ m_samples->add_route(9, "bspeaker", 0.25); } /* Cockpit: CN2 1+2 -> FRONT CN2 3+4 -> REAR CN2 5+6 -> RIGHT CN2 7+8 -> LEFT Upright: CN2 1+2 -> UPPER CN2 3+4 -> LOWER F.OUT = CRASH.S + ALARM.F + SKID.F + OCAR.F + MYCAR.F + MYCAR0.F + MYCAR1.F + AMBULACE W.OUT = CRASH.L + OCAR.W + MYCAR.W + MYCAR0.W + MYCAR1.W + SLF R.OUT = ALARM.R + SKID.R + OCAR.R L.OUT = ALARM.L + SKID.L + OCAR.L M.OUT = CRASH.SM + CRASH.LM + SKID.M + ALARM.M + AMBULACE.M + MYCAR.M + MYCAR0.M + MYCAR1.M + OCAR.FM + OCAR.LM + OCAR.RM + OCAR.WM */ /************************************* * * Subroc 3D PPI write handlers * *************************************/ WRITE8_MEMBER(turbo_state::subroc3d_sound_a_w) { m_sound_state[0] = data; /* DIS0-3 contained in bits 0-3 */ /* DIR0-2 contained in bits 4-6 */ } inline void turbo_state::subroc3d_update_volume(int leftchan, uint8_t dis, uint8_t dir) { float volume = (float)(15 - dis) / 16.0f; float lvol, rvol; /* compute the left/right volume from the data */ if (dir != 7) { lvol = volume * (float)(6 - dir) / 6.0f; rvol = volume * (float)dir / 6.0f; } else lvol = rvol = 0; /* if the sample is playing, adjust it */ m_samples->set_volume(leftchan + 0, lvol); m_samples->set_volume(leftchan + 1, rvol); } WRITE8_MEMBER(turbo_state::subroc3d_sound_b_w) { uint8_t diff = data ^ m_sound_state[1]; m_sound_state[1] = data; /* bit 0 latches direction/volume for missile */ if ((diff & 0x01) && (data & 0x01)) { m_subroc3d_mdis = m_sound_state[0] & 0x0f; m_subroc3d_mdir = (m_sound_state[0] >> 4) & 0x07; if (!m_samples->playing(0)) { m_samples->start(0, 0, true); m_samples->start(1, 0, true); } subroc3d_update_volume(0, m_subroc3d_mdis, m_subroc3d_mdir); } /* bit 1 latches direction/volume for torpedo */ if ((diff & 0x02) && (data & 0x02)) { m_subroc3d_tdis = m_sound_state[0] & 0x0f; m_subroc3d_tdir = (m_sound_state[0] >> 4) & 0x07; if (!m_samples->playing(2)) { m_samples->start(2, 1, true); m_samples->start(3, 1, true); } subroc3d_update_volume(2, m_subroc3d_tdis, m_subroc3d_tdir); } /* bit 2 latches direction/volume for fighter */ if ((diff & 0x04) && (data & 0x04)) { m_subroc3d_fdis = m_sound_state[0] & 0x0f; m_subroc3d_fdir = (m_sound_state[0] >> 4) & 0x07; if (!m_samples->playing(4)) { m_samples->start(4, 2, true); m_samples->start(5, 2, true); } subroc3d_update_volume(4, m_subroc3d_fdis, m_subroc3d_fdir); } /* bit 3 latches direction/volume for hit */ if ((diff & 0x08) && (data & 0x08)) { m_subroc3d_hdis = m_sound_state[0] & 0x0f; m_subroc3d_hdir = (m_sound_state[0] >> 4) & 0x07; subroc3d_update_volume(6, m_subroc3d_hdis, m_subroc3d_hdir); } } WRITE8_MEMBER(turbo_state::subroc3d_sound_c_w) { uint8_t diff = data ^ m_sound_state[2]; m_sound_state[2] = data; /* /FIRE TRIG */ /* FIRE SELECT */ if ((diff & 0x01) && (data & 0x01)) m_samples->start(8, (data & 0x02) ? 6 : 5); /* /SHIP EXP TRIG -> MY SHIP EXP: channel 9 */ if ((diff & 0x04) && (data & 0x04)) m_samples->start(9, 7); /* /HIT TRIG -> HIT.L/R: channels 6+7 */ if ((diff & 0x08) && (data & 0x08)) { m_samples->start(6, (m_sound_state[0] & 0x80) ? 4 : 3); m_samples->start(7, (m_sound_state[0] & 0x80) ? 4 : 3); } /* /ALARM TRIG -> ALARM.M: channel 10 */ /* ALARM SELECT */ if ((diff & 0x10) && (data & 0x10)) m_samples->start(10, (data & 0x20) ? 10 : 9); /* /PROLOGUE */ if (!m_samples->playing(11)) m_samples->start(11, 8, true); m_samples->set_volume(11, (data & 0x40) ? 0 : 1.0); /* /GAME START */ machine().sound().system_mute(data & 0x80); } /************************************* * * Subroc 3D sound hardware description * *************************************/ static const char *const subroc3d_sample_names[] = { "*subroc3d", "01", /* 0: enemy missile */ "02", /* 1: enemy torpedo */ "03", /* 2: enemy fighter */ "04", /* 3: explosion in sky */ "05", /* 4: explosion on sea */ "06", /* 5: missile shoot */ "07", /* 6: torpedo shoot */ "08", /* 7: my ship expl */ "09", /* 8: prolog sound */ "11", /* 9: alarm 0 */ "12", /* 10: alarm 1 */ nullptr }; void turbo_state::subroc3d_samples(machine_config &config) { SPEAKER(config, "lspeaker").front_left(); SPEAKER(config, "rspeaker").front_right(); SAMPLES(config, m_samples); m_samples->set_channels(12); m_samples->set_samples_names(subroc3d_sample_names); /* MISSILE in channels 0 and 1 */ m_samples->add_route(0, "lspeaker", 0.25); m_samples->add_route(1, "rspeaker", 0.25); /* TORPEDO in channels 2 and 3 */ m_samples->add_route(2, "lspeaker", 0.25); m_samples->add_route(3, "rspeaker", 0.25); /* FIGHTER in channels 4 and 5 */ m_samples->add_route(4, "lspeaker", 0.25); m_samples->add_route(5, "rspeaker", 0.25); /* HIT in channels 6 and 7 */ m_samples->add_route(6, "lspeaker", 0.25); m_samples->add_route(7, "rspeaker", 0.25); /* FIRE sound in channel 8 */ m_samples->add_route(8, "lspeaker", 0.25); m_samples->add_route(8, "rspeaker", 0.25); /* SHIP EXP sound in channel 9 */ m_samples->add_route(9, "lspeaker", 0.25); m_samples->add_route(9, "rspeaker", 0.25); /* ALARM TRIG sound in channel 10 */ m_samples->add_route(10, "lspeaker", 0.25); m_samples->add_route(10, "rspeaker", 0.25); /* PROLOGUE sound in channel 11 */ m_samples->add_route(11, "lspeaker", 0.25); m_samples->add_route(11, "rspeaker", 0.25); } /************************************* * * Buck Rogers PPI write handlers * *************************************/ void turbo_state::buckrog_update_samples() { /* accelerator sounds */ if (m_samples->playing(5)) m_samples->set_frequency(5, m_samples->base_frequency(5) * (m_buckrog_myship / 100.25 + 1)); } WRITE8_MEMBER(turbo_state::buckrog_sound_a_w) { uint8_t diff = data ^ m_sound_state[0]; m_sound_state[0] = data; /* clock HIT DIS from bits 0-2 */ if ((diff & 0x10) && (data & 0x10)) m_samples->set_volume(3, (float)(/*7 - */(data & 7)) / 7.0f); /* clock ACC from bits 0-3 */ if ((diff & 0x20) && (data & 0x20)) { m_buckrog_myship = data & 0x0f; buckrog_update_samples(); } /* /ALARM0: channel 0 */ if ((diff & 0x40) && !(data & 0x40)) m_samples->start(0, 0); /* /ALARM1: channel 0 */ if ((diff & 0x80) && !(data & 0x80)) m_samples->start(0, 1); } WRITE8_MEMBER(turbo_state::buckrog_sound_b_w) { uint8_t diff = data ^ m_sound_state[1]; m_sound_state[1] = data; /* /ALARM3: channel 0 */ if ((diff & 0x01) && !(data & 0x01)) m_samples->start(0, 2); /* /ALARM4: channel 0 */ if ((diff & 0x02) && !(data & 0x02)) m_samples->start(0, 3); /* /FIRE: channel 1 */ if ((diff & 0x04) && !(data & 0x04)) m_samples->start(1, 5); /* /EXP: channel 2 */ if ((diff & 0x08) && !(data & 0x08)) m_samples->start(2, 4); /* /HIT: channel 3 */ if ((diff & 0x10) && !(data & 0x10)) { m_samples->start(3, 7); buckrog_update_samples(); } /* /REBOUND: channel 4 */ if ((diff & 0x20) && !(data & 0x20)) m_samples->start(4, 6); /* SHIP: channel 5 */ if ((diff & 0x40) && (data & 0x40) && !m_samples->playing(5)) { m_samples->start(5, 8, true); buckrog_update_samples(); } if ((diff & 0x40) && !(data & 0x40) && m_samples->playing(5)) m_samples->stop(5); /* GAME ON */ machine().sound().system_enable(data & 0x80); } /************************************* * * Sound interfaces * *************************************/ static const char *const buckrog_sample_names[]= { "*buckrog", "alarm0", /* 0 */ "alarm1", /* 1 */ "alarm2", /* 2 */ "alarm3", /* 3 */ "exp", /* 4 */ "fire", /* 5 */ "rebound", /* 6 */ "hit", /* 7 */ "shipsnd1", /* 8 */ "shipsnd2", /* 9 */ "shipsnd3", /* 10 */ nullptr }; void turbo_state::buckrog_samples(machine_config &config) { SPEAKER(config, "mono").front_center(); SAMPLES(config, m_samples); m_samples->set_channels(6); m_samples->set_samples_names(buckrog_sample_names); m_samples->add_route(ALL_OUTPUTS, "mono", 0.25); } /************************************* * * Discrete test code * *************************************/ #if (DISCRETE_TEST) /* Nodes - Inputs */ #define TURBO_CRASH_EN NODE_01 #define TURBO_TRIG1_INV NODE_02 #define TURBO_TRIG2_INV NODE_03 #define TURBO_TRIG3_INV NODE_04 #define TURBO_TRIG4_INV NODE_05 #define TURBO_SLIP_EN NODE_06 #define TURBO_CRASHL_EN NODE_07 #define TURBO_ACC_VAL NODE_08 #define TURBO_AMBU_EN NODE_09 #define TURBO_SPIN_EN NODE_10 #define TURBO_OSEL_VAL NODE_11 #define TURBO_BSEL_VAL NODE_12 /* Nodes - Sounds */ #define FIRETRUCK_NOISE NODE_20 static const discrete_555_desc turbo_alarm_555 = { DISC_555_OUT_SQW | DISC_555_OUT_DC, 5, // B+ voltage of 555 DEFAULT_555_VALUES, }; DISCRETE_SOUND_START(turbo_discrete) /************************************************/ /* Input register mapping for turbo */ /************************************************/ /* NODE ADDR MASK GAIN OFFSET INIT */ DISCRETE_INPUT(TURBO_CRASH_EN ,0x00,0x001f, 0.0) DISCRETE_INPUT(TURBO_TRIG1_INV ,0x01,0x001f, 1.0) DISCRETE_INPUT(TURBO_TRIG2_INV ,0x02,0x001f, 1.0) DISCRETE_INPUT(TURBO_TRIG3_INV ,0x03,0x001f, 1.0) DISCRETE_INPUT(TURBO_TRIG4_INV ,0x04,0x001f, 1.0) DISCRETE_INPUT(TURBO_SLIP_EN ,0x05,0x001f, 0.0) DISCRETE_INPUT(TURBO_CRASHL_EN ,0x06,0x001f, 0.0) DISCRETE_INPUT(TURBO_ACC_VAL ,0x07,0x001f, 0.0) DISCRETE_INPUT(TURBO_AMBU_EN ,0x08,0x001f, 0.0) DISCRETE_INPUT(TURBO_SPIN_EN ,0x09,0x001f, 0.0) DISCRETE_INPUT(TURBO_OSEL_VAL ,0x0a,0x001f, 0.0) DISCRETE_INPUT(TURBO_BSEL_VAL ,0x0b,0x001f, 0.0) /************************************************/ /* Alarm sounds */ /************************************************/ // 5-5-5 counter provides the input clock DISCRETE_555_ASTABLE(NODE_50,1,470,120,0.1e-6,&turbo_alarm_555) // which clocks a 74393 dual 4-bit counter, clocked on the falling edge DISCRETE_COUNTER(NODE_51,1,0,NODE_50,0,15,1,0,DISC_CLK_ON_F_EDGE) // the high bit of this counter DISCRETE_TRANSFORM2(NODE_52,NODE_51,8,"01/") // clocks the other half of the 74393 DISCRETE_COUNTER(NODE_53,1,0,NODE_52,0,15,1,0,DISC_CLK_ON_F_EDGE) // trig1 triggers a LS123 retriggerable multivibrator DISCRETE_ONESHOT(NODE_60,TURBO_TRIG1_INV,5.0,(0.33e-9)*47*1e6, DISC_ONESHOT_FEDGE|DISC_ONESHOT_RETRIG|DISC_OUT_ACTIVE_HIGH) // which interacts with bit 0 of the second counter DISCRETE_TRANSFORM2(NODE_61,NODE_53,1,"01&") // via a NAND DISCRETE_LOGIC_NAND(NODE_62,1,NODE_60,NODE_61) // trig2 triggers a LS123 retriggerable multivibrator DISCRETE_ONESHOT(NODE_65,TURBO_TRIG2_INV,5.0,(0.33e-9)*47*10e6,DISC_ONESHOT_FEDGE|DISC_ONESHOT_RETRIG|DISC_OUT_ACTIVE_HIGH) // which interacts with bit 3 of the first counter via a NAND DISCRETE_LOGIC_NAND(NODE_66,1,NODE_65,NODE_52) // trig3 triggers a LS123 retriggerable multivibrator DISCRETE_ONESHOT(NODE_70,TURBO_TRIG3_INV,5.0,(0.33e-9)*47*33e6,DISC_ONESHOT_FEDGE|DISC_ONESHOT_RETRIG|DISC_OUT_ACTIVE_HIGH) // which interacts with bit 2 of the first counter DISCRETE_TRANSFORM3(NODE_71,NODE_51,4,1,"01/2&") // via a NAND DISCRETE_LOGIC_NAND(NODE_72,1,NODE_70,NODE_71) // trig4 triggers a LS123 retriggerable multivibrator DISCRETE_ONESHOT(NODE_75,TURBO_TRIG4_INV,5.0,(0.33e-9)*47*10e6,DISC_ONESHOT_FEDGE|DISC_ONESHOT_RETRIG|DISC_OUT_ACTIVE_HIGH) // which interacts with bit 1 of the first counter DISCRETE_TRANSFORM3(NODE_76,NODE_51,2,1,"01/2&") // via a NAND DISCRETE_LOGIC_NAND(NODE_77,1,NODE_75,NODE_76) // everything is effectively NANDed together DISCRETE_LOGIC_NAND4(NODE_80,1,NODE_62,NODE_66,NODE_72,NODE_77) /* the rest of the circuit looks like this: +5V +12V +---+ ^ ^ +--------+ 1K v | | | | |\ | +---NNN--NNN--+ Z 1K 10K Z | | \ | | |\ 20K | +--|(----> ALARM_M Z Z +-|- \ | | | \ | | 4.7u | | | >--+---NNNN----+-|- \ | +--|(----> ALARM_F +--NNNN--|(----+-----|+ / 22K | >-------+---+ 4.7u +-\ | 5.1K 4.7u | | / +6V--|+ / +--|(----> ALARM_R | >o---(NODE_62)--+ Z |/ | / | 4.7u +-/ | 10K Z |/ +--|(----> ALARM_L | | 4.7u +-\ | v | >o---(NODE_66)--+ GND +-/ | | +-\ | | >o---(NODE_72)--+ +-/ | | +-\ | | >o---(NODE_77)--+ +-/ */ /************************************************/ /* Combine all 7 sound sources. */ /************************************************/ DISCRETE_OUTPUT(NODE_80, 16000) DISCRETE_SOUND_END #endif