diff options
Diffstat (limited to 'src/devices/sound')
-rw-r--r-- | src/devices/sound/c6280.cpp | 104 | ||||
-rw-r--r-- | src/devices/sound/c6280.h | 9 | ||||
-rw-r--r-- | src/devices/sound/es8712.cpp | 2 | ||||
-rw-r--r-- | src/devices/sound/meg.cpp | 384 | ||||
-rw-r--r-- | src/devices/sound/meg.h | 122 | ||||
-rw-r--r-- | src/devices/sound/megd.cpp | 118 | ||||
-rw-r--r-- | src/devices/sound/megd.h | 39 | ||||
-rw-r--r-- | src/devices/sound/namco_163.cpp | 176 | ||||
-rw-r--r-- | src/devices/sound/namco_163.h | 42 | ||||
-rw-r--r-- | src/devices/sound/swp30.cpp | 61 | ||||
-rw-r--r-- | src/devices/sound/swp30.h | 9 | ||||
-rw-r--r-- | src/devices/sound/vrc6.cpp | 99 | ||||
-rw-r--r-- | src/devices/sound/vrc6.h | 14 | ||||
-rw-r--r-- | src/devices/sound/ym2413.cpp | 124 | ||||
-rw-r--r-- | src/devices/sound/ym2413.h | 23 |
15 files changed, 1136 insertions, 190 deletions
diff --git a/src/devices/sound/c6280.cpp b/src/devices/sound/c6280.cpp index 0e5873c86de..6c6f2410166 100644 --- a/src/devices/sound/c6280.cpp +++ b/src/devices/sound/c6280.cpp @@ -92,16 +92,16 @@ void c6280_device::sound_stream_update(sound_stream &stream, stream_sample_t **i if((ch >= 4) && (m_channel[ch].m_noise_control & 0x80)) { /* Noise mode */ - uint32_t step = m_noise_freq_tab[(m_channel[ch].m_noise_control & 0x1F) ^ 0x1F]; + uint32_t step = (m_channel[ch].m_noise_control & 0x1F) ^ 0x1F; for (int i = 0; i < samples; i += 1) { static int data = 0; - m_channel[ch].m_noise_counter += step; - if(m_channel[ch].m_noise_counter >= 0x800) + if(m_channel[ch].m_noise_counter <= 0) { + m_channel[ch].m_noise_counter = step << 2; data = (machine().rand() & 1) ? 0x1F : 0; } - m_channel[ch].m_noise_counter &= 0x7FF; + m_channel[ch].m_noise_counter--; outputs[0][i] += (int16_t)(vll * (data - 16)); outputs[1][i] += (int16_t)(vlr * (data - 16)); } @@ -118,28 +118,40 @@ void c6280_device::sound_stream_update(sound_stream &stream, stream_sample_t **i } else { - if ((m_lfo_control & 0x80) && (ch < 2)) + if ((m_lfo_control & 3) && (ch < 2)) { if (ch == 0) // CH 0 only, CH 1 is muted { /* Waveform mode with LFO */ - uint32_t step = m_channel[0].m_frequency; - uint16_t lfo_step = m_channel[1].m_frequency; + uint16_t lfo_step = m_channel[1].m_frequency ? m_channel[1].m_frequency : 0x1000; for (int i = 0; i < samples; i += 1) { - int offset, lfooffset; - int16_t data, lfo_data; - lfooffset = (m_channel[1].m_counter >> 12) & 0x1F; - m_channel[1].m_counter += m_wave_freq_tab[(lfo_step * m_lfo_frequency) & 0xfff]; // TODO : multiply? verify this from real hardware. - m_channel[1].m_counter &= 0x1FFFF; - lfo_data = m_channel[1].m_waveform[lfooffset]; - if (m_lfo_control & 3) + int32_t step = m_channel[0].m_frequency ? m_channel[0].m_frequency : 0x1000; + if (m_lfo_control & 0x80) // reset LFO + { + m_channel[1].m_tick = lfo_step * m_lfo_frequency; + m_channel[1].m_counter = 0; + } + else + { + int lfooffset = m_channel[1].m_counter; + m_channel[1].m_tick--; + if (m_channel[1].m_tick <= 0) + { + m_channel[1].m_tick = lfo_step * m_lfo_frequency; // TODO : multiply? verify this from real hardware. + m_channel[1].m_counter = (m_channel[1].m_counter + 1) & 0x1f; + } + int16_t lfo_data = m_channel[1].m_waveform[lfooffset]; step += ((lfo_data - 16) << (((m_lfo_control & 3)-1)<<1)); // verified from patent, TODO : same in real hardware? - - offset = (m_channel[0].m_counter >> 12) & 0x1F; - m_channel[0].m_counter += m_wave_freq_tab[step & 0xfff]; - m_channel[0].m_counter &= 0x1FFFF; - data = m_channel[0].m_waveform[offset]; + } + int offset = m_channel[0].m_counter; + m_channel[0].m_tick--; + if (m_channel[0].m_tick <= 0) + { + m_channel[0].m_tick = step; + m_channel[0].m_counter = (m_channel[0].m_counter + 1) & 0x1f; + } + int16_t data = m_channel[0].m_waveform[offset]; outputs[0][i] += (int16_t)(vll * (data - 16)); outputs[1][i] += (int16_t)(vlr * (data - 16)); } @@ -148,15 +160,17 @@ void c6280_device::sound_stream_update(sound_stream &stream, stream_sample_t **i else { /* Waveform mode */ - uint32_t step = m_wave_freq_tab[m_channel[ch].m_frequency]; + uint32_t step = m_channel[ch].m_frequency ? m_channel[ch].m_frequency : 0x1000; for (int i = 0; i < samples; i += 1) { - int offset; - int16_t data; - offset = (m_channel[ch].m_counter >> 12) & 0x1F; - m_channel[ch].m_counter += step; - m_channel[ch].m_counter &= 0x1FFFF; - data = m_channel[ch].m_waveform[offset]; + int offset = m_channel[ch].m_counter; + m_channel[ch].m_tick--; + if (m_channel[ch].m_tick <= 0) + { + m_channel[ch].m_tick = step; + m_channel[ch].m_counter = (m_channel[ch].m_counter + 1) & 0x1f; + } + int16_t data = m_channel[ch].m_waveform[offset]; outputs[0][i] += (int16_t)(vll * (data - 16)); outputs[1][i] += (int16_t)(vlr * (data - 16)); } @@ -205,6 +219,10 @@ WRITE8_MEMBER( c6280_device::c6280_w ) { chan->m_index = 0; } + if(((chan->m_control & 0x80) == 0) && (data & 0x80)) + { + chan->m_tick = chan->m_frequency; + } chan->m_control = data; break; @@ -261,38 +279,9 @@ c6280_device::c6280_device(const machine_config &mconfig, const char *tag, devic { } -//------------------------------------------------- -// calculate_clocks - (re)calculate clock-derived -// members -//------------------------------------------------- - -void c6280_device::calculate_clocks() -{ - int rate = clock() / 16; - - /* Make waveform frequency table */ - for (int i = 0; i < 4096; i += 1) - { - double step = (16 * 4096) / (i + 1); - m_wave_freq_tab[(1 + i) & 0xFFF] = (uint32_t)step; - } - - /* Make noise frequency table */ - for (int i = 0; i < 32; i += 1) - { - double step = (16 * 32) / (i+1); - m_noise_freq_tab[i] = (uint32_t)step; - } - - if (m_stream != nullptr) - m_stream->set_sample_rate(rate); - else - m_stream = machine().sound().stream_alloc(*this, 0, 2, rate); -} - void c6280_device::device_clock_changed() { - calculate_clocks(); + m_stream->set_sample_rate(clock()); } //------------------------------------------------- @@ -311,7 +300,7 @@ void c6280_device::device_start() m_lfo_control = 0; memset(m_channel, 0, sizeof(channel) * 8); - calculate_clocks(); + m_stream = machine().sound().stream_alloc(*this, 0, 2, clock()); /* Make volume table */ /* PSG has 48dB volume range spread over 32 steps */ @@ -338,5 +327,6 @@ void c6280_device::device_start() save_item(NAME(m_channel[chan].m_noise_control), chan); save_item(NAME(m_channel[chan].m_noise_counter), chan); save_item(NAME(m_channel[chan].m_counter), chan); + save_item(NAME(m_channel[chan].m_tick), chan); } } diff --git a/src/devices/sound/c6280.h b/src/devices/sound/c6280.h index 5b7f94bce7f..0cfdc34799b 100644 --- a/src/devices/sound/c6280.h +++ b/src/devices/sound/c6280.h @@ -8,6 +8,8 @@ class c6280_device : public device_t, public device_sound_interface { public: + static constexpr feature_type imperfect_features() { return feature::SOUND; } // Incorrect / Not verified noise / LFO output + c6280_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); // write only @@ -22,8 +24,6 @@ protected: virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; private: - void calculate_clocks(); - struct channel { uint16_t m_frequency; uint8_t m_control; @@ -32,8 +32,9 @@ private: uint8_t m_index; int16_t m_dda; uint8_t m_noise_control; - uint32_t m_noise_counter; + int32_t m_noise_counter; uint32_t m_counter; + int32_t m_tick; }; // internal state @@ -44,8 +45,6 @@ private: uint8_t m_lfo_control; channel m_channel[8]; int16_t m_volume_table[32]; - uint32_t m_noise_freq_tab[32]; - uint32_t m_wave_freq_tab[4096]; }; DECLARE_DEVICE_TYPE(C6280, c6280_device) diff --git a/src/devices/sound/es8712.cpp b/src/devices/sound/es8712.cpp index e0b2699edd0..00ec7fe90d3 100644 --- a/src/devices/sound/es8712.cpp +++ b/src/devices/sound/es8712.cpp @@ -228,7 +228,7 @@ WRITE_LINE_MEMBER(es8712_device::msm_int) } else { - m_adpcm_select->write_ab(read_byte(m_base_offset)); + m_adpcm_select->ab_w(read_byte(m_base_offset)); m_adpcm_select->select_w(m_adpcm_trigger); m_adpcm_trigger ^= 1; if (m_adpcm_trigger == 0) diff --git a/src/devices/sound/meg.cpp b/src/devices/sound/meg.cpp new file mode 100644 index 00000000000..a1f9d67b293 --- /dev/null +++ b/src/devices/sound/meg.cpp @@ -0,0 +1,384 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha MEG - Multiple effects generator +// +// Audio dsp dedicated to effects generation + +#include "emu.h" +#include "debugger.h" +#include "meg.h" + +DEFINE_DEVICE_TYPE(MEG, meg_device, "meg", "Multiple Effects Generator (HD62098 / XM309A00)") +DEFINE_DEVICE_TYPE(MEGEMB, meg_embedded_device, "megemb", "Multiple Effects Generator (embedded)") + +void meg_base_device::prg_map(address_map &map) +{ + map(0, m_prg_size - 1).ram(); +} + +void meg_base_device::fp_map(address_map &map) +{ + map(0, m_prg_size - 1).ram(); +} + +void meg_base_device::offsets_map(address_map &map) +{ + map(0, 0x7f).ram(); +} + +meg_base_device::meg_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, u32 prg_size) : + cpu_device(mconfig, type, tag, owner, clock), + m_program_config("program", ENDIANNESS_BIG, 64, prg_size > 256 ? 9 : 8, -3, address_map_constructor(FUNC(meg_base_device::prg_map), this)), + m_fp_config("fp", ENDIANNESS_BIG, 16, prg_size > 256 ? 9 : 8, -1, address_map_constructor(FUNC(meg_base_device::fp_map), this)), + m_offsets_config("offsets", ENDIANNESS_BIG, 16, prg_size > 256 ? 7 : 7, -1, address_map_constructor(FUNC(meg_base_device::offsets_map), this)), + m_prg_size(prg_size) +{ +} + + +void meg_base_device::prg_w(u16 address, u64 opcode) +{ + m_program->write_qword(address, opcode); +} + +void meg_base_device::fp_w(u16 address, u16 value) +{ + m_fp->write_word(address, value); +} + +void meg_base_device::offset_w(u16 address, u16 value) +{ + m_offsets->write_word(address, value); +} + +void meg_base_device::lfo_w(u8 reg, u16 value) +{ + m_lfo[reg] = value; + + static const int dt[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 }; + static const int sh[8] = { 0, 0, 1, 2, 3, 4, 5, 6 }; + + int scale = (value >> 5) & 7; + int step = ((value & 31) << sh[scale]) + dt[scale]; + logerror("lfo_w %02x freq=%5.2f phase=%6.4f\n", reg, step * 44100.0/4194304, (value >> 8)/256.0); +} + +void meg_base_device::map_w(u8 reg, u16 value) +{ + m_map[reg] = value; +} + +u64 meg_base_device::prg_r(u16 address) const +{ + return m_program->read_qword(address); +} + +u16 meg_base_device::fp_r(u16 address) const +{ + return m_fp->read_word(address); +} + +u16 meg_base_device::offset_r(u16 address) const +{ + return m_offsets->read_word(address); +} + +u16 meg_base_device::lfo_r(u8 reg) const +{ + return m_lfo[reg]; +} + +u16 meg_base_device::map_r(u8 reg) const +{ + return m_map[reg]; +} + + +void meg_base_device::device_start() +{ + m_program = &space(AS_PROGRAM); + m_fp = &space(AS_FP); + m_offsets = &space(AS_OFFSETS); + + state_add(STATE_GENPC, "GENPC", m_pc).noshow(); + state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); + state_add(0, "PC", m_pc); + + set_icountptr(m_icount); + + save_item(NAME(m_lfo)); + save_item(NAME(m_map)); + save_item(NAME(m_pc)); +} + +void meg_base_device::device_reset() +{ + memset(m_lfo, 0, sizeof(m_lfo)); + memset(m_map, 0, sizeof(m_map)); + m_pc = 0; +} + +uint32_t meg_base_device::execute_min_cycles() const +{ + return 1; +} + +uint32_t meg_base_device::execute_max_cycles() const +{ + return 1; +} + +uint32_t meg_base_device::execute_input_lines() const +{ + return 0; +} + +void meg_base_device::execute_run() +{ + if(machine().debug_flags & DEBUG_FLAG_ENABLED) + debugger_instruction_hook(m_pc); + m_icount = 0; +} + +device_memory_interface::space_config_vector meg_base_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(AS_PROGRAM, &m_program_config), + std::make_pair(AS_FP, &m_fp_config), + std::make_pair(AS_OFFSETS, &m_offsets_config) + }; +} + +void meg_base_device::state_import(const device_state_entry &entry) +{ +} + +void meg_base_device::state_export(const device_state_entry &entry) +{ +} + +void meg_base_device::state_string_export(const device_state_entry &entry, std::string &str) const +{ +} + +std::unique_ptr<util::disasm_interface> meg_base_device::create_disassembler() +{ + return std::make_unique<meg_disassembler>(this); +} + +meg_embedded_device::meg_embedded_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + meg_base_device(mconfig, MEGEMB, tag, owner, clock, 384) +{ +} + +meg_device::meg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + meg_base_device(mconfig, MEG, tag, owner, clock, 256) +{ +} + + +// vl70: +// 6d1e: write 1, r0l +// 6d26: write 2, r0l +// 6d2e: read 2 +// 6d36: write 3, r0l +// 6d3e: write reg 4:r0h, r0l +// 6d52: write reg 5:r0h, r0l-1 +// 6d68: write 7, r0l +// 6d70: write reg 8:r0h, r0l +// 6d84: write reg 9:r0h, r0l +// 6dac: write a, r0l +// 6db4: write reg cd:r1l, r0 +// 6dd4: write reg e:r0h, r0l +// 6dee: write reg f:r0h, r0l +// 6e08: read 10,11 +// 6e1c: write reg 1213:r1l, r0 +// 6e3c: write reg 14:r0h, r0l +// 6e50: write 15, r0l +// 6e58: write reg 16:r0h, r0l +// 6e6c: write reg 17:r0h, r0l +// 6e80: write reg 18:e0h, e0l + +void meg_device::map(address_map &map) +{ + map(0x00, 0x00).w(FUNC(meg_device::select_w)); + map(0x01, 0x01).w(FUNC(meg_device::s1_w)); + map(0x02, 0x02).rw(FUNC(meg_device::s2_r), FUNC(meg_device::s2_w)); + map(0x03, 0x03).w(FUNC(meg_device::s3_w)); + map(0x04, 0x04).w(FUNC(meg_device::s4_w)); + map(0x05, 0x05).w(FUNC(meg_device::s5_w)); + map(0x07, 0x07).w(FUNC(meg_device::s7_w)); + map(0x08, 0x08).w(FUNC(meg_device::s8_w)); + map(0x09, 0x09).w(FUNC(meg_device::s9_w)); + map(0x0a, 0x0a).w(FUNC(meg_device::sa_w)); + map(0x0c, 0x0c).w(FUNC(meg_device::fph_w)); + map(0x0d, 0x0d).w(FUNC(meg_device::fpl_w)); + map(0x0e, 0x0e).w(FUNC(meg_device::se_w)); + map(0x0f, 0x0f).w(FUNC(meg_device::sf_w)); + map(0x10, 0x10).r(FUNC(meg_device::s10_r)); + map(0x11, 0x11).r(FUNC(meg_device::s11_r)); + map(0x12, 0x12).w(FUNC(meg_device::offseth_w)); + map(0x13, 0x13).w(FUNC(meg_device::offsetl_w)); + map(0x14, 0x14).w(FUNC(meg_device::s14_w)); + map(0x15, 0x15).w(FUNC(meg_device::s15_w)); + map(0x16, 0x16).w(FUNC(meg_device::s16_w)); + map(0x17, 0x17).w(FUNC(meg_device::s17_w)); + map(0x18, 0x18).w(FUNC(meg_device::s18_w)); +} + +u8 meg_device::s2_r() +{ + logerror("read r2 %s\n", machine().describe_context()); + return 0x00; +} + +void meg_device::select_w(u8 data) +{ + m_reg = data; +} + +void meg_device::s1_w(u8 data) +{ + logerror("r1 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s2_w(u8 data) +{ + logerror("r2 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s3_w(u8 data) +{ + logerror("r3 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s4_w(u8 data) +{ + if(m_r4[m_reg] != data) { + m_r4[m_reg] = data; + logerror("r4[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s5_w(u8 data) +{ + if(m_r5[m_reg] != data) { + m_r5[m_reg] = data; + logerror("r5[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s7_w(u8 data) +{ + logerror("r7 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s8_w(u8 data) +{ + if(m_r8[m_reg] != data) { + m_r8[m_reg] = data; + logerror("r8[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + + +void meg_device::s9_w(u8 data) +{ + if(m_r9[m_reg] != data) { + m_r9[m_reg] = data; + logerror("r9[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::sa_w(u8 data) +{ + logerror("ra %02x %s\n", data, machine().describe_context()); +} + +void meg_device::fph_w(u8 data) +{ + fp_w(m_reg, (fp_r(m_reg) & 0x00ff) | (data << 8)); +} + + +void meg_device::fpl_w(u8 data) +{ + fp_w(m_reg, (fp_r(m_reg) & 0xff00) | data); +} + +void meg_device::se_w(u8 data) +{ + if(m_re[m_reg] != data) { + m_re[m_reg] = data; + logerror("re[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + + +void meg_device::sf_w(u8 data) +{ + if(m_rf[m_reg] != data) { + m_rf[m_reg] = data; + logerror("rf[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +u8 meg_device::s10_r() +{ + logerror("read r10 %s\n", machine().describe_context()); + return 0x00; +} + +u8 meg_device::s11_r() +{ + logerror("read r11 %s\n", machine().describe_context()); + return 0x00; +} + +void meg_device::offseth_w(u8 data) +{ + offset_w(m_reg, (offset_r(m_reg) & 0x00ff) | (data << 8)); +} + +void meg_device::offsetl_w(u8 data) +{ + offset_w(m_reg, (offset_r(m_reg) & 0xff00) | data); +} + +void meg_device::s14_w(u8 data) +{ + if(m_r14[m_reg] != data) { + m_r14[m_reg] = data; + logerror("r14[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s15_w(u8 data) +{ + logerror("r15 %02x %s\n", data, machine().describe_context()); +} + +void meg_device::s16_w(u8 data) +{ + if(m_r16[m_reg] != data) { + m_r16[m_reg] = data; + logerror("r16[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s17_w(u8 data) +{ + if(m_r17[m_reg] != data) { + m_r17[m_reg] = data; + logerror("r17[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} + +void meg_device::s18_w(u8 data) +{ + if(m_r18[m_reg] != data) { + m_r18[m_reg] = data; + logerror("r18[%02x] = %02x %s\n", m_reg, data, machine().describe_context()); + } +} diff --git a/src/devices/sound/meg.h b/src/devices/sound/meg.h new file mode 100644 index 00000000000..e9ff44f990a --- /dev/null +++ b/src/devices/sound/meg.h @@ -0,0 +1,122 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha MEG - Multiple effects generator +// +// Audio dsp dedicated to effects generation + +#ifndef DEVICES_SOUND_MEG_H +#define DEVICES_SOUND_MEG_H + +#pragma once + +#include "megd.h" + + +class meg_base_device : public cpu_device, public meg_disassembler::info +{ +public: + enum { + AS_FP = 1, + AS_OFFSETS = 2 + }; + + meg_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, u32 prg_size); + + void prg_w(u16 address, u64 opcode); + void fp_w(u16 address, u16 value); + void offset_w(u16 address, u16 value); + void lfo_w(u8 reg, u16 value); + void map_w(u8 reg, u16 value); + u64 prg_r(u16 address) const; + virtual u16 fp_r(u16 address) const override; + virtual u16 offset_r(u16 address) const override; + u16 lfo_r(u8 reg) const; + u16 map_r(u8 reg) const; + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual uint32_t execute_min_cycles() const override; + virtual uint32_t execute_max_cycles() const override; + virtual uint32_t execute_input_lines() const override; + virtual void execute_run() override; + virtual space_config_vector memory_space_config() const override; + virtual void state_import(const device_state_entry &entry) override; + virtual void state_export(const device_state_entry &entry) override; + virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; + virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; + +private: + address_space_config m_program_config, m_fp_config, m_offsets_config; + address_space *m_program, *m_fp, *m_offsets; + + u32 m_prg_size, m_pc; + int m_icount; + + u16 m_lfo[0x18], m_map[8]; + + void prg_map(address_map &map); + void fp_map(address_map &map); + void offsets_map(address_map &map); +}; + +class meg_embedded_device : public meg_base_device +{ +public: + meg_embedded_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44100*384); +}; + +class meg_device : public meg_base_device +{ +public: + meg_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 44100*256); + void map(address_map &map); + +private: + u8 m_r4[256]; + u8 m_r5[256]; + u8 m_r8[256]; + u8 m_r9[256]; + u8 m_re[256]; + u8 m_rf[256]; + u8 m_r12[256]; + u8 m_r13[256]; + u8 m_r14[256]; + u8 m_r16[256]; + u8 m_r17[256]; + u8 m_r18[256]; + u8 m_reg; + u8 s2_r(); + u8 s10_r(); + u8 s11_r(); + void select_w(u8 reg); + void s1_w(u8 data); + void s2_w(u8 data); + void s3_w(u8 data); + void s4_w(u8 data); + void s5_w(u8 data); + void s7_w(u8 data); + void s8_w(u8 data); + void s9_w(u8 data); + void sa_w(u8 data); + void fph_w(u8 data); + void fpl_w(u8 data); + void se_w(u8 data); + void sf_w(u8 data); + void s10_w(u8 data); + void s11_w(u8 data); + void offseth_w(u8 data); + void offsetl_w(u8 data); + void s14_w(u8 data); + void s15_w(u8 data); + void s16_w(u8 data); + void s17_w(u8 data); + void s18_w(u8 data); +}; + + +DECLARE_DEVICE_TYPE(MEG, meg_device) +DECLARE_DEVICE_TYPE(MEGEMB, meg_embedded_device) + +#endif diff --git a/src/devices/sound/megd.cpp b/src/devices/sound/megd.cpp new file mode 100644 index 00000000000..2790a7ca69c --- /dev/null +++ b/src/devices/sound/megd.cpp @@ -0,0 +1,118 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha MEG - Multiple effects generator +// +// Audio dsp dedicated to effects generation +// +// Disassembler + +#include "emu.h" +#include "megd.h" + +meg_disassembler::meg_disassembler(info *inf) : m_info(inf) +{ +} + +u32 meg_disassembler::opcode_alignment() const +{ + return 1; +} + +std::string meg_disassembler::gfp(offs_t address) const +{ + if(!m_info) + return util::string_format("fp%03x", address); + s16 fp = m_info->fp_r(address); + return util::string_format("%g", fp / 16384.0); +} + +std::string meg_disassembler::goffset(offs_t address) const +{ + return m_info ? util::string_format("%x", m_info->offset_r(address)) : util::string_format("of%02x", address); +} + +u32 meg_disassembler::b(u64 opc, u32 start, u32 count) +{ + return (opc >> start) & ((1 << count) - 1); +} + +void meg_disassembler::append(std::string &r, std::string e) +{ + if(r != "") + r += " ; "; + r += e; +} + +// 33333333 33333333 22222222 22222222 11111111 11111111 00000000 00000000 +// fedcba98 76543210 fedcba98 76543210 fedcba98 76543210 fedcba98 76543210 + +// 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 +// 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 +// XLB----- -rrrrrrr r--mmmmm m-MM---- -P-----* -----Arr rrrrrrmm mmmm---- + +// m = low is read port, high is write port, memory register +// r = low is read port, high is high port, rotating register + +// X = used for lo-fi variation only +// L = lfo read +// * = compute mul +// A = mul input = m or r +// P = P sent for register write +// B = register write to mbuf +// M = memory mode, none/read/write/read+1 + +offs_t meg_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ + u64 opc = opcodes.r64(pc); + + std::string r; + + r = util::string_format("[m%02x]", b(opc, 39, 6)); + + if(b(opc, 62, 1)) + append(r, "lfo"); + + if(b(opc, 23, 1)) + switch(b(opc, 24, 2)) { + case 0: + if(b(opc, 18, 1)) + append(r, util::string_format("p += %s*m%02x", gfp(pc), b(opc, 4, 6))); + else + append(r, util::string_format("p += %s*r%02x", gfp(pc), b(opc, 10, 8))); + break; + case 1: + append(r, util::string_format("p = %s*(r%02x+m%02x)", gfp(pc), b(opc, 10, 8), b(opc, 4, 6))); + break; + case 2: + append(r, util::string_format("p ?= %s*(r%02x+m%02x)", gfp(pc), b(opc, 10, 8), b(opc, 4, 6))); + break; + case 3: + if(b(opc, 18, 1)) + append(r, util::string_format("p = %s*m%02x", gfp(pc), b(opc, 4, 6))); + else + append(r, util::string_format("p = %s*r%02x", gfp(pc), b(opc, 10, 8))); + break; + } + + if(b(opc, 30, 1)) { + if(b(opc, 61, 1)) + append(r, "mb = p"); + else if(b(opc, 46, 1) == 1) + append(r, util::string_format("m%02x = p", b(opc, 39, 6))); + else + append(r, util::string_format("r%02x = p", b(opc, 47, 8))); + } + + u32 memmode = b(opc, 36, 2); + if(memmode) { + static const char *modes[4] = { nullptr, "w", "r", "rw" }; + + append(r, util::string_format("mem_%s %x +%s", modes[memmode], b(opc, 33, 3), goffset(pc/3))); + r += util::string_format("-> m%02x", b(opcodes.r64(pc+2), 39, 6)); + } + + stream << r; + + return 1 | SUPPORTED; +} diff --git a/src/devices/sound/megd.h b/src/devices/sound/megd.h new file mode 100644 index 00000000000..59a13aa0ea4 --- /dev/null +++ b/src/devices/sound/megd.h @@ -0,0 +1,39 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Yamaha MEG - Multiple effects generator +// +// Audio dsp dedicated to effects generation +// +// Disassembler + +#ifndef DEVICES_SOUND_MEGD_H +#define DEVICES_SOUND_MEGD_H + +#pragma once + +class meg_disassembler : public util::disasm_interface +{ +public: + class info { + public: + virtual u16 fp_r(u16 address) const = 0; + virtual u16 offset_r(u16 address) const = 0; + }; + + meg_disassembler(info *inf = nullptr); + + virtual u32 opcode_alignment() const override; + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; + +private: + info *m_info; + + std::string gfp(offs_t address) const; + std::string goffset(offs_t address) const; + + static inline u32 b(u64 opc, u32 start, u32 count); + static inline void append(std::string &r, std::string e); +}; + +#endif diff --git a/src/devices/sound/namco_163.cpp b/src/devices/sound/namco_163.cpp new file mode 100644 index 00000000000..6fdde572537 --- /dev/null +++ b/src/devices/sound/namco_163.cpp @@ -0,0 +1,176 @@ +// license:BSD-3-Clause +// copyright-holders:cam900 +/*************************************************************************** + + Namco 163 internal sound emulation by cam900 + 4 bit wavetable (variable length), 1 ~ 8 channel + Reference : https://wiki.nesdev.com/w/index.php/Namco_163_audio + +***************************************************************************/ + +#include "emu.h" +#include "namco_163.h" + + +DEFINE_DEVICE_TYPE(NAMCO_163, namco_163_sound_device, "namco_163_sound", "Namco 163 (Sound)") + +namco_163_sound_device::namco_163_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, NAMCO_163, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_ram(nullptr) + , m_reg_addr(0x78) + , m_addr(0) + , m_inc(false) + , m_disable(false) + , m_stream(nullptr) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void namco_163_sound_device::device_start() +{ + m_ram = make_unique_clear<u8[]>(0x80); + m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / 15); + + save_pointer(NAME(m_ram), 0x80); + save_item(NAME(m_reg_addr)); + save_item(NAME(m_addr)); + save_item(NAME(m_inc)); + save_item(NAME(m_disable)); +} + + +//------------------------------------------------- +// device_clock_changed - called if the clock +// changes +//------------------------------------------------- + +void namco_163_sound_device::device_clock_changed() +{ + m_stream->set_sample_rate(clock() / 15); +} + + +inline s8 namco_163_sound_device::get_sample(u16 addr) +{ + return ((m_ram[(addr >> 1) & 0x7f] >> ((addr & 1) << 2)) & 0xf) - 8; +} + + +WRITE_LINE_MEMBER(namco_163_sound_device::disable_w) +{ + m_disable = state; +} + + +/********************************************************************************/ + +/* + Register Map (in RAM) + + 40 ffff ffff Channel 0 Frequency bits 0 - 7 + 41 pppp pppp Channel 0 Phase bits 0 - 7 + 42 ffff ffff Channel 0 Frequency bits 8 - 15 + 43 pppp pppp Channel 0 Phase bits 8 - 15 + 44 ---- --ff Channel 0 Frequency bits 16 - 17 + llll ll-- Channel 0 Waveform Length (256 - (l * 4)) 4 bit samples + 45 pppp pppp Channel 0 Phase bits 16 - 23 + 46 oooo oooo Channel 0 Waveform Offset at 4 bit samples + 47 ---- cccc Channel 0 Volume + + 48 ffff ffff Channel 1 Frequency bits 0 - 7 + 49 pppp pppp Channel 1 Phase bits 0 - 7 + 4a ffff ffff Channel 1 Frequency bits 8 - 15 + 4b pppp pppp Channel 1 Phase bits 8 - 15 + 4c ---- --ff Channel 1 Frequency bits 16 - 17 + llll ll-- Channel 1 Waveform Length (256 - (l * 4)) 4 bit samples + 4d pppp pppp Channel 1 Phase bits 16 - 23 + 4e oooo oooo Channel 1 Waveform Offset at 4 bit samples + 4f ---- cccc Channel 1 Volume + + . + . + . + + 78 ffff ffff Channel 7 Frequency bits 0 - 7 + 79 pppp pppp Channel 7 Phase bits 0 - 7 + 7a ffff ffff Channel 7 Frequency bits 8 - 15 + 7b pppp pppp Channel 7 Phase bits 8 - 15 + 7c ---- --ff Channel 7 Frequency bits 16 - 17 + llll ll-- Channel 7 Waveform Length (256 - (l * 4)) 4 bit samples + 7d pppp pppp Channel 7 Phase bits 16 - 23 + 7e oooo oooo Channel 7 Waveform Offset at 4 bit samples + 7f ---- cccc Channel 7 Volume + -ccc ---- Enable channels + -000 ---- Enable channel 7 only + -001 ---- Enable channel 7, 6 + -010 ---- Enable channel 7, 6, 5 + + . + . + . + + -111 ---- Enable all channels +*/ + +void namco_163_sound_device::addr_w(u8 data) +{ + m_inc = data & 0x80; + m_addr = data & 0x7f; +} + + +void namco_163_sound_device::data_w(u8 data) +{ + m_stream->update(); + m_ram[m_addr] = data; + if (m_inc) + m_addr = (m_addr + 1) & 0x7f; +} + + +u8 namco_163_sound_device::data_r() +{ + u8 val = m_ram[m_addr]; + if (m_inc) + m_addr = (m_addr + 1) & 0x7f; + + return val; +} + + +void namco_163_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + std::fill_n(&outputs[0][0], samples, 0); + + if (m_disable) + return; + + // Slightly noisy but closer to real hardware behavior + for (int s = 0; s < samples; s++) + { + u32 phase = (m_ram[m_reg_addr + 5] << 16) | (m_ram[m_reg_addr + 3] << 8) | m_ram[m_reg_addr + 1]; + const u32 freq = ((m_ram[m_reg_addr + 4] & 0x3) << 16) | (m_ram[m_reg_addr + 2] << 8) | m_ram[m_reg_addr + 0]; + const u16 length = 256 - (m_ram[m_reg_addr + 4] & 0xfc); + const u16 offset = m_ram[m_reg_addr + 6]; + const u8 vol = m_ram[m_reg_addr + 7] & 0xf; + + phase = (phase + freq) % (length << 16); + s32 output = get_sample((phase >> 16) + offset) * vol; + + m_ram[m_reg_addr + 1] = phase & 0xff; + m_ram[m_reg_addr + 3] = phase >> 8; + m_ram[m_reg_addr + 5] = phase >> 16; + + m_reg_addr += 8; + if (m_reg_addr >= 0x80) + { + m_reg_addr = 0x78 - ((m_ram[0x7f] & 0x70) >> 1); + } + outputs[0][s] = (output << 8); + } +} diff --git a/src/devices/sound/namco_163.h b/src/devices/sound/namco_163.h new file mode 100644 index 00000000000..1256884242b --- /dev/null +++ b/src/devices/sound/namco_163.h @@ -0,0 +1,42 @@ +// license:BSD-3-Clause +// copyright-holders:cam900 +#ifndef MAME_SOUND_NAMCO_163_H +#define MAME_SOUND_NAMCO_163_H + +#pragma once + + +class namco_163_sound_device : public device_t, + public device_sound_interface +{ +public: + namco_163_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + + DECLARE_WRITE_LINE_MEMBER(disable_w); + + void addr_w(u8 data); + void data_w(u8 data); + u8 data_r(); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_clock_changed() override; + + // global sound parameters + std::unique_ptr<u8[]> m_ram; + u8 m_reg_addr; + u8 m_addr; + bool m_inc; + bool m_disable; + sound_stream *m_stream; + + // internals + inline s8 get_sample(u16 addr); + + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; +}; + +DECLARE_DEVICE_TYPE(NAMCO_163, namco_163_sound_device) + +#endif // MAME_SOUND_NAMCO_163_H diff --git a/src/devices/sound/swp30.cpp b/src/devices/sound/swp30.cpp index 56870df5ec7..f43df8084f9 100644 --- a/src/devices/sound/swp30.cpp +++ b/src/devices/sound/swp30.cpp @@ -150,8 +150,15 @@ DEFINE_DEVICE_TYPE(SWP30, swp30_device, "swp30", "Yamaha SWP30 sound chip") swp30_device::swp30_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, SWP30, tag, owner, clock), device_sound_interface(mconfig, *this), - device_rom_interface(mconfig, *this, 25+2, ENDIANNESS_LITTLE, 32) + device_rom_interface(mconfig, *this, 25+2, ENDIANNESS_LITTLE, 32), + m_meg(*this, "meg") { + (void)m_map; +} + +void swp30_device::device_add_mconfig(machine_config &config) +{ + MEGEMB(config, m_meg); } void swp30_device::device_start() @@ -160,7 +167,7 @@ void swp30_device::device_start() // Attenuantion for panning is 4.4 floating point. That means 0 // to -96.3dB. Since it's a nice range, we assume it's the same - // for other attenuation values. Computed value is is 1.16 + // for other attenuation values. Computed value is 1.16 // format, to avoid overflow for(int i=0; i<256; i++) @@ -328,8 +335,8 @@ void swp30_device::map(address_map &map) rchan(map, 0x27).rw(FUNC(swp30_device::prg_fp_r<3>), FUNC(swp30_device::prg_fp_w<3>)); rchan(map, 0x29).rw(FUNC(swp30_device::prg_fp_r<4>), FUNC(swp30_device::prg_fp_w<4>)); rchan(map, 0x2b).rw(FUNC(swp30_device::prg_fp_r<5>), FUNC(swp30_device::prg_fp_w<5>)); - rchan(map, 0x30).rw(FUNC(swp30_device::prg_int_r<0>), FUNC(swp30_device::prg_int_w<0>)); - rchan(map, 0x31).rw(FUNC(swp30_device::prg_int_r<1>), FUNC(swp30_device::prg_int_w<1>)); + rchan(map, 0x30).rw(FUNC(swp30_device::prg_off_r<0>), FUNC(swp30_device::prg_off_w<0>)); + rchan(map, 0x31).rw(FUNC(swp30_device::prg_off_r<1>), FUNC(swp30_device::prg_off_w<1>)); rchan(map, 0x3e).rw(FUNC(swp30_device::prg_lfo_r<0>), FUNC(swp30_device::prg_lfo_w<0>)); rchan(map, 0x3f).rw(FUNC(swp30_device::prg_lfo_r<1>), FUNC(swp30_device::prg_lfo_w<1>)); } @@ -381,14 +388,14 @@ void swp30_device::prg_address_w(u16 data) template<int sel> u16 swp30_device::prg_r() { constexpr offs_t shift = 48-16*sel; - return m_program[m_program_address] >> shift; + return m_meg->prg_r(m_program_address) >> shift; } template<int sel> void swp30_device::prg_w(u16 data) { constexpr offs_t shift = 48-16*sel; constexpr u64 mask = ~(u64(0xffff) << shift); - m_program[m_program_address] = (m_program[m_program_address] & mask) | (u64(data) << shift); + m_meg->prg_w(m_program_address, (m_meg->prg_r(m_program_address) & mask) | (u64(data) << shift)); if(sel == 3) { if(0) @@ -402,14 +409,12 @@ template<int sel> void swp30_device::prg_w(u16 data) template<int sel> u16 swp30_device::map_r() { - return m_map[sel]; + return m_meg->map_r(sel); } template<int sel> void swp30_device::map_w(u16 data) { - m_map[sel] = data; - if(0) - logerror("map %d: type=%02x offset=%05x size=%05x\n", sel, data >> 11, (data & 0xff) << 10, 0x400 << ((data >> 8) & 7)); + m_meg->map_w(sel, data); } @@ -656,54 +661,36 @@ void swp30_device::address_l_w(offs_t offset, u16 data) } -// MEG registers (Multiple Effects Generator) +// MEG registers forwarding template<int sel> u16 swp30_device::prg_fp_r(offs_t offset) { - offs_t adr = (offset >> 6)*6 + sel; - return m_program_pfp[adr]; + return m_meg->fp_r((offset >> 6)*6 + sel); } template<int sel> void swp30_device::prg_fp_w(offs_t offset, u16 data) { - offs_t adr = (offset >> 6)*6 + sel; - m_program_pfp[adr] = data; - if(0) - logerror("prg_fp_w %03x, %04x\n", adr, data); + m_meg->fp_w((offset >> 6)*6 + sel, data); } -template<int sel> u16 swp30_device::prg_int_r(offs_t offset) +template<int sel> u16 swp30_device::prg_off_r(offs_t offset) { - offs_t adr = (offset >> 6)*2 + sel; - return m_program_pint[adr]; + return m_meg->offset_r((offset >> 6)*2 + sel); } -template<int sel> void swp30_device::prg_int_w(offs_t offset, u16 data) +template<int sel> void swp30_device::prg_off_w(offs_t offset, u16 data) { - offs_t adr = (offset >> 6)*2 + sel; - m_program_pint[adr] = data; - if(0) - logerror("prg_int_w %02x, %04x\n", adr, data); + m_meg->offset_w((offset >> 6)*2 + sel, data); } template<int sel> u16 swp30_device::prg_lfo_r(offs_t offset) { - offs_t adr = (offset >> 6)*2 + sel; - return m_program_plfo[adr]; + return m_meg->lfo_r((offset >> 6)*2 + sel); } template<int sel> void swp30_device::prg_lfo_w(offs_t offset, u16 data) { - offs_t adr = (offset >> 6)*2 + sel; - m_program_plfo[adr] = data; - - static const int dt[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 }; - static const int sh[8] = { 0, 0, 1, 2, 3, 4, 5, 6 }; - - int scale = (data >> 5) & 7; - int step = ((data & 31) << sh[scale]) + dt[scale]; - if(0) - logerror("prg_lfo_w %02x freq=%5.2f phase=%6.4f\n", adr, step * 44100.0/4194304, (data >> 8)/256.0); + m_meg->lfo_w((offset >> 6)*2 + sel, data); } diff --git a/src/devices/sound/swp30.h b/src/devices/sound/swp30.h index 56e24b453f0..dd95a8dd85f 100644 --- a/src/devices/sound/swp30.h +++ b/src/devices/sound/swp30.h @@ -8,6 +8,8 @@ #pragma once +#include "meg.h" + class swp30_device : public device_t, public device_sound_interface, public device_rom_interface { public: @@ -20,8 +22,11 @@ protected: virtual void device_reset() override; virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; virtual void rom_bank_updated() override; + virtual void device_add_mconfig(machine_config &config) override; private: + required_device<meg_embedded_device> m_meg; + sound_stream *m_stream; s32 m_sample_increment[0x4000]; @@ -99,8 +104,8 @@ private: // MEG registers template<int sel> u16 prg_fp_r(offs_t offset); template<int sel> void prg_fp_w(offs_t offset, u16 data); - template<int sel> u16 prg_int_r(offs_t offset); - template<int sel> void prg_int_w(offs_t offset, u16 data); + template<int sel> u16 prg_off_r(offs_t offset); + template<int sel> void prg_off_w(offs_t offset, u16 data); template<int sel> u16 prg_lfo_r(offs_t offset); template<int sel> void prg_lfo_w(offs_t offset, u16 data); diff --git a/src/devices/sound/vrc6.cpp b/src/devices/sound/vrc6.cpp index 29729fc9fcc..b769fde4043 100644 --- a/src/devices/sound/vrc6.cpp +++ b/src/devices/sound/vrc6.cpp @@ -2,7 +2,7 @@ // copyright-holders:R. Belmont /*************************************************************************** - vrc6.c + vrc6.cpp Konami VRC6 additional sound channels Emulation by R. Belmont @@ -16,8 +16,6 @@ #include "emu.h" #include "vrc6.h" -#define DISABLE_VRC6_SOUND // not ready yet - // device type definition DEFINE_DEVICE_TYPE(VRC6, vrc6snd_device, "vrc6snd", "Konami VRC6 (Sound)") @@ -29,10 +27,10 @@ DEFINE_DEVICE_TYPE(VRC6, vrc6snd_device, "vrc6snd", "Konami VRC6 (Sound)") // vrc6snd_device - constructor //------------------------------------------------- -vrc6snd_device::vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +vrc6snd_device::vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, VRC6, tag, owner, clock) , device_sound_interface(mconfig, *this) - , m_freqctrl(0), m_pulsectrl{ 0, 0 }, m_sawrate(0) + , m_freqctrl(0), m_pulsectrl{ 0, 0 }, m_sawrate(0), m_master_freq(0) , m_pulsefrql{ 0, 0 }, m_pulsefrqh{ 0, 0 }, m_pulseduty{ 0, 0 } , m_sawfrql(0), m_sawfrqh(0), m_sawclock(0), m_sawaccum(0) , m_ticks{ 0, 0, 0 } @@ -55,10 +53,12 @@ void vrc6snd_device::device_start() m_ticks[0] = m_ticks[1] = m_ticks[2] = 0; m_output[0] = m_output[1] = m_output[2] = 0; m_pulseduty[0] = m_pulseduty[1] = 15; + m_master_freq = 0; save_item(NAME(m_freqctrl)); save_item(NAME(m_pulsectrl)); save_item(NAME(m_sawrate)); + save_item(NAME(m_master_freq)); save_item(NAME(m_sawaccum)); save_item(NAME(m_pulsefrql)); save_item(NAME(m_pulsefrqh)); @@ -85,6 +85,7 @@ void vrc6snd_device::device_reset() m_output[0] = m_output[1] = m_output[2] = 0; m_pulseduty[0] = m_pulseduty[1] = 15; m_pulsefrqh[0] = m_pulsefrqh[1] = m_sawfrqh = 0; + m_master_freq = 0; } //------------------------------------------------- @@ -94,27 +95,22 @@ void vrc6snd_device::device_reset() void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { - stream_sample_t *out = outputs[0]; - int16_t tmp; - int i; + std::fill_n(&outputs[0][0], samples, 0); // check global halt bit if (m_freqctrl & 1) - { return; - } - for (i = 0; i < samples; i++) + for (int i = 0; i < samples; i++) { // update pulse1 if (m_pulsefrqh[0] & 0x80) { - m_ticks[0]--; if (m_ticks[0] == 0) { - m_ticks[0] = m_pulsefrql[0] | (m_pulsefrqh[0] & 0xf)<<4; + m_ticks[0] = (m_pulsefrql[0] | ((m_pulsefrqh[0] & 0xf) << 8)) >> m_master_freq; - m_pulseduty[0]--; + m_pulseduty[0] = (m_pulseduty[0] - 1) & 0xf; if (m_pulsectrl[0] & 0x80) { m_output[0] = m_pulsectrl[0] & 0xf; @@ -130,12 +126,9 @@ void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t * m_output[0] = 0; } } - - if (m_pulseduty[0] == 0) - { - m_pulseduty[0] = 15; - } } + else + m_ticks[0]--; } else { @@ -145,12 +138,11 @@ void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t * // update pulse2 if (m_pulsefrqh[1] & 0x80) { - m_ticks[1]--; if (m_ticks[1] == 0) { - m_ticks[1] = m_pulsefrql[1] | (m_pulsefrqh[1] & 0xf)<<4; + m_ticks[1] = (m_pulsefrql[1] | ((m_pulsefrqh[1] & 0xf) << 8)) >> m_master_freq; - m_pulseduty[1]--; + m_pulseduty[1] = (m_pulseduty[1] - 1) & 0xf; if (m_pulsectrl[1] & 0x80) { m_output[1] = m_pulsectrl[1] & 0xf; @@ -166,12 +158,9 @@ void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t * m_output[1] = 0; } } - - if (m_pulseduty[1] == 0) - { - m_pulseduty[1] = 15; - } } + else + m_ticks[1]--; } else { @@ -181,10 +170,9 @@ void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t * // update saw if (m_sawfrqh & 0x80) { - m_ticks[2]--; if (m_ticks[2] == 0) { - m_ticks[2] = m_sawfrql | (m_sawfrqh & 0xf)<<4; + m_ticks[2] = (m_sawfrql | ((m_sawfrqh & 0xf) << 8)) >> m_master_freq; // only update on even steps if ((m_sawclock > 0) && (!(m_sawclock & 1))) @@ -200,6 +188,8 @@ void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t * m_output[2] = 0; } } + else + m_ticks[2]--; } else { @@ -207,10 +197,10 @@ void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t * } // sum 2 4-bit pulses, 1 5-bit saw = unsigned 6 bit output - tmp = (int16_t)(uint8_t)(m_output[0] + m_output[1] + m_output[2]); + s16 tmp = (s16)(u8)(m_output[0] + m_output[1] + m_output[2]); tmp <<= 8; - out[i] = tmp; + outputs[0][i] = tmp; } } @@ -218,7 +208,7 @@ void vrc6snd_device::sound_stream_update(sound_stream &stream, stream_sample_t * // write - write to the chip's registers //--------------------------------------- -void vrc6snd_device::write(offs_t offset, uint8_t data) +void vrc6snd_device::write(offs_t offset, u8 data) { switch (offset >> 8) { @@ -232,28 +222,43 @@ void vrc6snd_device::write(offs_t offset, uint8_t data) case 1: m_pulsefrql[0] = data; - if (!(m_pulsefrqh[1] & 0x80)) + if (!(m_pulsefrqh[0] & 0x80)) { - m_ticks[0] &= ~0xff; - m_ticks[0] |= m_pulsefrql[0]; + m_ticks[0] = (m_pulsefrql[0] | ((m_pulsefrqh[0] & 0xf) << 8)) >> m_master_freq; } break; case 2: - #ifndef DISABLE_VRC6_SOUND m_pulsefrqh[0] = data; // if disabling channel, reset phase if (!(data & 0x80)) { m_pulseduty[0] = 15; - m_ticks[0] &= 0xff; - m_ticks[0] |= (m_pulsefrqh[0] & 0xf)<<4; + m_ticks[0] = (m_pulsefrql[0] | ((m_pulsefrqh[0] & 0xf) << 8)) >> m_master_freq; } - #endif break; case 3: m_freqctrl = data; + if (m_freqctrl & 4) + m_master_freq = 0x8; + else if (m_freqctrl & 2) + m_master_freq = 0x4; + else + m_master_freq = 0; + + if (!(m_pulsefrqh[0] & 0x80)) + { + m_ticks[0] = (m_pulsefrql[0] | ((m_pulsefrqh[0] & 0xf) << 8)) >> m_master_freq; + } + if (!(m_pulsefrqh[1] & 0x80)) + { + m_ticks[1] = (m_pulsefrql[1] | ((m_pulsefrqh[1] & 0xf) << 8)) >> m_master_freq; + } + if (!(m_sawfrqh & 0x80)) + { + m_ticks[2] = (m_sawfrql | ((m_sawfrqh & 0xf) << 8)) >> m_master_freq; + } break; } break; @@ -270,22 +275,18 @@ void vrc6snd_device::write(offs_t offset, uint8_t data) m_pulsefrql[1] = data; if (!(m_pulsefrqh[1] & 0x80)) { - m_ticks[1] &= ~0xff; - m_ticks[1] |= m_pulsefrql[1]; + m_ticks[1] = (m_pulsefrql[1] | ((m_pulsefrqh[1] & 0xf) << 8)) >> m_master_freq; } break; case 2: - #ifndef DISABLE_VRC6_SOUND m_pulsefrqh[1] = data; // if disabling channel, reset phase if (!(data & 0x80)) { m_pulseduty[1] = 15; - m_ticks[1] &= 0xff; - m_ticks[1] |= (m_pulsefrqh[1] & 0xf)<<4; + m_ticks[1] = (m_pulsefrql[1] | ((m_pulsefrqh[1] & 0xf) << 8)) >> m_master_freq; } - #endif break; } break; @@ -302,22 +303,18 @@ void vrc6snd_device::write(offs_t offset, uint8_t data) m_sawfrql = data; if (!(m_sawfrqh & 0x80)) { - m_ticks[2] &= ~0xff; - m_ticks[2] |= m_sawfrql; + m_ticks[2] = (m_sawfrql | ((m_sawfrqh & 0xf) << 8)) >> m_master_freq; } break; case 2: - #ifndef DISABLE_VRC6_SOUND m_sawfrqh = data; // if disabling channel, reset phase if (!(data & 0x80)) { m_sawaccum = 0; - m_ticks[2] &= 0xff; - m_ticks[2] |= (m_sawfrqh & 0xf)<<4; + m_ticks[2] = (m_sawfrql | ((m_sawfrqh & 0xf) << 8)) >> m_master_freq; } - #endif break; } break; diff --git a/src/devices/sound/vrc6.h b/src/devices/sound/vrc6.h index a24d34a90c4..f1ea8f5c9e3 100644 --- a/src/devices/sound/vrc6.h +++ b/src/devices/sound/vrc6.h @@ -22,9 +22,9 @@ class vrc6snd_device : public device_t, public device_sound_interface { public: // construction/destruction - vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + vrc6snd_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - void write(offs_t offset, uint8_t data); + void write(offs_t offset, u8 data); protected: // device-level overrides @@ -34,11 +34,11 @@ protected: virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; private: - uint8_t m_freqctrl, m_pulsectrl[2], m_sawrate; - uint8_t m_pulsefrql[2], m_pulsefrqh[2], m_pulseduty[2]; - uint8_t m_sawfrql, m_sawfrqh, m_sawclock, m_sawaccum; - uint16_t m_ticks[3]; - uint8_t m_output[3]; + u8 m_freqctrl, m_pulsectrl[2], m_sawrate, m_master_freq; + u8 m_pulsefrql[2], m_pulsefrqh[2], m_pulseduty[2]; + u8 m_sawfrql, m_sawfrqh, m_sawclock, m_sawaccum; + u16 m_ticks[3]; + u8 m_output[3]; sound_stream *m_stream; }; diff --git a/src/devices/sound/ym2413.cpp b/src/devices/sound/ym2413.cpp index 3935b1baa0e..755e66ea629 100644 --- a/src/devices/sound/ym2413.cpp +++ b/src/devices/sound/ym2413.cpp @@ -42,6 +42,8 @@ to do: #include "emu.h" #include "ym2413.h" +#include <algorithm> + #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ #define EG_SH 16 /* 16.16 fixed point (EG timing) */ #define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ @@ -131,7 +133,7 @@ const uint32_t ym2413_device::sl_tab[16] = { }; #undef SC -const unsigned char ym2413_device::eg_inc[15*RATE_STEPS] = { +const uint8_t ym2413_device::eg_inc[15*RATE_STEPS] = { /*cycle:0 1 2 3 4 5 6 7*/ /* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */ @@ -158,7 +160,7 @@ const unsigned char ym2413_device::eg_inc[15*RATE_STEPS] = { #define O(a) (a*RATE_STEPS) /*note that there is no O(13) in this table - it's directly in the code */ -const unsigned char ym2413_device::eg_rate_select[16+64+16] = { /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ +const uint8_t ym2413_device::eg_rate_select[16+64+16] = { /* Envelope Generator rates (16 + 64 rates + 16 RKS) */ /* 16 infinite time rates */ O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14), @@ -199,7 +201,7 @@ const unsigned char ym2413_device::eg_rate_select[16+64+16] = { /* Envelope Ge /*mask 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0 */ #define O(a) (a*1) -const unsigned char ym2413_device::eg_rate_shift[16+64+16] = { /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ +const uint8_t ym2413_device::eg_rate_shift[16+64+16] = { /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */ /* 16 infinite time rates */ O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), @@ -350,9 +352,10 @@ const int8_t ym2413_device::lfo_pm_table[8*8] = { - waveform DC and DM select are 100% correct */ -const unsigned char ym2413_device::table[19][8] = { +const uint8_t ym2413_device::table[19][8] = { /* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */ /* 0 1 2 3 4 5 6 7 */ +/* These YM2413(OPLL) patch dumps are done via audio analysis (and a/b testing?) from Jarek and are known to be inaccurate */ {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, //0 {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, //1 @@ -385,9 +388,45 @@ const unsigned char ym2413_device::table[19][8] = { /* drum instruments definitions */ /* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */ /* 0 1 2 3 4 5 6 7 */ - {0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */ - {0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */ - {0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */ +/* old dumps via audio analysis (and a/b testing?) from Jarek */ +//{0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */ +//{0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */ +//{0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */ +/* Drums dumped from the VRC7 using debug mode, these are likely also correct for ym2413(OPLL) but need verification */ + {0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d },/* BD */ + {0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68 },/* HH, SD */ + {0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55 },/* TOM, TOP CYM */ +}; + +// VRC7 Instruments : Dumped from internal ROM +// reference : https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2 +const uint8_t vrc7snd_device::vrc7_table[19][8] = { +/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */ +/* 0 1 2 3 4 5 6 7 */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, //0 (This is the user-defined instrument, should this default to anything?) + + {0x03, 0x21, 0x05, 0x06, 0xe8, 0x81, 0x42, 0x27 }, //1 + {0x13, 0x41, 0x14, 0x0d, 0xd8, 0xf6, 0x23, 0x12 }, //2 + {0x11, 0x11, 0x08, 0x08, 0xfa, 0xb2, 0x20, 0x12 }, //3 + {0x31, 0x61, 0x0c, 0x07, 0xa8, 0x64, 0x61, 0x27 }, //4 + {0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28 }, //5 + {0x02, 0x01, 0x06, 0x00, 0xa3, 0xe2, 0xf4, 0xf4 }, //6 + {0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07 }, //7 + {0x23, 0x21, 0x22, 0x17, 0xa2, 0x72, 0x01, 0x17 }, //8 + {0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01 }, //9 + {0xb5, 0x01, 0x0f, 0x0f, 0xa8, 0xa5, 0x51, 0x02 }, //A + {0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12 }, //B + {0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16 }, //C + {0x01, 0x02, 0xd3, 0x05, 0xc9, 0x95, 0x03, 0x02 }, //D + {0x61, 0x63, 0x0c, 0x00, 0x94, 0xc0, 0x33, 0xf6 }, //E + {0x21, 0x72, 0x0d, 0x00, 0xc1, 0xd5, 0x56, 0x06 }, //F + +/* Drums (silent due to no RO output pin(?) on VRC7, but present internally; these are probably shared with YM2413) */ +/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */ +/* 0 1 2 3 4 5 6 7 */ + {0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d },/* BD */ + {0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68 },/* HH, SD */ + {0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55 },/* TOM, TOP CYM */ }; /* work table */ @@ -823,21 +862,21 @@ void ym2413_device::rhythm_calc( OPLL_CH *CH, unsigned int noise ) */ /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; - unsigned char bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; - unsigned char bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; + uint8_t bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; + uint8_t bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; + uint8_t bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; - unsigned char res1 = (bit2 ^ bit7) | bit3; + uint8_t res1 = (bit2 ^ bit7) | bit3; /* when res1 = 0 phase = 0x000 | 0xd0; */ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */ uint32_t phase = res1 ? (0x200|(0xd0>>2)) : 0xd0; /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; - unsigned char bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; + uint8_t bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; + uint8_t bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; - unsigned char res2 = (bit3e | bit5e); + uint8_t res2 = (bit3e | bit5e); /* when res2 = 0 pass the phase from calculation above (res1); */ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */ @@ -868,7 +907,7 @@ void ym2413_device::rhythm_calc( OPLL_CH *CH, unsigned int noise ) if( env < ENV_QUIET ) { /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit8 = ((SLOT7_1->phase>>FREQ_SH)>>8)&1; + uint8_t bit8 = ((SLOT7_1->phase>>FREQ_SH)>>8)&1; /* when bit8 = 0 phase = 0x100; */ /* when bit8 = 1 phase = 0x200; */ @@ -894,21 +933,21 @@ void ym2413_device::rhythm_calc( OPLL_CH *CH, unsigned int noise ) if( env < ENV_QUIET ) { /* base frequency derived from operator 1 in channel 7 */ - unsigned char bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; - unsigned char bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; - unsigned char bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; + uint8_t bit7 = ((SLOT7_1->phase>>FREQ_SH)>>7)&1; + uint8_t bit3 = ((SLOT7_1->phase>>FREQ_SH)>>3)&1; + uint8_t bit2 = ((SLOT7_1->phase>>FREQ_SH)>>2)&1; - unsigned char res1 = (bit2 ^ bit7) | bit3; + uint8_t res1 = (bit2 ^ bit7) | bit3; /* when res1 = 0 phase = 0x000 | 0x100; */ /* when res1 = 1 phase = 0x200 | 0x100; */ uint32_t phase = res1 ? 0x300 : 0x100; /* enable gate based on frequency of operator 2 in channel 8 */ - unsigned char bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; - unsigned char bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; + uint8_t bit5e= ((SLOT8_2->phase>>FREQ_SH)>>5)&1; + uint8_t bit3e= ((SLOT8_2->phase>>FREQ_SH)>>3)&1; - unsigned char res2 = (bit3e | bit5e); + uint8_t res2 = (bit3e | bit5e); /* when res2 = 0 pass the phase from calculation above (res1); */ /* when res2 = 1 phase = 0x200 | 0x100; */ if (res2) @@ -1635,11 +1674,14 @@ void ym2413_device::device_reset() noise_rng = 1; /* noise shift register */ /* setup instruments table */ - for (int i=0; i<19; i++) + if (m_inst_table != nullptr) { - for (int c=0; c<8; c++) + for (int i=0; i<19; i++) { - inst_tab[i][c] = table[i][c]; + for (int c=0; c<8; c++) + { + inst_tab[i][c] = m_inst_table[i][c]; + } } } @@ -1686,7 +1728,37 @@ void ym2413_device::data_port_w(u8 data) DEFINE_DEVICE_TYPE(YM2413, ym2413_device, "ym2413", "Yamaha YM2413 OPLL") ym2413_device::ym2413_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) - : device_t(mconfig, YM2413, tag, owner, clock) + : ym2413_device(mconfig, YM2413, tag, owner, clock) +{ + for (int i = 0; i < 19; i++) + { + for (int c = 0; c < 8; c++) + { + m_inst_table[i][c] = table[i][c]; + } + } +} + +ym2413_device::ym2413_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) , device_sound_interface(mconfig, *this) { + for (int i = 0; i < 19; i++) + { + std::fill_n(&m_inst_table[i][0], 8, 0); + } +} + +DEFINE_DEVICE_TYPE(VRC7, vrc7snd_device, "vrc7snd", "Konami VRC7 (Sound)") + +vrc7snd_device::vrc7snd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : ym2413_device(mconfig, VRC7, tag, owner, clock) +{ + for (int i = 0; i < 19; i++) + { + for (int c = 0; c < 8; c++) + { + m_inst_table[i][c] = vrc7_table[i][c]; + } + } } diff --git a/src/devices/sound/ym2413.h b/src/devices/sound/ym2413.h index 32114624eff..1b0d9869770 100644 --- a/src/devices/sound/ym2413.h +++ b/src/devices/sound/ym2413.h @@ -17,6 +17,8 @@ public: void data_port_w(u8 data); protected: + ym2413_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + // device-level overrides virtual void device_start() override; virtual void device_clock_changed() override; @@ -25,6 +27,8 @@ protected: // sound stream update overrides virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + uint8_t m_inst_table[19][8]; + private: struct OPLL_SLOT { @@ -106,13 +110,13 @@ private: static const double ksl_tab[8*16]; static const uint32_t ksl_shift[4]; static const uint32_t sl_tab[16]; - static const unsigned char eg_inc[15*RATE_STEPS]; - static const unsigned char eg_rate_select[16+64+16]; - static const unsigned char eg_rate_shift[16+64+16]; + static const uint8_t eg_inc[15*RATE_STEPS]; + static const uint8_t eg_rate_select[16+64+16]; + static const uint8_t eg_rate_shift[16+64+16]; static const uint8_t mul_tab[16]; static const uint8_t lfo_am_table[LFO_AM_TAB_ELEMENTS]; static const int8_t lfo_pm_table[8*8]; - static const unsigned char table[19][8]; + static const uint8_t table[19][8]; int tl_tab[TL_TAB_LEN]; @@ -185,4 +189,15 @@ private: DECLARE_DEVICE_TYPE(YM2413, ym2413_device) +class vrc7snd_device : public ym2413_device +{ +public: + vrc7snd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + +private: + static const uint8_t vrc7_table[19][8]; +}; + +DECLARE_DEVICE_TYPE(VRC7, vrc7snd_device) + #endif // MAME_SOUND_YM2413_H |