furnace/extern/vgsound_emu-modified/vgsound_emu/src/es550x/es5505.hpp

312 lines
7.1 KiB
C++

/*
License: Zlib
see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details
Copyright holder(s): cam900
Ensoniq ES5504 emulation core
*/
#ifndef _VGSOUND_EMU_SRC_ES5505_HPP
#define _VGSOUND_EMU_SRC_ES5505_HPP
#pragma once
#include "es550x.hpp"
// ES5505 specific
class es5505_core : public es550x_shared_core
{
private:
class output_t : public vgsound_emu_core
{
public:
output_t(s32 left = 0, s32 right = 0)
: vgsound_emu_core("es5505_output")
, m_left(left)
, m_right(right)
{
}
void reset()
{
m_left = 0;
m_right = 0;
}
inline void copy_output(output_t &src)
{
m_left = src.left();
m_right = src.right();
}
inline s32 clamp16(s32 in) { return clamp(in, -0x8000, 0x7fff); }
inline void clamp16(output_t &src)
{
m_left = clamp16(src.left());
m_right = clamp16(src.right());
}
inline void clamp16()
{
m_left = clamp16(m_left);
m_right = clamp16(m_right);
}
// setters
inline void set_left(s32 left) { m_left = left; }
inline void set_right(s32 right) { m_right = right; }
inline void serial_in(bool ch, u8 in)
{
if (ch) // Right output
{
m_right = (m_right << 1) | (in ? 1 : 0);
}
else // Left output
{
m_left = (m_left << 1) | (in ? 1 : 0);
}
}
// getters
inline s32 left() { return m_left; }
inline s32 right() { return m_right; }
output_t &operator+=(output_t &src)
{
m_left = clamp16(m_left + src.left());
m_right = clamp16(m_right + src.right());
return *this;
}
output_t &operator>>(s32 shift)
{
m_left >>= shift;
m_right >>= shift;
return *this;
}
private:
s32 m_left = 0;
s32 m_right = 0;
};
// es5505 voice classes
class voice_t : public es550x_voice_t
{
public:
// constructor
voice_t(es5505_core &host)
: es550x_voice_t("es5505_voice", 20, 9, false)
, m_host(host)
, m_lvol(0)
, m_rvol(0)
, m_ch(output_t())
, m_mute(false)
, m_output{0}
{
}
// internal state
virtual void reset() override;
virtual void fetch(u8 voice, u8 cycle) override;
virtual void tick(u8 voice) override;
// setters
inline void set_lvol(u8 lvol) { m_lvol = lvol; }
inline void set_rvol(u8 rvol) { m_rvol = rvol; }
// getters
inline u8 lvol() { return m_lvol; }
inline u8 rvol() { return m_rvol; }
output_t &ch() { return m_ch; }
// for debug/preview only
inline void set_mute(bool mute) { m_mute = mute; }
inline s32 left_out() { return m_mute ? 0 : m_output[0]; }
inline s32 right_out() { return m_mute ? 0 : m_output[1]; }
private:
s32 volume_calc(u8 volume, s32 in);
// registers
es5505_core &m_host;
u8 m_lvol = 0; // Left volume
u8 m_rvol = 0; // Right volume
output_t m_ch; // channel output
bool m_mute = false; // mute flag (for debug purpose)
std::array<s32, 2> m_output; // output preview (for debug purpose)
};
class sermode_t : public vgsound_emu_core
{
public:
sermode_t()
: vgsound_emu_core("es5505_sermode")
, m_adc(0)
, m_test(0)
, m_sony_bb(0)
, m_msb(0)
{
}
void reset()
{
m_adc = 0;
m_test = 0;
m_sony_bb = 0;
m_msb = 0;
}
// setters
void write(u16 data)
{
m_adc = (data >> 0) & 1;
m_test = (data >> 1) & 1;
m_sony_bb = (data >> 2) & 1;
m_msb = (data >> 11) & 0x1f;
}
void set_adc(bool adc) { m_adc = adc ? 1 : 0; }
void set_test(bool test) { m_test = test ? 1 : 0; }
void set_sony_bb(bool sony_bb) { m_sony_bb = sony_bb ? 1 : 0; }
void set_msb(u8 msb) { m_msb = msb & 0x1f; }
// getters
bool adc() { return m_adc; }
bool test() { return m_test; }
bool sony_bb() { return m_sony_bb; }
u8 msb() { return m_msb; }
private:
u8 m_adc : 1; // A/D
u8 m_test : 1; // Test
u8 m_sony_bb : 1; // Sony/BB format serial output
u8 m_msb : 5; // Serial output MSB
};
public:
// constructor
es5505_core(es550x_intf &intf)
: es550x_shared_core("es5505", 32, intf)
, m_voice{*this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this,
*this, *this, *this, *this, *this, *this, *this, *this, *this, *this, *this,
*this, *this, *this, *this, *this, *this, *this, *this, *this, *this}
, m_sermode(sermode_t())
, m_bclk(clock_pulse_t<s8>(4, 0))
, m_lrclk(clock_pulse_t<s8>(16, 1))
, m_wclk(0)
, m_wclk_lr(false)
, m_output_bit(0)
, m_ch{output_t()}
, m_output{output_t()}
, m_output_temp{output_t()}
, m_output_latch{output_t()}
{
}
// host interface
u16 host_r(u8 address);
void host_w(u8 address, u16 data);
// internal state
virtual void reset() override;
virtual void tick() override;
// less cycle accurate, but also less cpu heavy update routine
void tick_perf();
// clock outputs
inline bool bclk() { return m_bclk.current_edge(); }
inline bool bclk_rising_edge() { return m_bclk.rising_edge(); }
inline bool bclk_falling_edge() { return m_bclk.falling_edge(); }
// Input mode for Channel 3
inline void lin(s32 in)
{
if (m_sermode.adc())
{
m_ch[3].set_left(in);
}
}
inline void rin(s32 in)
{
if (m_sermode.adc())
{
m_ch[3].set_right(in);
}
}
// 4 stereo output channels
inline s32 lout(u8 ch) { return m_ch[ch & 0x3].left(); }
inline s32 rout(u8 ch) { return m_ch[ch & 0x3].right(); }
//-----------------------------------------------------------------
//
// for preview/debug purpose only, not for serious emulators
//
//-----------------------------------------------------------------
// bypass chips host interface for debug purpose only
u16 read(u8 address, bool cpu_access = false);
void write(u8 address, u16 data);
u16 regs_r(u8 page, u8 address, bool cpu_access = false);
void regs_w(u8 page, u8 address, u16 data);
u16 regs_r(u8 page, u8 address)
{
u8 prev = m_page;
m_page = page;
u16 ret = read(address, false);
m_page = prev;
return ret;
}
inline void set_mute(u8 ch, bool mute) { m_voice[ch & 0x1f].set_mute(mute); }
// per-voice outputs
inline s32 voice_lout(u8 voice) { return (voice < 32) ? m_voice[voice].left_out() : 0; }
inline s32 voice_rout(u8 voice) { return (voice < 32) ? m_voice[voice].right_out() : 0; }
protected:
virtual inline u8 max_voices() override { return 32; }
virtual void voice_tick() override;
private:
std::array<voice_t, 32> m_voice; // 32 voices
// Serial related stuffs
sermode_t m_sermode; // Serial mode register
clock_pulse_t<s8> m_bclk; // BCLK clock (CLKIN / 4), freely running clock
clock_pulse_t<s8> m_lrclk; // LRCLK
s16 m_wclk = 0; // WCLK
bool m_wclk_lr = false; // WCLK, L/R output select
s8 m_output_bit = 0; // Bit position in output
std::array<output_t, 4> m_ch; // 4 stereo output channels
std::array<output_t, 4> m_output; // Serial outputs
std::array<output_t, 4> m_output_temp; // temporary signal for serial output
std::array<output_t, 4> m_output_latch; // output latch
};
#endif