diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index 3b98b9095ce..a49d74dbc9e 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -1589,3 +1589,15 @@ if (SOUNDS["KS0164"]~=null) then MAME_DIR .. "src/devices/sound/ks0164.h", } end + +--------------------------------------------------- +-- +--@src/devices/sound/rp2c33_snd.h,SOUNDS["RP2C33_SOUND"] = true +--------------------------------------------------- + +if (SOUNDS["RP2C33_SOUND"]~=null) then + files { + MAME_DIR .. "src/devices/sound/rp2c33_snd.cpp", + MAME_DIR .. "src/devices/sound/rp2c33_snd.h", + } +end diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 17183bc20fb..281808353c6 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["RP2C33_SOUND"] = true -------------------------------------------------- -- specify available video cores diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index d48981e9c55..a3545320953 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["RP2C33_SOUND"] = true -------------------------------------------------- -- specify available video cores diff --git a/src/devices/bus/nes/disksys.cpp b/src/devices/bus/nes/disksys.cpp index 0b2bb169a2d..d94dcf2b4c1 100644 --- a/src/devices/bus/nes/disksys.cpp +++ b/src/devices/bus/nes/disksys.cpp @@ -23,6 +23,7 @@ #include "disksys.h" #include "imagedev/flopdrv.h" #include "formats/nes_dsk.h" +#include "speaker.h" #ifdef NES_PCB_DEBUG #define VERBOSE 1 @@ -54,6 +55,11 @@ static const floppy_interface nes_floppy_interface = void nes_disksys_device::device_add_mconfig(machine_config &config) { LEGACY_FLOPPY(config, m_disk, 0, &nes_floppy_interface); + + SPEAKER(config, "addon").front_center(); // connected to motherboard + + RP2C33_SOUND(config, m_sound, XTAL(21'477'272)/12); // clock driven from motherboard? + m_sound->add_route(0, "addon", 0.2); } @@ -102,6 +108,7 @@ nes_disksys_device::nes_disksys_device(const machine_config &mconfig, const char , m_2c33_rom(*this, "drive") , m_fds_data(nullptr) , m_disk(*this, "floppy0") + , m_sound(*this, "rp2c33snd") , irq_timer(nullptr) , m_irq_count(0), m_irq_count_latch(0), m_irq_enable(0), m_irq_transfer(0), m_fds_motor_on(0), m_fds_door_closed(0), m_fds_current_side(0), m_fds_head_position(0), m_fds_status0(0), m_read_mode(0), m_drive_ready(0) , m_fds_sides(0), m_fds_last_side(0), m_fds_count(0) @@ -216,6 +223,8 @@ void nes_disksys_device::write_ex(offs_t offset, uint8_t data) if (offset >= 0x20 && offset < 0x60) { // wavetable + if (m_sound_en) + m_sound->wave_w(offset - 0x20, data); } switch (offset) @@ -233,6 +242,7 @@ void nes_disksys_device::write_ex(offs_t offset, uint8_t data) case 0x03: // bit0 - Enable disk I/O registers // bit1 - Enable sound I/O registers + m_sound_en = BIT(data, 1); break; case 0x04: // write data out to disk @@ -277,6 +287,8 @@ void nes_disksys_device::write_ex(offs_t offset, uint8_t data) case 0x68: // $4088 - Mod table write case 0x69: // $4089 - Wave write / master volume case 0x6a: // $408a - Envelope speed + if (m_sound_en) + m_sound->write(offset - 0x60, data); break; } } @@ -284,11 +296,13 @@ void nes_disksys_device::write_ex(offs_t offset, uint8_t data) uint8_t nes_disksys_device::read_ex(offs_t offset) { LOG_MMC(("Famicom Disk System read_ex, offset: %04x\n", offset)); - uint8_t ret; + uint8_t ret = 0x00; if (offset >= 0x20 && offset < 0x60) { // wavetable + if (m_sound_en) + ret = m_sound->wave_r(offset - 0x20); } switch (offset) @@ -351,6 +365,9 @@ uint8_t nes_disksys_device::read_ex(offs_t offset) break; case 0x70: // $4090 - Volume gain - write through $4080 case 0x72: // $4092 - Mod gain - read through $4084 + if (m_sound_en) + ret = m_sound->read(offset - 0x60); + break; default: ret = 0x00; break; diff --git a/src/devices/bus/nes/disksys.h b/src/devices/bus/nes/disksys.h index 1e3e45afb1e..1b5fed37e77 100644 --- a/src/devices/bus/nes/disksys.h +++ b/src/devices/bus/nes/disksys.h @@ -7,6 +7,7 @@ #include "nxrom.h" #include "imagedev/flopdrv.h" +#include "sound/rp2c33_snd.h" // ======================> nes_disksys_device @@ -45,6 +46,7 @@ private: std::unique_ptr m_fds_data; // here, we store a copy of the disk required_device m_disk; + required_device m_sound; static const device_timer_id TIMER_IRQ = 0; emu_timer *irq_timer; @@ -54,6 +56,7 @@ private: uint16_t m_irq_count, m_irq_count_latch; int m_irq_enable, m_irq_transfer; + bool m_sound_en; uint8_t m_fds_motor_on; uint8_t m_fds_door_closed; diff --git a/src/devices/sound/rp2c33_snd.cpp b/src/devices/sound/rp2c33_snd.cpp new file mode 100644 index 00000000000..d8d8bd2af82 --- /dev/null +++ b/src/devices/sound/rp2c33_snd.cpp @@ -0,0 +1,361 @@ +// license:BSD-3-Clause +// copyright-holders:cam900, Brad Smith, Brezza +/*************************************************************************** + + Ricoh RP2C33 Sound emulation + + Based on: + - NSFplay github code by Brad Smith/Brezza + - Information from NESDev wiki + (https://wiki.nesdev.com/w/index.php/FDS_audio) + + TODO: + - verify register behaviors + - verify unknown read, writes + - Lowpass filter? + +***************************************************************************/ + +#include "emu.h" +#include "rp2c33_snd.h" + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +// device type definition +DEFINE_DEVICE_TYPE(RP2C33_SOUND, rp2c33_sound_device, "rp2c33_snd", "Ricoh RP2C33 (sound)") + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// rp2c33_sound_device - constructor +//------------------------------------------------- + +rp2c33_sound_device::rp2c33_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, RP2C33_SOUND, tag, owner, clock) + , device_sound_interface(mconfig, *this) +{ + std::fill(std::begin(m_wave), std::end(m_wave), 0); + std::fill(std::begin(m_mod_table), std::end(m_mod_table), 0); + std::fill(std::begin(m_mvol_table), std::end(m_mvol_table), 0); +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void rp2c33_sound_device::device_start() +{ + // normalize output to 16 bit + for (int i = 0; i < 4; i++) + m_mvol_table[i] = int((65536.0 / (32.0 * 64.0)) * 2.0 / double(i + 2)); + + m_stream = machine().sound().stream_alloc(*this, 0, 1, clock()); + + // save states + save_item(NAME(m_regs)); + save_item(NAME(m_wave)); + save_item(NAME(m_vol_env_disable)); + save_item(NAME(m_vol_env_mode)); + save_item(NAME(m_vol_env_spd)); + save_item(NAME(m_vol_env_clk)); + save_item(NAME(m_vol_env_out)); + save_item(NAME(m_wave_halt)); + save_item(NAME(m_env_halt)); + save_item(NAME(m_wave_freq)); + save_item(NAME(m_wave_acc)); + save_item(NAME(m_wave_addr)); + + save_item(NAME(m_mod_env_disable)); + save_item(NAME(m_mod_env_mode)); + save_item(NAME(m_mod_env_spd)); + save_item(NAME(m_mod_env_clk)); + save_item(NAME(m_mod_env_out)); + save_item(NAME(m_mod_halt)); + save_item(NAME(m_mod_freq)); + save_item(NAME(m_mod_table)); + save_item(NAME(m_mod_acc)); + save_item(NAME(m_mod_addr)); + save_item(NAME(m_mod_pos)); + + save_item(NAME(m_env_spd)); + save_item(NAME(m_wave_write)); + save_item(NAME(m_mvol)); + save_item(NAME(m_output)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void rp2c33_sound_device::device_reset() +{ +} + + +//------------------------------------------------- +// device_clock_changed - called if the clock +// changes +//------------------------------------------------- + +void rp2c33_sound_device::device_clock_changed() +{ + m_stream->set_sample_rate(clock()); +} + +//************************************************************************** +// READ/WRITE HANDLERS +//************************************************************************** + +u8 rp2c33_sound_device::read(offs_t offset) +{ + m_stream->update(); + + offset += 0x4080; + // TODO: open bus? + u8 ret = 0; + switch (offset) + { + case 0x4090: // volume envelope output + ret = (m_vol_env_out & 0x3f) | 0x40; + break; + case 0x4091: // bit 12-19 of accumulator + ret = (m_wave_addr << 4) + (m_wave_acc >> 12); + break; + case 0x4092: // modulator envelope output + ret = (m_mod_env_out & 0x3f) | 0x40; + break; + case 0x4093: // bit 5-11 of modtable address + ret = (m_mod_addr >> 5) & 0x7f; + break; + case 0x4094: // mod counter + gain result + break; + case 0x4095: // mod counter increment + ret = mod_inc[m_mod_table[(m_mod_addr >> 1) & 0x1f]] & 0xf; + // bit 4-7 : unknown counter + break; + case 0x4096: // wavetable value + ret = (m_wave[m_wave_addr & 0x3f] & 0x3f) | 0x40; + break; + case 0x4097: // modulator position + ret = m_mod_pos & 0x7f; + break; + } + return ret; +} + +void rp2c33_sound_device::write(offs_t offset, u8 data) +{ + m_regs[offset & 0xf] = data; + m_stream->update(); + + offset += 0x4080; + switch (offset) + { + case 0x4080: // volume envelope + m_vol_env_disable = BIT(data, 7); + m_vol_env_mode = BIT(data, 6); + m_vol_env_spd = data & 0x3f; + m_vol_env_clk = 0; + if (m_vol_env_disable) + m_vol_env_out = m_vol_env_spd; + break; + case 0x4082: // wave frequency low + m_wave_freq = (m_wave_freq & 0xf00) | (data & 0xff); + break; + case 0x4083: // wave frequency high + m_wave_halt = BIT(data, 7); + m_env_halt = BIT(data, 6); + m_wave_freq = (m_wave_freq & 0x0ff) | ((data & 0xf) << 8); + if (m_wave_halt) + m_wave_acc = 0; + if (m_env_halt) + m_vol_env_clk = m_mod_env_clk = 0; + break; + case 0x4084: // modulator envelope + m_mod_env_disable = BIT(data, 7); + m_mod_env_mode = BIT(data, 6); + m_mod_env_spd = data & 0x3f; + m_mod_env_clk = 0; + if (m_mod_env_disable) + m_mod_env_out = m_mod_env_spd; + break; + case 0x4085: // modulator position + m_mod_pos = data & 0x7f; + break; + case 0x4086: // modulator frequency low + m_mod_freq = (m_mod_freq & 0xf00) | (data & 0xff); + break; + case 0x4087: // modulator frequency high + m_mod_halt = BIT(data, 7); + // TODO: bit 6? + m_mod_freq = (m_mod_freq & 0x0ff) | ((data & 0xf) << 8); + if (m_mod_halt) + m_mod_acc = 0; + break; + case 0x4088: // modulator table + if (m_mod_halt) + { + m_mod_table[(m_mod_addr >> 1) & 0x1f] = data & 7; + m_mod_addr += 2; + } + break; + case 0x4089: // wave write, master volume + m_wave_write = BIT(data, 7); + m_mvol = data & 3; + break; + case 0x408a: // envelope speed + m_env_spd = data; + break; + } +} + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void rp2c33_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + for (int i = 0; i < samples; i++) + { + m_output = 0; + if (!m_env_halt && !m_wave_halt) + { + exec_vol_env(); + exec_mod_env(); + } + exec_mod(); + exec_wave(); + /* Update the buffers */ + outputs[0][i] = m_output * m_mvol_table[m_mvol]; + } +} + +//------------------------------------------------- +// exec_vol_env - execute volume envelope +//------------------------------------------------- + +inline void rp2c33_sound_device::exec_vol_env() +{ + if (!m_vol_env_disable) + { + const u32 period = ((m_vol_env_spd + 1) * (m_env_spd + 1)) << 3; // input clock / ((overall speed + 1) * (volume envelope speed + 1) * 8) + if (++m_vol_env_clk > period) + { + // clock the envelope + if (m_vol_env_mode) + { + if (m_vol_env_out < 32) ++m_vol_env_out; + } + else + { + if (m_vol_env_out > 0) --m_vol_env_out; + } + m_vol_env_clk = 0; + } + } +} + +//------------------------------------------------- +// exec_mod_env - execute modulator envelope +//------------------------------------------------- + +inline void rp2c33_sound_device::exec_mod_env() +{ + if (!m_mod_env_disable) + { + const u32 period = ((m_mod_env_spd + 1) * (m_env_spd + 1)) << 3; // input clock / ((overall speed + 1) * (modulator envelope speed + 1) * 8) + if (++m_mod_env_clk > period) + { + // clock the envelope + if (m_mod_env_mode) + { + if (m_mod_env_out < 32) ++m_mod_env_out; + } + else + { + if (m_mod_env_out > 0) --m_mod_env_out; + } + m_mod_env_clk = 0; + } + } +} + +//------------------------------------------------- +// exec_mod - execute modulator table +//------------------------------------------------- + +inline void rp2c33_sound_device::exec_mod() +{ + if (!m_mod_halt) + { + m_mod_acc += m_mod_freq; // input clock * frequency / 65536 + while (m_mod_acc > 0xffff) + { + int val = m_mod_table[((m_mod_addr++) >> 1) & 0x1f] & 7; + m_mod_acc -= (1 << 16); + if (val == 4) + m_mod_pos = 0; + else + m_mod_pos = (m_mod_pos + mod_inc[val]) & 0x7f; + } + } +} + +//------------------------------------------------- +// exec_wave - execute wavetable output +//------------------------------------------------- + +inline void rp2c33_sound_device::exec_wave() +{ + if (!m_wave_halt) + { + int mod = 0; + if (m_mod_env_out != 0) // skip if modulator off + { + // convert mod_pos to 7-bit signed + int pos = (m_mod_pos & 0x3f) - (m_mod_pos & 0x40); + + // multiply pos by gain, + // shift off 4 bits but with odd "rounding" behaviour + int temp = pos * m_mod_env_out; + int rem = temp & 0x0f; + temp >>= 4; + if ((rem > 0) && ((temp & 0x80) == 0)) + { + if (pos < 0) temp -= 1; + else temp += 2; + } + + // wrap if range is exceeded + while (temp >= 192) temp -= 256; + while (temp < -64) temp += 256; + + // multiply result by pitch, + // shift off 6 bits, round to nearest + temp = m_wave_freq * temp; + rem = temp & 0x3f; + temp >>= 6; + if (rem >= 32) temp += 1; + + mod = temp; + } + + // accumulate + m_wave_acc += std::max(0, m_wave_freq + mod); // input clock * (frequency + modulator output) / 65536 + m_wave_addr += m_wave_acc >> 16; + m_wave_acc &= 0xffff; + } + + // calculate output + if (!m_wave_write) + m_output = (m_wave[m_wave_addr & 0x3f] - 0x20) * std::min(32, m_vol_env_out); + +} diff --git a/src/devices/sound/rp2c33_snd.h b/src/devices/sound/rp2c33_snd.h new file mode 100644 index 00000000000..9c23c770a4b --- /dev/null +++ b/src/devices/sound/rp2c33_snd.h @@ -0,0 +1,105 @@ +// license:BSD-3-Clause +// copyright-holders:cam900, Brad Smith, Brezza +/*************************************************************************** + + Ricoh RP2C33 Sound emulation + +***************************************************************************/ + +#ifndef MAME_MACHINE_RP2C33_SND_H +#define MAME_MACHINE_RP2C33_SND_H + +#pragma once + +#include + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> rp2c33_sound_device + +class rp2c33_sound_device : public device_t, public device_sound_interface +{ +public: + static constexpr feature_type imperfect_features() { return feature::SOUND; } // one or more features are not verified, and possibly incorrect + + // construction/destruction + rp2c33_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); + + // host interface + void write(offs_t offset, u8 data); + u8 read(offs_t offset); + + // wavetable handlers + void wave_w(offs_t offset, u8 data) { if (m_wave_write) { m_stream->update(); m_wave[offset & 0x3f] = data & 0x3f; } } + u8 wave_r(offs_t offset) { return (m_wave[offset & 0x3f] & 0x3f) | 0x40; } // TODO: bit 6-7 is open bus? not allowed when wave is not halted? + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_clock_changed() override; + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + +private: + sound_stream *m_stream = nullptr; + + // sound states + u8 m_regs[16]; + u8 m_wave[64]; // 64 entries of wavetable, 6 bit unsigned + + bool m_vol_env_disable = true; // disable volume envelope + bool m_vol_env_mode = false; // volume envelope direction, increase or decrease + int m_vol_env_spd = 0; // volume envelope speed/gain + u32 m_vol_env_clk = 0; // volume envelope clock counter + int m_vol_env_out = 0; // output result of volume envelope + bool m_wave_halt = true; // halt wavetable + bool m_env_halt = true; // halt envelope + int m_wave_freq = 0; // wavetable frequency + u32 m_wave_acc = 0; // wavetable accumulator (.16 fixed point) + u8 m_wave_addr = 0; // wavetable address counter (.16 fixed point) + + u8 m_mod_table[32]; // 32 entries of modulator table, 3 bit unsigned + bool m_mod_env_disable = true; // disable modulator envelope + bool m_mod_env_mode = false; // modulator envelope direction, increase or decrease + int m_mod_env_spd = 0; // modulator envelope speed/gain + u32 m_mod_env_clk = 0; // modulator envelope clock counter + int m_mod_env_out = 0; // output result of modulator envelope + bool m_mod_halt = true; // halt modulator + int m_mod_freq = 0; // modulator frequency + u32 m_mod_acc = 0; // modulator accumulator (.16 fixed point) + u8 m_mod_addr = 0; // modulator table address counter (.16 fixed point) + int m_mod_pos = 0; // modulator position, 7 bit signed + + int m_env_spd = 0; // overall envelope speed + bool m_wave_write = true; // play sound or write wavetable + int m_mvol = 0; // master volume + int m_output = 0; // output result + int m_mvol_table[4]; // master volume table + + const int mod_inc[8] = { 0, 1, 2, 4, -4, -4, -2, -1 }; + + // inlines + inline void exec_vol_env(); + inline void exec_mod_env(); + + inline void exec_mod(); + inline void exec_wave(); +}; + + +// device type definition +DECLARE_DEVICE_TYPE(RP2C33_SOUND, rp2c33_sound_device) + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + + +#endif // MAME_MACHINE_RP2C33_SND_H