From 3204234f8d7edce4a3373be3895d6852f3480ff8 Mon Sep 17 00:00:00 2001 From: cam900 Date: Fri, 24 Aug 2018 10:47:41 +0900 Subject: [PATCH] Add huc6230 Emulation (#3829) * Add huc6230 Emulation huc6272.cpp : Add ADPCM transfer, Add save states PC-FXGA for PC-9801 C Bus is released in December 1995 in Japan, Correct metadata * huc6272.cpp : Fix ADPCM address * huc6230.cpp : Simpler interpolate * huc6230.cpp : Fix clamp huc6272.cpp : Fix ADPCM nibble * huc6272.cpp : Fix data type * Revert pcfxga year; PC-FXGA for PC9801 C-bus is not dumped? --- scripts/src/sound.lua | 14 +++ scripts/target/mame/arcade.lua | 1 + scripts/target/mame/mess.lua | 1 + src/devices/sound/huc6230.cpp | 173 +++++++++++++++++++++++++++++++ src/devices/sound/huc6230.h | 56 ++++++++++ src/devices/video/huc6272.cpp | 180 +++++++++++++++++++++++++++++++++ src/devices/video/huc6272.h | 22 ++++ src/mame/drivers/pcfx.cpp | 14 ++- 8 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 src/devices/sound/huc6230.cpp create mode 100644 src/devices/sound/huc6230.h diff --git a/scripts/src/sound.lua b/scripts/src/sound.lua index f8e7f0f20a1..db5930d3801 100644 --- a/scripts/src/sound.lua +++ b/scripts/src/sound.lua @@ -381,6 +381,20 @@ end +--------------------------------------------------- +-- Hudsonsoft HuC6230 SoundBox +--@src/devices/sound/huc6230.h,SOUNDS["HUC6230"] = true +--------------------------------------------------- + +if (SOUNDS["HUC6230"]~=null) then + files { + MAME_DIR .. "src/devices/sound/huc6230.cpp", + MAME_DIR .. "src/devices/sound/huc6230.h", + } +end + + + --------------------------------------------------- -- Hudsonsoft C6280 sound chip --@src/devices/sound/c6280.h,SOUNDS["C6280"] = true diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 6d034dd8d49..f005c0edcf6 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -213,6 +213,7 @@ SOUNDS["ES5506"] = true SOUNDS["BSMT2000"] = true SOUNDS["GAELCO_CG1V"] = true SOUNDS["GAELCO_GAE1"] = true +--SOUNDS["HUC6230"] = true SOUNDS["C6280"] = true SOUNDS["SP0250"] = true SOUNDS["SPU"] = true diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 50d58fc1fef..8af94ec43d1 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -219,6 +219,7 @@ SOUNDS["ES5506"] = true --SOUNDS["BSMT2000"] = true --SOUNDS["GAELCO_CG1V"] = true --SOUNDS["GAELCO_GAE1"] = true +SOUNDS["HUC6230"] = true SOUNDS["C6280"] = true SOUNDS["SP0250"] = true SOUNDS["SPU"] = true diff --git a/src/devices/sound/huc6230.cpp b/src/devices/sound/huc6230.cpp new file mode 100644 index 00000000000..f762b2ad7f2 --- /dev/null +++ b/src/devices/sound/huc6230.cpp @@ -0,0 +1,173 @@ +// license:BSD-3-Clause +// copyright-holders:cam900 +/* + Hudson HuC6230 SoundBox + HuC6280 PSG with ADPCM + + TODO: + - Volume is linear? + - Make it actually working + - Implement CDDA Volume +*/ + +#include "emu.h" +#include "huc6230.h" + +static const int clamp(int val, int min, int max) { return std::min(max,std::max(min,val)); } + + +void huc6230_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + /* Clear buffer */ + + int frq = (1 << m_adpcm_freq); + for (int i = 0; i < samples; i++) + { + outputs[0][i] = inputs[0][i]; + outputs[1][i] = inputs[1][i]; + + int cdda_l = inputs[2][i]; + int cdda_r = inputs[3][i]; + outputs[0][i] = clamp(outputs[0][i] + ((cdda_l * m_cdda_lvol) >> 6), -32768, 32767); + outputs[1][i] = clamp(outputs[1][i] + ((cdda_r * m_cdda_rvol) >> 6), -32768, 32767); + + for (int adpcm = 0; adpcm < 2; adpcm++) + { + adpcm_channel *channel = &m_adpcm_channel[adpcm]; + + if (!channel->m_playing) + continue; + + int32_t sample; + + channel->m_pos++; + channel->m_input = m_adpcm_update_cb[adpcm](); + + if (channel->m_pos > frq) + { + channel->m_pos = 0; + channel->m_prev_sample = channel->m_curr_sample; + channel->m_curr_sample = channel->m_adpcm.clock(channel->m_input & 0xf); + } + if (!channel->m_interpolate) + sample = channel->m_curr_sample; + else + sample = ((channel->m_prev_sample * (frq - channel->m_pos)) + + (channel->m_curr_sample * channel->m_pos)) >> m_adpcm_freq; + + outputs[0][i] = clamp(outputs[0][i] + ((sample * channel->m_lvol) >> 2), -32768, 32767); + outputs[1][i] = clamp(outputs[1][i] + ((sample * channel->m_rvol) >> 2), -32768, 32767); + } + } +} + + +/*--------------------------------------------------------------------------*/ +/* MAME specific code */ +/*--------------------------------------------------------------------------*/ + +WRITE8_MEMBER( huc6230_device::write ) +{ + /* Update stream */ + m_stream->update(); + if (offset < 0x10) + { + m_psg->c6280_w(space, offset, data, mem_mask); + } + else if (offset < 0x11) + { + m_adpcm_freq = data & 3; + for (int i = 0; i < 2; i++) + { + m_adpcm_channel[i].m_interpolate = BIT(data, 2+i); + if (BIT(data, 4+i)) + { + m_adpcm_channel[i].m_adpcm.reset(); + m_adpcm_channel[i].m_playing = 0; + m_adpcm_channel[i].m_prev_sample = 0; + m_adpcm_channel[i].m_curr_sample = 0; + m_adpcm_channel[i].m_pos = 0; + } + else + { + m_adpcm_channel[i].m_playing = 1; + } + } + } + else if (offset < 0x15) + { + offset -= 0x11; + if ((offset & 1) == 0) + m_adpcm_channel[offset >> 1].m_lvol = data & 0x3f; + else + m_adpcm_channel[offset >> 1].m_rvol = data & 0x3f; + } + else if (offset < 0x16) + m_cdda_lvol = data & 0x3f; + else if (offset < 0x17) + m_cdda_rvol = data & 0x3f; +} + +DEFINE_DEVICE_TYPE(HuC6230, huc6230_device, "huc6230", "Hudson Soft HuC6230 SoundBox") + +huc6230_device::huc6230_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, HuC6230, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_stream(nullptr) + , m_psg(*this, "psg") + , m_adpcm_freq(0) + , m_cdda_lvol(0) + , m_cdda_rvol(0) + , m_adpcm_update_cb{{*this}, {*this}} +{ +} + +//------------------------------------------------- +// device_add_mconfig - add machine configuration +//------------------------------------------------- +void huc6230_device::device_add_mconfig(machine_config &config) +{ + C6280(config, m_psg, DERIVED_CLOCK(1,6)); + m_psg->add_route(0, DEVICE_SELF, 1.0, 0); + m_psg->add_route(1, DEVICE_SELF, 1.0, 1); +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void huc6230_device::device_start() +{ + for (auto &cb : m_adpcm_update_cb) + cb.resolve_safe(0); + + m_stream = machine().sound().stream_alloc(*this, 4, 2, clock() / 682); // or /672? + + for (int i = 0; i < 2; i++) + { + m_adpcm_channel[i].m_playing = 0; + m_adpcm_channel[i].m_prev_sample = 0; + m_adpcm_channel[i].m_curr_sample = 0; + m_adpcm_channel[i].m_pos = 0; + m_adpcm_channel[i].m_input = 0; + + save_item(NAME(m_adpcm_channel[i].m_adpcm.m_signal), i); + save_item(NAME(m_adpcm_channel[i].m_adpcm.m_step), i); + save_item(NAME(m_adpcm_channel[i].m_lvol), i); + save_item(NAME(m_adpcm_channel[i].m_rvol), i); + save_item(NAME(m_adpcm_channel[i].m_interpolate), i); + save_item(NAME(m_adpcm_channel[i].m_playing), i); + save_item(NAME(m_adpcm_channel[i].m_prev_sample), i); + save_item(NAME(m_adpcm_channel[i].m_curr_sample), i); + save_item(NAME(m_adpcm_channel[i].m_pos), i); + save_item(NAME(m_adpcm_channel[i].m_input), i); + } + save_item(NAME(m_adpcm_freq)); + save_item(NAME(m_cdda_lvol)); + save_item(NAME(m_cdda_rvol)); +} + +void huc6230_device::device_clock_changed() +{ + m_stream->set_sample_rate(clock() / 682); +} diff --git a/src/devices/sound/huc6230.h b/src/devices/sound/huc6230.h new file mode 100644 index 00000000000..632dbee80de --- /dev/null +++ b/src/devices/sound/huc6230.h @@ -0,0 +1,56 @@ +// license:BSD-3-Clause +// copyright-holders:cam900 +#ifndef MAME_SOUND_HUC6230_H +#define MAME_SOUND_HUC6230_H + +#pragma once + +#include "sound/okiadpcm.h" +#include "sound/c6280.h" + +class huc6230_device : public device_t, public device_sound_interface +{ +public: + huc6230_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + template auto adpcm_update_cb() { return m_adpcm_update_cb[N].bind(); } + + // write only + DECLARE_WRITE8_MEMBER( write ); + +protected: + // device-level overrides + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_start() 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: + struct adpcm_channel { + oki_adpcm_state m_adpcm; + uint8_t m_lvol; + uint8_t m_rvol; + uint8_t m_interpolate; + uint8_t m_playing; + int32_t m_prev_sample; + int32_t m_curr_sample; + uint32_t m_pos; + uint8_t m_input; + }; + + // internal state + sound_stream *m_stream; + required_device m_psg; + adpcm_channel m_adpcm_channel[2]; + uint32_t m_adpcm_freq; + uint32_t m_cdda_lvol; + uint32_t m_cdda_rvol; + + devcb_read8 m_adpcm_update_cb[2]; +}; + +DECLARE_DEVICE_TYPE(HuC6230, huc6230_device) + +#endif // MAME_SOUND_HUC6230_H diff --git a/src/devices/video/huc6272.cpp b/src/devices/video/huc6272.cpp index 3755a1d870d..264e896bbd3 100644 --- a/src/devices/video/huc6272.cpp +++ b/src/devices/video/huc6272.cpp @@ -6,6 +6,7 @@ TODO: - Use NSCSI instead of legacy one! + - ADPCM Transfer is correct? ***************************************************************************/ @@ -77,6 +78,51 @@ void huc6272_device::device_validity_check(validity_checker &valid) const void huc6272_device::device_start() { m_irq_changed_cb.resolve_safe(); + + save_item(NAME(m_register)); + save_item(NAME(m_kram_addr_r)); + save_item(NAME(m_kram_inc_r)); + save_item(NAME(m_kram_page_r)); + save_item(NAME(m_kram_addr_w)); + save_item(NAME(m_kram_inc_w)); + save_item(NAME(m_kram_page_w)); + save_item(NAME(m_page_setting)); + + for (int bg = 0; bg < 4; bg++) + { + save_item(NAME(m_bg[bg].bat_address), bg); + save_item(NAME(m_bg[bg].cg_address), bg); + save_item(NAME(m_bg[bg].mode), bg); + save_item(NAME(m_bg[bg].height), bg); + save_item(NAME(m_bg[bg].width), bg); + save_item(NAME(m_bg[bg].xscroll), bg); + save_item(NAME(m_bg[bg].yscroll), bg); + save_item(NAME(m_bg[bg].priority), bg); + } + + save_item(NAME(m_bg0sub.bat_address)); + save_item(NAME(m_bg0sub.cg_address)); + save_item(NAME(m_bg0sub.height)); + save_item(NAME(m_bg0sub.width)); + + save_item(NAME(m_micro_prg.index)); + save_item(NAME(m_micro_prg.ctrl)); + + save_item(NAME(m_adpcm.rate)); + save_item(NAME(m_adpcm.status)); + save_item(NAME(m_adpcm.interrupt)); + for (int adpcm = 0; adpcm < 2; adpcm++) + { + save_item(NAME(m_adpcm.playing[adpcm]), adpcm); + save_item(NAME(m_adpcm.control[adpcm]), adpcm); + save_item(NAME(m_adpcm.start[adpcm]), adpcm); + save_item(NAME(m_adpcm.end[adpcm]), adpcm); + save_item(NAME(m_adpcm.imm[adpcm]), adpcm); + save_item(NAME(m_adpcm.input[adpcm]), adpcm); + save_item(NAME(m_adpcm.nibble[adpcm]), adpcm); + save_item(NAME(m_adpcm.pos[adpcm]), adpcm); + save_item(NAME(m_adpcm.addr[adpcm]), adpcm); + } } @@ -156,6 +202,7 @@ READ32_MEMBER( huc6272_device::read ) ---- ---- ---- ---- ---- ---- -xxx xxxx register read-back */ res = m_register & 0x7f; + res |= (m_adpcm.interrupt << 10); res |= (m_scsi_ctrl_in->read() & 0xff) << 16; } else @@ -191,6 +238,14 @@ READ32_MEMBER( huc6272_device::read ) case 0x0f: res = m_page_setting; break; + + case 0x53: // ADPCM status + res = m_adpcm.status; + + m_adpcm.status = 0; + m_adpcm.interrupt = 0; + interrupt_update(); + break; //default: printf("%04x\n",m_register); } } @@ -370,11 +425,136 @@ WRITE32_MEMBER( huc6272_device::write ) break; } + case 0x50: // ADPCM control + { + for (int i = 0; i < 2; i++) + { + m_adpcm.playing[i] = BIT(data, i); + if (!m_adpcm.playing[i]) + { + m_adpcm.input[i] = -1; + m_adpcm.pos[i] = 0; + } + else + { + m_adpcm.addr[i] = m_adpcm.start[i]; + } + } + + m_adpcm.rate = (data & 0xc) >> 2; + break; + } + + // ADPCM channel control + case 0x51: + case 0x52: + { + uint8_t reg_offs = 1-(m_register & 1); + m_adpcm.control[reg_offs] = data & 0x7; + if (BIT(m_adpcm.control[reg_offs], 1) == 0) + m_adpcm.status &= ~(1 << (2*reg_offs)); + + if (BIT(m_adpcm.control[reg_offs], 2) == 0) + m_adpcm.status &= ~(1 << (2*reg_offs+1)); + + break; + } + + // ADPCM start address + case 0x58: + case 0x5c: + m_adpcm.start[(m_register >> 2) & 1] = (data << 8) & 0x3ffff; + break; + + // ADPCM end address + case 0x59: + case 0x5d: + m_adpcm.end[(m_register >> 2) & 1] = data & 0x3ffff; + break; + + // ADPCM intermediate address + case 0x5a: + case 0x5e: + m_adpcm.imm[(m_register >> 2) & 1] = (data << 6) & 0x3ffff; + break; + //default: printf("%04x %04x %08x\n",m_register,data,mem_mask); } } } +uint8_t huc6272_device::adpcm_update(int chan) +{ + if (!m_adpcm.playing[chan]) + return 0; + + int rate = (1 << m_adpcm.rate); + m_adpcm.pos[chan]++; + if (m_adpcm.pos[chan] > rate) + { + if (m_adpcm.input[chan] == -1) + { + m_adpcm.input[chan] = read_dword(((m_page_setting & 0x1000) << 6) | m_adpcm.addr[chan]); + m_adpcm.addr[chan] = (m_adpcm.addr[chan] & 0x20000) | ((m_adpcm.addr[chan] + 1) & 0x1ffff); + if (m_adpcm.addr[chan] == m_adpcm.imm[chan]) + { + m_adpcm.status |= (1 << (chan*2+1)); + if (BIT(m_adpcm.control[chan], 2)) + { + m_adpcm.interrupt = 1; + interrupt_update(); + } + } + if (m_adpcm.addr[chan] > m_adpcm.end[chan]) + { + m_adpcm.status |= (1 << (chan*2)); + if (BIT(m_adpcm.control[chan], 1)) + { + m_adpcm.interrupt = 1; + interrupt_update(); + } + + if (BIT(m_adpcm.control[chan],0)) // Ring Buffer + { + m_adpcm.addr[chan] = m_adpcm.start[chan]; + } + else + { + m_adpcm.playing[chan] = 0; + return 0; + } + } + m_adpcm.nibble[chan] = 0; + } + else + { + m_adpcm.nibble[chan] += 4; + if (m_adpcm.nibble[chan] >= 28) + m_adpcm.input[chan] = -1; + } + } + + return (m_adpcm.input[chan] >> m_adpcm.nibble[chan]) & 0xf; +} + +READ8_MEMBER(huc6272_device::adpcm_update_0) +{ + return adpcm_update(0); +} + +READ8_MEMBER(huc6272_device::adpcm_update_1) +{ + return adpcm_update(1); +} + +void huc6272_device::interrupt_update() +{ + if (m_adpcm.interrupt) + m_irq_changed_cb(ASSERT_LINE); + else + m_irq_changed_cb(CLEAR_LINE); +} + //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- diff --git a/src/devices/video/huc6272.h b/src/devices/video/huc6272.h index 474c424373b..a20b94e530e 100644 --- a/src/devices/video/huc6272.h +++ b/src/devices/video/huc6272.h @@ -47,6 +47,10 @@ public: DECLARE_WRITE32_MEMBER( write ); DECLARE_READ32_MEMBER( read ); + // ADPCM operations + DECLARE_READ8_MEMBER( adpcm_update_0 ); + DECLARE_READ8_MEMBER( adpcm_update_1 ); + protected: // device-level overrides virtual void device_validity_check(validity_checker &valid) const override; @@ -87,6 +91,21 @@ private: uint8_t ctrl; }m_micro_prg; + struct{ + uint8_t rate; + uint32_t status; + int interrupt; + uint8_t playing[2]; + uint8_t control[2]; + uint32_t start[2]; + uint32_t end[2]; + uint32_t imm[2]; + uint32_t input[2]; + int nibble[2]; + uint32_t pos[2]; + uint32_t addr[2]; + }m_adpcm; + const address_space_config m_program_space_config; const address_space_config m_data_space_config; required_shared_ptr m_microprg_ram; @@ -104,6 +123,9 @@ private: void write_dword(offs_t address, uint32_t data); void write_microprg_data(offs_t address, uint16_t data); + uint8_t adpcm_update(int chan); + void interrupt_update(); + void kram_map(address_map &map); void microprg_map(address_map &map); }; diff --git a/src/mame/drivers/pcfx.cpp b/src/mame/drivers/pcfx.cpp index 53ea9668e5e..5a38480311f 100644 --- a/src/mame/drivers/pcfx.cpp +++ b/src/mame/drivers/pcfx.cpp @@ -11,11 +11,13 @@ #include "emu.h" #include "cpu/v810/v810.h" +#include "sound/huc6230.h" #include "video/huc6261.h" #include "video/huc6270.h" #include "video/huc6271.h" #include "video/huc6272.h" #include "screen.h" +#include "speaker.h" class pcfx_state : public driver_device { @@ -189,7 +191,7 @@ WRITE16_MEMBER( pcfx_state::pad_w ) void pcfx_state::pcfx_io(address_map &map) { map(0x00000000, 0x000000FF).rw(FUNC(pcfx_state::pad_r), FUNC(pcfx_state::pad_w)); /* PAD */ - map(0x00000100, 0x000001FF).noprw(); /* HuC6230 */ + map(0x00000100, 0x000001FF).w("huc6230", FUNC(huc6230_device::write)).umask32(0x00ff00ff); /* HuC6230 */ map(0x00000200, 0x000002FF).m("huc6271", FUNC(huc6271_device::regs)).umask32(0x0000ffff); /* HuC6271 */ map(0x00000300, 0x000003FF).rw(m_huc6261, FUNC(huc6261_device::read), FUNC(huc6261_device::write)).umask32(0x0000ffff); /* HuC6261 */ map(0x00000400, 0x000004FF).rw("huc6270_a", FUNC(huc6270_device::read), FUNC(huc6270_device::write)).umask32(0x0000ffff); /* HuC6270-A */ @@ -444,6 +446,16 @@ MACHINE_CONFIG_START(pcfx_state::pcfx) MCFG_DEVICE_ADD( "huc6271", HUC6271, XTAL(21'477'272) ) MCFG_SOFTWARE_LIST_ADD("cd_list", "pcfx") + + /* sound hardware */ + SPEAKER(config, "lspeaker").front_left(); + SPEAKER(config, "rspeaker").front_right(); + + huc6230_device &huc6230(HuC6230(config, "huc6230", XTAL(21'477'272))); + huc6230.adpcm_update_cb<0>().set("huc6272", FUNC(huc6272_device::adpcm_update_0)); + huc6230.adpcm_update_cb<1>().set("huc6272", FUNC(huc6272_device::adpcm_update_1)); + huc6230.add_route(0, "lspeaker", 1.0); + huc6230.add_route(1, "rspeaker", 1.0); MACHINE_CONFIG_END