diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index bcbc9749c4b..c28d9bf0d71 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -1605,3 +1605,15 @@ if (SOUNDS["RP2C33_SOUND"]~=null) then MAME_DIR .. "src/devices/sound/rp2c33_snd.h", } end + +--------------------------------------------------- +-- +--@src/devices/sound/tt5665.h,SOUNDS["TT5665"] = true +--------------------------------------------------- + +if (SOUNDS["TT5665"]~=null) then + files { + MAME_DIR .. "src/devices/sound/tt5665.cpp", + MAME_DIR .. "src/devices/sound/tt5665.h", + } +end diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 80466061f4a..9c1fca398cd 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -284,6 +284,7 @@ SOUNDS["LC7535"] = true --SOUNDS["UPD934G"] = true SOUNDS["S_DSP"] = true SOUNDS["KS0164"] = true +SOUNDS["TT5665"] = true --SOUNDS["RP2C33_SOUND"] = true -------------------------------------------------- diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 1401795fd94..2951a968659 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -308,6 +308,7 @@ SOUNDS["SWP20"] = true SOUNDS["SWP30"] = true SOUNDS["S_DSP"] = true SOUNDS["ROLANDPCM"] = true +--SOUNDS["TT5665"] = true SOUNDS["RP2C33_SOUND"] = true -------------------------------------------------- diff --git a/src/devices/sound/tt5665.cpp b/src/devices/sound/tt5665.cpp new file mode 100644 index 00000000000..fad98371774 --- /dev/null +++ b/src/devices/sound/tt5665.cpp @@ -0,0 +1,436 @@ +// license:BSD-3-Clause +// copyright-holders:Mirko Buffoni,Aaron Giles,cam900 +/*************************************************************************** + + tt5665.cpp + + Tontek TT5665 ADCPM sound chip. + +**************************************************************************** + + Emulation core by cam900 + Base code by OKI MSM6295 ADPCM emulation(okim6295.cpp), + by Mirko Buffoni,Aaron Giles + +**************************************************************************** + + Tontek TT5665 ADPCM chip: + + Command bytes are sent: + + 1xxx xxxx = start of 2-byte command sequence, xxxxxxx is the sample + number to trigger + xxab vvvv = second half of command; the ab bits are voice number, + the x bits are high bits of sample number, the v bits + are volume + + 0abc d000 = stop playing; one or more of the abcd bits is set to + indicate which voice(s) + + Status is read: + + ???? abcd = one bit per voice, set to 0 if nothing is playing, or + 1 if it is active + + ADPCM data and volume is compatible with OKI MSM6295, but some Command + byte is slightly different. + + Compares to other Tontek OKI MSM6295 derivatives: + |--------------------|-----------------|-----------------|-----------------| + | Part number | TT5665 | TT6297 | TT6298 | + |--------------------|-----------------|-----------------|-----------------| + | Address Bus width | 24bit (128MBit) | 24bit (128MBit) | 23bit (64MBit) | + |--------------------|-----------------|-----------------|-----------------| + | Output channels | 2 (DAOL/R) | 1 (DAO) | 1 (DAO) | + |--------------------|-----------------|-----------------|-----------------| + | Variable Frequency | SS, S0-1(For | Not exists | Not exists | + | Pins | DAOL only) | | | + |--------------------|-----------------|-----------------|-----------------| + | Crystal Oscillator | 2 (XT, XTB) | 1 (XT) | 2 (XT, XTB) | + | Pins | | | | + |--------------------|-----------------|-----------------|-----------------| + | Frequency Divider | DAOR: 136, 170 | 136 | 136 | + | | DAOL: DAOR * n | | | + | | (n : 1,2,4,8) | | | + |--------------------|-----------------|-----------------|-----------------| + These chips share Command bytes and ADPCM format. + + Frequency Table (Unit: KHz) : + |---------------------------|---------------------------| + | SS = "1" | SS = "0" | + |-----------------|---------------------------|---------------------------| + | (S1,S0) | 00 | 01 | 10 | 11 | 00 | 01 | 10 | 11 | + |----------|------|------|------|------|------|------|------|------|------| + | OSC = | DAOR | 8.0 | 8.0 | 8.0 | 8.0 | 6.4 | 6.4 | 6.4 | 6.4 | + | 1.088MHz |------|------|------|------|------|------|------|------|------| + | | DAOL | 8.0 | 4.0 | 2.0 | 1.0 | 6.4 | 3.2 | 1.6 | 0.8 | + |----------|------|------|------|------|------|------|------|------|------| + | OSC = | DAOR | 16.0 | 16.0 | 16.0 | 16.0 | 12.8 | 12.8 | 12.8 | 12.8 | + | 2.176MHz |------|------|------|------|------|------|------|------|------| + | | DAOL | 16.0 | 8.0 | 4.0 | 2.0 | 12.8 | 6.4 | 3.2 | 1.6 | + |----------|------|------|------|------|------|------|------|------|------| + | OSC = | DAOR | 32.0 | 32.0 | 32.0 | 32.0 | 25.6 | 25.6 | 25.6 | 25.6 | + | 4.352MHz |------|------|------|------|------|------|------|------|------| + | | DAOL | 32.0 | 16.0 | 8.0 | 4.0 | 25.6 | 12.8 | 6.4 | 3.2 | + |----------|------|------|------|------|------|------|------|------|------| + +***************************************************************************/ + +#include "emu.h" +#include "tt5665.h" + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(TT5665, tt5665_device, "tt5665", "Tontek TT5665 ADPCM Voice Synthesis LSI") + +// same as MSM6295 +const stream_buffer::sample_t tt5665_device::s_volume_table[16] = +{ + stream_buffer::sample_t(0x20) / stream_buffer::sample_t(0x20), // 0 dB + stream_buffer::sample_t(0x16) / stream_buffer::sample_t(0x20), // -3.2 dB + stream_buffer::sample_t(0x10) / stream_buffer::sample_t(0x20), // -6.0 dB + stream_buffer::sample_t(0x0b) / stream_buffer::sample_t(0x20), // -9.2 dB + stream_buffer::sample_t(0x08) / stream_buffer::sample_t(0x20), // -12.0 dB + stream_buffer::sample_t(0x06) / stream_buffer::sample_t(0x20), // -14.5 dB + stream_buffer::sample_t(0x04) / stream_buffer::sample_t(0x20), // -18.0 dB + stream_buffer::sample_t(0x03) / stream_buffer::sample_t(0x20), // -20.5 dB + stream_buffer::sample_t(0x02) / stream_buffer::sample_t(0x20), // -24.0 dB + stream_buffer::sample_t(0x00) / stream_buffer::sample_t(0x20), + stream_buffer::sample_t(0x00) / stream_buffer::sample_t(0x20), + stream_buffer::sample_t(0x00) / stream_buffer::sample_t(0x20), + stream_buffer::sample_t(0x00) / stream_buffer::sample_t(0x20), + stream_buffer::sample_t(0x00) / stream_buffer::sample_t(0x20), + stream_buffer::sample_t(0x00) / stream_buffer::sample_t(0x20), + stream_buffer::sample_t(0x00) / stream_buffer::sample_t(0x20), +}; + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// tt5665_device - constructor +//------------------------------------------------- + +tt5665_device::tt5665_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, TT5665, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , device_rom_interface(mconfig, *this) + , m_ss_state(~u8(0)) + , m_s0_s1_state(0) + , m_command(-1) + , m_stream(nullptr) + , m_daol_output(0.0) + , m_daol_timing(0) +{ +} + + +//------------------------------------------------- +// device_validity_check - device-specific checks +//------------------------------------------------- + +void tt5665_device::device_validity_check(validity_checker &valid) const +{ + if ((SS_LOW != m_ss_state) && (SS_HIGH != m_ss_state)) + osd_printf_error("Initial pin SS state not configured\n"); +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void tt5665_device::device_start() +{ + if ((SS_LOW != m_ss_state) && (SS_HIGH != m_ss_state)) + m_ss_state = 0; + + // create the stream + m_stream = stream_alloc(0, 2, clock() / freq_divider()); + + save_item(NAME(m_command)); + save_item(NAME(m_ss_state)); + save_item(NAME(m_s0_s1_state)); + save_item(NAME(m_daol_output)); + save_item(NAME(m_daol_timing)); + + save_item(STRUCT_MEMBER(m_voice, m_playing)); + save_item(STRUCT_MEMBER(m_voice, m_sample)); + save_item(STRUCT_MEMBER(m_voice, m_count)); + save_item(STRUCT_MEMBER(m_voice, m_volume)); + save_item(STRUCT_MEMBER(m_voice, m_base_offset)); + for (int voicenum = 0; voicenum < TT5665_VOICES * 2; voicenum++) + { + save_item(NAME(m_voice[voicenum].m_adpcm.m_signal), voicenum); + save_item(NAME(m_voice[voicenum].m_adpcm.m_step), voicenum); + } +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void tt5665_device::device_reset() +{ + m_stream->update(); + for (auto & elem : m_voice) + elem.m_playing = false; +} + + +//------------------------------------------------- +// device_post_load - device-specific post-load +//------------------------------------------------- + +void tt5665_device::device_post_load() +{ + device_clock_changed(); +} + + +//------------------------------------------------- +// device_clock_changed - called if the clock +// changes +//------------------------------------------------- + +void tt5665_device::device_clock_changed() +{ + m_stream->set_sample_rate(clock() / freq_divider()); +} + + +//------------------------------------------------- +// stream_generate - handle update requests for +// our sound stream +//------------------------------------------------- + +void tt5665_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) +{ + // reset the output stream + outputs[0].fill(0); + outputs[1].fill(0); + bool update_daol = false; + + // iterate over voices and accumulate sample data + // loop while we still have samples to generate + for (int s = 0; s < outputs[0].samples(); s++) + { + // adjust DAOL clock timing + m_daol_timing--; + if (m_daol_timing <= 0) + { + update_daol = true; + m_daol_output = 0.0; + } + stream_buffer::sample_t daor_output = 0.0; + + for (int b = 0; b < TT5665_VOICES; b++) + { + // refresh DAOL output + if (update_daol) + m_voice[b].generate_adpcm(*this, &m_daol_output); + + // refresh DAOR output + m_voice[b + 4].generate_adpcm(*this, &daor_output); + } + outputs[0].put(s, m_daol_output); + outputs[1].put(s, daor_output); + if (update_daol) + { + update_daol = false; + m_daol_timing = 1 << m_s0_s1_state; + } + } +} + + +//------------------------------------------------- +// rom_bank_updated - the rom bank has changed +//------------------------------------------------- + +void tt5665_device::rom_bank_updated() +{ + m_stream->update(); +} + + +//------------------------------------------------- +// set_ss - change the state of pin SS, which +// alters the frequency we output +//------------------------------------------------- + +void tt5665_device::set_ss(bool ss) +{ + assert(started()); + m_ss_state = ss ? 1 : 0; + device_clock_changed(); +} + + +//------------------------------------------------- +// set_s0_s1 - change the state of S0 & S1, which +// alters the frequency we output +//------------------------------------------------- + +void tt5665_device::set_s0_s1(int s0_s1) +{ + m_stream->update(); + assert(started()); + m_s0_s1_state = s0_s1 & 3; +} + + +//------------------------------------------------- +// read - read the status register +//------------------------------------------------- + +u8 tt5665_device::read() +{ + u8 result = 0xf0; // naname expects bits 4-7 to be 1 + + // set the bit to 1 if something is playing on a given channel + m_stream->update(); + for (int voicenum = 0; voicenum < TT5665_VOICES; voicenum++) + if (m_voice[voicenum].m_playing || m_voice[voicenum + 4].m_playing) // TODO: all or DAOL/DAOR only? + result |= 1 << voicenum; + + return result; +} + + +//------------------------------------------------- +// write - write to the command register +//------------------------------------------------- + +void tt5665_device::write(u8 command) +{ + // if a command is pending, process the second half + if (m_command != -1) + { + // the chip can only play one voice at a time, but has extra phrase spaces. that's are main difference from MSM 6295. + int voicemask = (command >> 4) & 3; + int basemask = (command >> 6) & 3; + + // update the stream + m_stream->update(); + + // determine which voice + for (int b = 0; b < 2; b++) + { + tt5665_voice &voice = m_voice[voicemask + (b * 4)]; + + const int sample_ind = (basemask << 7) | m_command; + if (!voice.m_playing) // TODO: Same as MSM6295? + { + // determine the start/stop positions + offs_t base = sample_ind * 8; + + offs_t start = read_byte(base + 0) << 16; + start |= read_byte(base + 1) << 8; + start |= read_byte(base + 2) << 0; + start &= 0xffffff; + + offs_t stop = read_byte(base + 3) << 16; + stop |= read_byte(base + 4) << 8; + stop |= read_byte(base + 5) << 0; + stop &= 0xffffff; + + if (start < stop) + { + // set up the voice to play this sample + voice.m_playing = true; + voice.m_base_offset = start; + voice.m_sample = 0; + voice.m_count = 2 * (stop - start + 1); + + // also reset the ADPCM parameters + voice.m_adpcm.reset(); + voice.m_volume = s_volume_table[command & 0x0f]; + } + + // invalid samples go here + else + { + logerror("Requested to play invalid sample %04x\n", sample_ind); + } + } + else + { + logerror("Requested to play sample %04x on non-stopped voice\n", sample_ind); + } + + // reset the command + m_command = -1; + } + } + + // if this is the start of a command, remember the sample number for next time + else if (command & 0x80) + m_command = command & 0x7f; + + // otherwise, see if this is a silence command + else + { + // update the stream, then turn it off + m_stream->update(); + + // determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command + int voicemask = command >> 3; + for (int voicenum = 0; voicenum < TT5665_VOICES; voicenum++, voicemask >>= 1) + if (voicemask & 1) + for (int b = 0; b < 2; b++) + m_voice[voicenum + (b * 4)].m_playing = false; + } +} + + + +//************************************************************************** +// TT5665 VOICE +//************************************************************************** + +//------------------------------------------------- +// tt5665_voice - constructor +//------------------------------------------------- + +tt5665_device::tt5665_voice::tt5665_voice() + : m_playing(false) + , m_base_offset(0) + , m_sample(0) + , m_count(0) + , m_volume(0) +{ +} + + +//------------------------------------------------- +// generate_adpcm - generate ADPCM samples and +// add them to an output stream +//------------------------------------------------- + +void tt5665_device::tt5665_voice::generate_adpcm(device_rom_interface &rom, stream_buffer::sample_t *buffer) +{ + // skip if not active + if (!m_playing) + return; + + // fetch the next sample byte + constexpr stream_buffer::sample_t sample_scale = 1.0 / 2048.0; + int nibble = rom.read_byte(m_base_offset + m_sample / 2) >> (((m_sample & 1) << 2) ^ 4); + + // output to the buffer, scaling by the volume + // signal in range -2048..2047, volume in range 2..32 => signal * volume / 2 in range -32768..32767 + *buffer += m_adpcm.clock(nibble) * sample_scale * m_volume; + + // next! + if (++m_sample >= m_count) + { + m_playing = false; + } +} diff --git a/src/devices/sound/tt5665.h b/src/devices/sound/tt5665.h new file mode 100644 index 00000000000..e896872ecb3 --- /dev/null +++ b/src/devices/sound/tt5665.h @@ -0,0 +1,110 @@ +// license:BSD-3-Clause +// copyright-holders:Mirko Buffoni,Aaron Giles,cam900 +/*************************************************************************** + + tt5665.h + + Tontek TT5665 ADCPM sound chip. + +***************************************************************************/ + +#ifndef MAME_SOUND_TT5665_H +#define MAME_SOUND_TT5665_H + +#pragma once + +#include "sound/okiadpcm.h" +#include "dirom.h" + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + + +// ======================> tt5665_device + +class tt5665_device : public device_t, + public device_sound_interface, + public device_rom_interface<24> +{ +public: + enum ss_state + { + SS_LOW = 0, + SS_HIGH = 1 + }; + + // construction/destruction + tt5665_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, ss_state ss, u8 s0_s1) + : tt5665_device(mconfig, tag, owner, clock) + { + config_ss(ss); + config_s0_s1(s0_s1); + } + tt5665_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + + // inline configuration helpers + void config_ss(ss_state ss) { assert(!started()); m_ss_state = ss; } + void config_s0_s1(u8 s0_s1) { assert(!started()); m_s0_s1_state = s0_s1 & 3; } + + // runtime configuration + void set_ss(bool ss); + void set_s0_s1(int s0_s1); + + u8 read(); + void write(u8 command); + +protected: + // device-level overrides + virtual void device_validity_check(validity_checker &valid) const override; + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_post_load() override; + virtual void device_clock_changed() override; + + // device_sound_interface overrides + virtual void sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) override; + + // device_rom_interface overrides + virtual void rom_bank_updated() override; + +private: + // a single voice + class tt5665_voice + { + public: + tt5665_voice(); + void generate_adpcm(device_rom_interface &rom, stream_buffer::sample_t *buffer); + + oki_adpcm_state m_adpcm; // current ADPCM state + bool m_playing; + offs_t m_base_offset; // pointer to the base memory location + u32 m_sample; // current sample number + u32 m_count; // total samples to play + stream_buffer::sample_t m_volume; // output volume + }; + + // configuration state + u8 m_ss_state; + u8 m_s0_s1_state; + + // internal state + static constexpr int TT5665_VOICES = 4; + + tt5665_voice m_voice[TT5665_VOICES * 2]; // separated voice for left and right output + s32 m_command; + sound_stream* m_stream; + stream_buffer::sample_t m_daol_output; + int m_daol_timing; + + inline int freq_divider() const { return m_ss_state ? 136 : 170; } + + static const stream_buffer::sample_t s_volume_table[16]; +}; + + +// device type definition +DECLARE_DEVICE_TYPE(TT5665, tt5665_device) + + +#endif // MAME_SOUND_TT5665_H diff --git a/src/mame/drivers/bowltry.cpp b/src/mame/drivers/bowltry.cpp index 87c6e9c314a..04ad485b879 100644 --- a/src/mame/drivers/bowltry.cpp +++ b/src/mame/drivers/bowltry.cpp @@ -2,7 +2,7 @@ // copyright-holders:David Haywood /************************************************************************************************************ - Bowling Try + Bowling Try! (c)200? Atlus @@ -30,7 +30,9 @@ #include "emu.h" #include "cpu/h8/h83008.h" +#include "sound/tt5665.h" #include "screen.h" +#include "speaker.h" #define HACK_ENABLED 0 @@ -82,11 +84,13 @@ void bowltry_state::bowltry_map(address_map &map) map.unmap_value_high(); map(0x000000, 0x07ffff).rom().region("maincpu", 0); map(0x080000, 0x083fff).ram(); + //map(0x200000, 0x200003).noprw(); + //map(0x240000, 0x240001).noprw(); + //map(0x400000, 0x400005).noprw(); map(0x600000, 0x60ffff).ram(); #if HACK_ENABLED map(0x60e090, 0x60e093).rw(FUNC(bowltry_state::hack_r), FUNC(bowltry_state::hack_w)); #endif - } static INPUT_PORTS_START( bowltry ) @@ -116,6 +120,9 @@ void bowltry_state::bowltry(machine_config &config) //PALETTE(config, "palette").set_entries(65536); /* tt5665 sound */ + SPEAKER(config, "speaker").front_center(); + + TT5665(config, "tt5665", 16000000/4, tt5665_device::ss_state::SS_HIGH, 0).add_route(1, "speaker", 1.0); // guessed, needs to verification } ROM_START( bowltry ) @@ -131,4 +138,4 @@ ROM_START( bowltry ) ROM_END -GAME( 200?, bowltry, 0, bowltry, bowltry, bowltry_state, empty_init, ROT0, "Atlus", "Bowling Try", MACHINE_IS_SKELETON ) +GAME( 200?, bowltry, 0, bowltry, bowltry, bowltry_state, empty_init, ROT0, "Atlus", "Bowling Try!", MACHINE_IS_SKELETON )