diff options
author | cam900 <dbtlrchl@naver.com> | 2019-10-30 01:25:36 +0900 |
---|---|---|
committer | cam900 <dbtlrchl@naver.com> | 2019-10-30 01:25:36 +0900 |
commit | 42f3c173f98b501f410a5f5fa47d7ce40b3451be (patch) | |
tree | 38e99311b436cfd858ca3676c14c98c30a28d89d /src/devices/sound | |
parent | 3151dbd31b1eebce1bce6f8780e3afe90def0cfd (diff) |
es5506.cpp : Add/Implement ES5505 difference, Fix spacing, Various updates
Reduce duplicates, Unnecessary lines, #define macros, Implement reset behavior(RESB in ES5506), Add notes, Fix filter behaviors, Sync to official documents
references : http://zine.r-massive.com/ensoniq-technical-documents-and-schematics/
Diffstat (limited to 'src/devices/sound')
-rw-r--r-- | src/devices/sound/es5506.cpp | 1601 | ||||
-rw-r--r-- | src/devices/sound/es5506.h | 182 |
2 files changed, 907 insertions, 876 deletions
diff --git a/src/devices/sound/es5506.cpp b/src/devices/sound/es5506.cpp index bc690058fe7..6f2b65c1796 100644 --- a/src/devices/sound/es5506.cpp +++ b/src/devices/sound/es5506.cpp @@ -101,63 +101,69 @@ Ensoniq OTIS - ES5505 Ensoniq OTTO - #define RAINE_CHECK 0 - -#define MAX_SAMPLE_CHUNK 10000 -#define ULAW_MAXBITS 8 - -#define CONTROL_BS1 0x8000 -#define CONTROL_BS0 0x4000 -#define CONTROL_CMPD 0x2000 -#define CONTROL_CA2 0x1000 -#define CONTROL_CA1 0x0800 -#define CONTROL_CA0 0x0400 -#define CONTROL_LP4 0x0200 -#define CONTROL_LP3 0x0100 -#define CONTROL_IRQ 0x0080 -#define CONTROL_DIR 0x0040 -#define CONTROL_IRQE 0x0020 -#define CONTROL_BLE 0x0010 -#define CONTROL_LPE 0x0008 -#define CONTROL_LEI 0x0004 -#define CONTROL_STOP1 0x0002 -#define CONTROL_STOP0 0x0001 - -#define CONTROL_BSMASK (CONTROL_BS1 | CONTROL_BS0) -#define CONTROL_CAMASK (CONTROL_CA2 | CONTROL_CA1 | CONTROL_CA0) -#define CONTROL_LPMASK (CONTROL_LP4 | CONTROL_LP3) -#define CONTROL_LOOPMASK (CONTROL_BLE | CONTROL_LPE) -#define CONTROL_STOPMASK (CONTROL_STOP1 | CONTROL_STOP0) - - -es550x_device::es550x_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), - m_stream(nullptr), - m_sample_rate(0), - m_write_latch(0), - m_read_latch(0), - m_master_clock(0), - m_current_page(0), - m_active_voices(0), - m_mode(0), - m_wst(0), - m_wend(0), - m_lrend(0), - m_irqv(0), - m_scratch(nullptr), - m_ulaw_lookup(nullptr), - m_volume_lookup(nullptr), +static constexpr unsigned FINE_FILTER_BIT = 16; +static constexpr unsigned FILTER_BIT = 12; +static constexpr unsigned FILTER_SHIFT = FINE_FILTER_BIT - FILTER_BIT; + +static constexpr unsigned MAX_SAMPLE_CHUNK = 10000; +static constexpr unsigned ULAW_MAXBITS = 8; + +static constexpr unsigned CONTROL_BS1 = 0x8000; +static constexpr unsigned CONTROL_BS0 = 0x4000; +static constexpr unsigned CONTROL_CMPD = 0x2000; +static constexpr unsigned CONTROL_CA2 = 0x1000; +static constexpr unsigned CONTROL_CA1 = 0x0800; +static constexpr unsigned CONTROL_CA0 = 0x0400; +static constexpr unsigned CONTROL_LP4 = 0x0200; +static constexpr unsigned CONTROL_LP3 = 0x0100; +static constexpr unsigned CONTROL_IRQ = 0x0080; +static constexpr unsigned CONTROL_DIR = 0x0040; +static constexpr unsigned CONTROL_IRQE = 0x0020; +static constexpr unsigned CONTROL_BLE = 0x0010; +static constexpr unsigned CONTROL_LPE = 0x0008; +static constexpr unsigned CONTROL_LEI = 0x0004; +static constexpr unsigned CONTROL_STOP1 = 0x0002; +static constexpr unsigned CONTROL_STOP0 = 0x0001; + +static constexpr unsigned CONTROL_BSMASK = (CONTROL_BS1 | CONTROL_BS0); +static constexpr unsigned CONTROL_CAMASK = (CONTROL_CA2 | CONTROL_CA1 | CONTROL_CA0); +static constexpr unsigned CONTROL_LPMASK = (CONTROL_LP4 | CONTROL_LP3); +static constexpr unsigned CONTROL_LOOPMASK = (CONTROL_BLE | CONTROL_LPE); +static constexpr unsigned CONTROL_STOPMASK = (CONTROL_STOP1 | CONTROL_STOP0); + +// ES5505 has sightly different control bit +static constexpr unsigned CONTROL_5505_LP4 = 0x0800; +static constexpr unsigned CONTROL_5505_LP3 = 0x0400; +static constexpr unsigned CONTROL_5505_CA1 = 0x0200; +static constexpr unsigned CONTROL_5505_CA0 = 0x0100; + +static constexpr unsigned CONTROL_5505_LPMASK = (CONTROL_5505_LP4 | CONTROL_5505_LP3); +static constexpr unsigned CONTROL_5505_CAMASK = (CONTROL_5505_CA1 | CONTROL_5505_CA0); + +es550x_device::es550x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, type, tag, owner, clock) + , device_sound_interface(mconfig, *this) + , m_stream(nullptr) + , m_sample_rate(0) + , m_master_clock(0) + , m_current_page(0) + , m_active_voices(0) + , m_mode(0) + , m_irqv(0) + , m_scratch(nullptr) + , m_ulaw_lookup(nullptr) + , m_volume_lookup(nullptr) #if ES5506_MAKE_WAVS - m_wavraw(nullptr), + , m_wavraw(nullptr) #endif - m_region0(nullptr), - m_region1(nullptr), - m_region2(nullptr), - m_region3(nullptr), - m_channels(0), - m_irq_cb(*this), - m_read_port_cb(*this), - m_sample_rate_changed_cb(*this) + , m_region0(nullptr) + , m_region1(nullptr) + , m_region2(nullptr) + , m_region3(nullptr) + , m_channels(0) + , m_irq_cb(*this) + , m_read_port_cb(*this) + , m_sample_rate_changed_cb(*this) { for (auto & elem : m_region_base) { @@ -167,8 +173,13 @@ es550x_device::es550x_device(const machine_config &mconfig, device_type type, co DEFINE_DEVICE_TYPE(ES5506, es5506_device, "es5506", "Ensoniq ES5506") -es5506_device::es5506_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +es5506_device::es5506_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : es550x_device(mconfig, ES5506, tag, owner, clock) + , m_write_latch(0) + , m_read_latch(0) + , m_wst(0) + , m_wend(0) + , m_lrend(0) { } @@ -177,12 +188,54 @@ es5506_device::es5506_device(const machine_config &mconfig, const char *tag, dev //------------------------------------------------- void es550x_device::device_start() { + /* initialize the rest of the structure */ + m_master_clock = clock(); + m_irq_cb.resolve(); + m_read_port_cb.resolve(); + m_sample_rate_changed_cb.resolve(); + m_irqv = 0x80; + + /* compute the tables */ + compute_tables(); + + /* allocate memory */ + m_scratch = make_unique_clear<s32[]>(2 * MAX_SAMPLE_CHUNK); + + /* register save */ + save_item(NAME(m_sample_rate)); + + save_item(NAME(m_current_page)); + save_item(NAME(m_active_voices)); + save_item(NAME(m_mode)); + save_item(NAME(m_irqv)); + + save_pointer(NAME(m_scratch), 2 * MAX_SAMPLE_CHUNK); + + for (int j = 0; j < 32; j++) + { + save_item(NAME(m_voice[j].control), j); + save_item(NAME(m_voice[j].freqcount), j); + save_item(NAME(m_voice[j].start), j); + save_item(NAME(m_voice[j].end), j); + save_item(NAME(m_voice[j].accum), j); + save_item(NAME(m_voice[j].lvol), j); + save_item(NAME(m_voice[j].rvol), j); + save_item(NAME(m_voice[j].k2), j); + save_item(NAME(m_voice[j].k1), j); + save_item(NAME(m_voice[j].o4n1), j); + save_item(NAME(m_voice[j].o3n1), j); + save_item(NAME(m_voice[j].o3n2), j); + save_item(NAME(m_voice[j].o2n1), j); + save_item(NAME(m_voice[j].o2n2), j); + save_item(NAME(m_voice[j].o1n1), j); + save_item(NAME(m_voice[j].exbank), j); + } + } void es5506_device::device_start() { - int j; - uint32_t accum_mask; + es550x_device::device_start(); int channels = 1; /* 1 channel by default, for backward compatibility */ /* only override the number of channels if the value is in the valid range 1 .. 6 */ @@ -199,7 +252,7 @@ void es5506_device::device_start() memory_region *region0 = machine().root_device().memregion(m_region0); if (region0 != nullptr) { - m_region_base[0] = (uint16_t *)region0->base(); + m_region_base[0] = (u16 *)region0->base(); } } if (m_region1) @@ -207,7 +260,7 @@ void es5506_device::device_start() memory_region *region1 = machine().root_device().memregion(m_region1); if (region1 != nullptr) { - m_region_base[1] = (uint16_t *)region1->base(); + m_region_base[1] = (u16 *)region1->base(); } } if (m_region2) @@ -215,7 +268,7 @@ void es5506_device::device_start() memory_region *region2 = machine().root_device().memregion(m_region2); if (region2 != nullptr) { - m_region_base[2] = (uint16_t *)region2->base(); + m_region_base[2] = (u16 *)region2->base(); } } if (m_region3) @@ -223,16 +276,11 @@ void es5506_device::device_start() memory_region *region3 = machine().root_device().memregion(m_region3); if (region3 != nullptr) { - m_region_base[3] = (uint16_t *)region3->base(); + m_region_base[3] = (u16 *)region3->base(); } } /* initialize the rest of the structure */ - m_master_clock = clock(); - m_irq_cb.resolve(); - m_read_port_cb.resolve(); - m_sample_rate_changed_cb.resolve(); - m_irqv = 0x80; m_channels = channels; /* KT-76 assumes all voices are active on an ES5506 without setting them! */ @@ -240,62 +288,34 @@ void es5506_device::device_start() m_sample_rate = m_master_clock / (16 * (m_active_voices + 1)); m_stream->set_sample_rate(m_sample_rate); - /* compute the tables */ - compute_tables(); - /* init the voices */ - accum_mask = 0xffffffff; - for (j = 0; j < 32; j++) + // 21 bit integer and 11 bit fraction + m_accum_shift = ADDRESS_FRAC_BIT - ADDRESS_FRAC_BIT_ES5506; + m_accum_mask = ((((1 << 21) - 1) << ADDRESS_FRAC_BIT_ES5506) | ((1 << ADDRESS_FRAC_BIT_ES5506) - 1)) << m_accum_shift; + for (int j = 0; j < 32; j++) { m_voice[j].index = j; m_voice[j].control = CONTROL_STOPMASK; - m_voice[j].lvol = 0xffff; - m_voice[j].rvol = 0xffff; + m_voice[j].lvol = 0xffff << VOLUME_SHIFT_ES5506; // 16 bit volume + m_voice[j].rvol = 0xffff << VOLUME_SHIFT_ES5506; m_voice[j].exbank = 0; - m_voice[j].accum_mask = accum_mask; } - /* allocate memory */ - m_scratch = make_unique_clear<int32_t[]>(2 * MAX_SAMPLE_CHUNK); - /* register save */ - save_item(NAME(m_sample_rate)); save_item(NAME(m_write_latch)); save_item(NAME(m_read_latch)); - save_item(NAME(m_current_page)); - save_item(NAME(m_active_voices)); - save_item(NAME(m_mode)); save_item(NAME(m_wst)); save_item(NAME(m_wend)); save_item(NAME(m_lrend)); - save_item(NAME(m_irqv)); - - save_pointer(NAME(m_scratch), 2 * MAX_SAMPLE_CHUNK); - for (j = 0; j < 32; j++) + for (int j = 0; j < 32; j++) { - save_item(NAME(m_voice[j].control), j); - save_item(NAME(m_voice[j].freqcount), j); - save_item(NAME(m_voice[j].start), j); - save_item(NAME(m_voice[j].lvol), j); - save_item(NAME(m_voice[j].end), j); + save_item(NAME(m_voice[j].ecount), j); save_item(NAME(m_voice[j].lvramp), j); - save_item(NAME(m_voice[j].accum), j); - save_item(NAME(m_voice[j].rvol), j); save_item(NAME(m_voice[j].rvramp), j); - save_item(NAME(m_voice[j].ecount), j); - save_item(NAME(m_voice[j].k2), j); save_item(NAME(m_voice[j].k2ramp), j); - save_item(NAME(m_voice[j].k1), j); save_item(NAME(m_voice[j].k1ramp), j); - save_item(NAME(m_voice[j].o4n1), j); - save_item(NAME(m_voice[j].o3n1), j); - save_item(NAME(m_voice[j].o3n2), j); - save_item(NAME(m_voice[j].o2n1), j); - save_item(NAME(m_voice[j].o2n2), j); - save_item(NAME(m_voice[j].o1n1), j); - save_item(NAME(m_voice[j].exbank), j); save_item(NAME(m_voice[j].filtcount), j); } @@ -323,6 +343,13 @@ void es550x_device::device_reset() { } +void es5506_device::device_reset() // RESB for Reset input +{ + m_active_voices = 0x1f; // 32 voice at reset + m_mode = 0x17; // Dual, Slave, BCLK_EN high, WCLK_EN high, LRCLK_EN high + notify_clock_changed(); +} + //------------------------------------------------- // device_stop - device-specific stop //------------------------------------------------- @@ -331,20 +358,14 @@ void es550x_device::device_stop() { #if ES5506_MAKE_WAVS { - int i; - - for (i = 0; i < MAX_ES5506; i++) - { - if (es5506[i].m_wavraw) - wav_close(es5506[i].m_wavraw); - } + wav_close(m_wavraw); } #endif } DEFINE_DEVICE_TYPE(ES5505, es5505_device, "es5505", "Ensoniq ES5505") -es5505_device::es5505_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) +es5505_device::es5505_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : es550x_device(mconfig, ES5505, tag, owner, clock) { } @@ -355,8 +376,7 @@ es5505_device::es5505_device(const machine_config &mconfig, const char *tag, dev void es5505_device::device_start() { - int j; - uint32_t accum_mask; + es550x_device::device_start(); int channels = 1; /* 1 channel by default, for backward compatibility */ /* only override the number of channels if the value is in the valid range 1 .. 4 */ @@ -370,79 +390,28 @@ void es5505_device::device_start() if (m_region0) { memory_region* region = machine().root_device().memregion(m_region0); - m_region_base[0] = region ? reinterpret_cast<uint16_t *>(region->base()) : nullptr; + m_region_base[0] = region ? reinterpret_cast<u16 *>(region->base()) : nullptr; } if (m_region1) { memory_region* region = machine().root_device().memregion(m_region1); - m_region_base[1] = region ? reinterpret_cast<uint16_t *>(region->base()) : nullptr; + m_region_base[1] = region ? reinterpret_cast<u16 *>(region->base()) : nullptr; } /* initialize the rest of the structure */ - m_master_clock = clock(); - m_irq_cb.resolve(); - m_read_port_cb.resolve(); - m_sample_rate_changed_cb.resolve(); - m_irqv = 0x80; m_channels = channels; - /* compute the tables */ - compute_tables(); - /* init the voices */ - accum_mask = 0x7fffffff; - for (j = 0; j < 32; j++) + // 20 bit integer and 9 bit fraction + m_accum_shift = ADDRESS_FRAC_BIT - ADDRESS_FRAC_BIT_ES5505; + m_accum_mask = ((((1 << 20) - 1) << ADDRESS_FRAC_BIT_ES5505) | ((1 << ADDRESS_FRAC_BIT_ES5505) - 1)) << m_accum_shift; + for (int j = 0; j < 32; j++) { m_voice[j].index = j; m_voice[j].control = CONTROL_STOPMASK; - m_voice[j].lvol = 0xffff; - m_voice[j].rvol = 0xffff; + m_voice[j].lvol = 0xff << VOLUME_SHIFT_ES5505; // 8 bit volume + m_voice[j].rvol = 0xff << VOLUME_SHIFT_ES5505; m_voice[j].exbank = 0; - m_voice[j].accum_mask = accum_mask; - } - - /* allocate memory */ - m_scratch = make_unique_clear<int32_t[]>(2 * MAX_SAMPLE_CHUNK); - - /* register save */ - save_item(NAME(m_sample_rate)); - save_item(NAME(m_write_latch)); - save_item(NAME(m_read_latch)); - - save_item(NAME(m_current_page)); - save_item(NAME(m_active_voices)); - save_item(NAME(m_mode)); - save_item(NAME(m_wst)); - save_item(NAME(m_wend)); - save_item(NAME(m_lrend)); - save_item(NAME(m_irqv)); - - save_pointer(NAME(m_scratch), 2 * MAX_SAMPLE_CHUNK); - - for (j = 0; j < 32; j++) - { - save_item(NAME(m_voice[j].control), j); - save_item(NAME(m_voice[j].freqcount), j); - save_item(NAME(m_voice[j].start), j); - save_item(NAME(m_voice[j].lvol), j); - save_item(NAME(m_voice[j].end), j); - save_item(NAME(m_voice[j].lvramp), j); - save_item(NAME(m_voice[j].accum), j); - save_item(NAME(m_voice[j].rvol), j); - save_item(NAME(m_voice[j].rvramp), j); - save_item(NAME(m_voice[j].ecount), j); - save_item(NAME(m_voice[j].k2), j); - save_item(NAME(m_voice[j].k2ramp), j); - save_item(NAME(m_voice[j].k1), j); - save_item(NAME(m_voice[j].k1ramp), j); - save_item(NAME(m_voice[j].o4n1), j); - save_item(NAME(m_voice[j].o3n1), j); - save_item(NAME(m_voice[j].o3n2), j); - save_item(NAME(m_voice[j].o2n1), j); - save_item(NAME(m_voice[j].o2n2), j); - save_item(NAME(m_voice[j].o1n1), j); - save_item(NAME(m_voice[j].exbank), j); - save_item(NAME(m_voice[j].filtcount), j); } /* success */ @@ -474,7 +443,7 @@ void es550x_device::update_internal_irq_state() terms they get updated next time generate_samples() is called. */ - m_irqv=0x80; + m_irqv = 0x80; if (!m_irq_cb.isnull()) m_irq_cb(0); /* IRQB set low */ @@ -488,35 +457,37 @@ void es550x_device::update_internal_irq_state() void es550x_device::compute_tables() { - int i; - /* allocate ulaw lookup table */ - m_ulaw_lookup = make_unique_clear<int16_t[]>(1 << ULAW_MAXBITS); + m_ulaw_lookup = make_unique_clear<s16[]>(1 << ULAW_MAXBITS); /* generate ulaw lookup table */ - for (i = 0; i < (1 << ULAW_MAXBITS); i++) + for (int i = 0; i < (1 << ULAW_MAXBITS); i++) { - uint16_t rawval = (i << (16 - ULAW_MAXBITS)) | (1 << (15 - ULAW_MAXBITS)); - uint8_t exponent = rawval >> 13; - uint32_t mantissa = (rawval << 3) & 0xffff; + u16 rawval = (i << (16 - ULAW_MAXBITS)) | (1 << (15 - ULAW_MAXBITS)); + u8 exponent = rawval >> 13; + u32 mantissa = (rawval << 3) & 0xffff; if (exponent == 0) - m_ulaw_lookup[i] = (int16_t)mantissa >> 7; + m_ulaw_lookup[i] = (s16)mantissa >> 7; else { mantissa = (mantissa >> 1) | (~mantissa & 0x8000); - m_ulaw_lookup[i] = (int16_t)mantissa >> (7 - exponent); + m_ulaw_lookup[i] = (s16)mantissa >> (7 - exponent); } } + const u32 volume_len = 1 << VOLUME_INTEGER_BIT; /* allocate volume lookup table */ - m_volume_lookup = make_unique_clear<uint16_t[]>(4096); + m_volume_lookup = make_unique_clear<u32[]>(volume_len); /* generate volume lookup table */ - for (i = 0; i < 4096; i++) + const u8 exponent_bit = (VOLUME_INTEGER_BIT - 4); + const u32 mantissa_len = (1 << exponent_bit); + const u32 mantissa_mask = (mantissa_len - 1); + for (int i = 0; i < volume_len; i++) { - uint8_t exponent = i >> 8; - uint32_t mantissa = (i & 0xff) | 0x100; + u8 exponent = i >> exponent_bit; + u32 mantissa = (i & mantissa_mask) | mantissa_len; m_volume_lookup[i] = (mantissa << 11) >> (20 - exponent); } @@ -530,10 +501,14 @@ void es550x_device::compute_tables() ***********************************************************************************************/ -#define interpolate(sample1, sample2, accum) \ - (sample1 * (int32_t)(0x800 - (accum & 0x7ff)) + \ - sample2 * (int32_t)(accum & 0x7ff)) >> 11; - +inline s32 es550x_device::interpolate(s32 sample1, s32 sample2, u32 accum) +{ + const u32 shifted = 1 << ADDRESS_FRAC_BIT; + const u32 mask = shifted - 1; + accum &= mask & m_accum_mask; + return (sample1 * (s32)(shifted - accum) + + sample2 * (s32)(accum)) >> ADDRESS_FRAC_BIT; +} /********************************************************************************************** @@ -542,67 +517,83 @@ void es550x_device::compute_tables() ***********************************************************************************************/ -#define apply_filters(voice, sample) \ -do \ -{ \ - /* pole 1 is always low-pass using K1 */ \ - sample = ((int32_t)(voice->k1 >> 2) * (sample - voice->o1n1) / 16384) + voice->o1n1; \ - voice->o1n1 = sample; \ - \ - /* pole 2 is always low-pass using K1 */ \ - sample = ((int32_t)(voice->k1 >> 2) * (sample - voice->o2n1) / 16384) + voice->o2n1; \ - voice->o2n2 = voice->o2n1; \ - voice->o2n1 = sample; \ - \ - /* remaining poles depend on the current filter setting */ \ - switch (voice->control & CONTROL_LPMASK) \ - { \ - case 0: \ - /* pole 3 is high-pass using K2 */ \ - sample = sample - voice->o2n2 + ((int32_t)(voice->k2 >> 2) * voice->o3n1) / 32768 + voice->o3n1 / 2; \ - voice->o3n2 = voice->o3n1; \ - voice->o3n1 = sample; \ - \ - /* pole 4 is high-pass using K2 */ \ - sample = sample - voice->o3n2 + ((int32_t)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ - voice->o4n1 = sample; \ - break; \ - \ - case CONTROL_LP3: \ - /* pole 3 is low-pass using K1 */ \ - sample = ((int32_t)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ - voice->o3n2 = voice->o3n1; \ - voice->o3n1 = sample; \ - \ - /* pole 4 is high-pass using K2 */ \ - sample = sample - voice->o3n2 + ((int32_t)(voice->k2 >> 2) * voice->o4n1) / 32768 + voice->o4n1 / 2; \ - voice->o4n1 = sample; \ - break; \ - \ - case CONTROL_LP4: \ - /* pole 3 is low-pass using K2 */ \ - sample = ((int32_t)(voice->k2 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ - voice->o3n2 = voice->o3n1; \ - voice->o3n1 = sample; \ - \ - /* pole 4 is low-pass using K2 */ \ - sample = ((int32_t)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ - voice->o4n1 = sample; \ - break; \ - \ - case CONTROL_LP4 | CONTROL_LP3: \ - /* pole 3 is low-pass using K1 */ \ - sample = ((int32_t)(voice->k1 >> 2) * (sample - voice->o3n1) / 16384) + voice->o3n1; \ - voice->o3n2 = voice->o3n1; \ - voice->o3n1 = sample; \ - \ - /* pole 4 is low-pass using K2 */ \ - sample = ((int32_t)(voice->k2 >> 2) * (sample - voice->o4n1) / 16384) + voice->o4n1; \ - voice->o4n1 = sample; \ - break; \ - } \ -} while (0) +// apply lowpass/highpass result +static inline s32 apply_lowpass(s32 out, s32 cutoff, s32 in) +{ + return ((s32)(cutoff >> FILTER_SHIFT) * (out - in) / (1 << FILTER_BIT)) + in; +} + +static inline s32 apply_highpass(s32 out, s32 cutoff, s32 in, s32 prev) +{ + return out - prev + ((s32)(cutoff >> FILTER_SHIFT) * in) / (1 << (FILTER_BIT + 1)) + in / 2; +} + +// update poles from outputs +static inline void update_pole(s32 &pole, s32 sample) +{ + pole = sample; +} +static inline void update_2_pole(s32 &prev, s32 &pole, s32 sample) +{ + prev = pole; + pole = sample; +} + +inline void es550x_device::apply_filters(es550x_voice *voice, s32 &sample) +{ + /* pole 1 is always low-pass using K1 */ + sample = apply_lowpass(sample, voice->k1, voice->o1n1); + update_pole(voice->o1n1, sample); + + /* pole 2 is always low-pass using K1 */ + sample = apply_lowpass(sample, voice->k1, voice->o2n1); + update_2_pole(voice->o2n2, voice->o2n1, sample); + + /* remaining poles depend on the current filter setting */ + switch (get_lp(voice->control)) + { + case 0: + /* pole 3 is high-pass using K2 */ + sample = apply_highpass(sample, voice->k2, voice->o3n1, voice->o2n2); + update_2_pole(voice->o3n2, voice->o3n1, sample); + + /* pole 4 is high-pass using K2 */ + sample = apply_highpass(sample, voice->k2, voice->o4n1, voice->o3n2); + update_pole(voice->o4n1, sample); + break; + + case LP3: + /* pole 3 is low-pass using K1 */ + sample = apply_lowpass(sample, voice->k1, voice->o3n1); + update_2_pole(voice->o3n2, voice->o3n1, sample); + + /* pole 4 is high-pass using K2 */ + sample = apply_highpass(sample, voice->k2, voice->o4n1, voice->o3n2); + update_pole(voice->o4n1, sample); + break; + + case LP4: + /* pole 3 is low-pass using K2 */ + sample = apply_lowpass(sample, voice->k2, voice->o3n1); + update_2_pole(voice->o3n2, voice->o3n1, sample); + + /* pole 4 is low-pass using K2 */ + sample = apply_lowpass(sample, voice->k2, voice->o4n1); + update_pole(voice->o4n1, sample); + break; + + case LP3 | LP4: + /* pole 3 is low-pass using K1 */ + sample = apply_lowpass(sample, voice->k1, voice->o3n1); + update_2_pole(voice->o3n2, voice->o3n1, sample); + + /* pole 4 is low-pass using K2 */ + sample = apply_lowpass(sample, voice->k2, voice->o4n1); + update_pole(voice->o4n1, sample); + break; + } +} /********************************************************************************************** @@ -611,51 +602,53 @@ do ***********************************************************************************************/ -#define update_envelopes(voice, samples) \ -do \ -{ \ - int count = (samples > 1 && samples > voice->ecount) ? voice->ecount : samples; \ - \ - /* decrement the envelope counter */ \ - voice->ecount -= count; \ - \ - /* ramp left volume */ \ - if (voice->lvramp) \ - { \ - voice->lvol += (int8_t)voice->lvramp * count; \ - if ((int32_t)voice->lvol < 0) voice->lvol = 0; \ - else if (voice->lvol > 0xffff) voice->lvol = 0xffff; \ - } \ - \ - /* ramp right volume */ \ - if (voice->rvramp) \ - { \ - voice->rvol += (int8_t)voice->rvramp * count; \ - if ((int32_t)voice->rvol < 0) voice->rvol = 0; \ - else if (voice->rvol > 0xffff) voice->rvol = 0xffff; \ - } \ - \ - /* ramp k1 filter constant */ \ - if (voice->k1ramp && ((int32_t)voice->k1ramp >= 0 || !(voice->filtcount & 7))) \ - { \ - voice->k1 += (int8_t)voice->k1ramp * count; \ - if ((int32_t)voice->k1 < 0) voice->k1 = 0; \ - else if (voice->k1 > 0xffff) voice->k1 = 0xffff; \ - } \ - \ - /* ramp k2 filter constant */ \ - if (voice->k2ramp && ((int32_t)voice->k2ramp >= 0 || !(voice->filtcount & 7))) \ - { \ - voice->k2 += (int8_t)voice->k2ramp * count; \ - if ((int32_t)voice->k2 < 0) voice->k2 = 0; \ - else if (voice->k2 > 0xffff) voice->k2 = 0xffff; \ - } \ - \ - /* update the filter constant counter */ \ - voice->filtcount += count; \ - \ -} while (0) +inline void es5506_device::update_envelopes(es550x_voice *voice) +{ + /* decrement the envelope counter */ + voice->ecount--; + + /* ramp left volume */ + if (voice->lvramp) + { + voice->lvol += (int8_t)voice->lvramp; + if ((s32)voice->lvol < 0) voice->lvol = 0; + else if (voice->lvol > 0xffff) voice->lvol = 0xffff; + } + + /* ramp right volume */ + if (voice->rvramp) + { + voice->rvol += (int8_t)voice->rvramp; + if ((s32)voice->rvol < 0) voice->rvol = 0; + else if (voice->rvol > 0xffff) voice->rvol = 0xffff; + } + + /* ramp k1 filter constant */ + if (voice->k1ramp && ((s32)voice->k1ramp >= 0 || !(voice->filtcount & 7))) + { + voice->k1 += (int8_t)voice->k1ramp; + if ((s32)voice->k1 < 0) voice->k1 = 0; + else if (voice->k1 > 0xffff) voice->k1 = 0xffff; + } + + /* ramp k2 filter constant */ + if (voice->k2ramp && ((s32)voice->k2ramp >= 0 || !(voice->filtcount & 7))) + { + voice->k2 += (int8_t)voice->k2ramp; + if ((s32)voice->k2 < 0) voice->k2 = 0; + else if (voice->k2 > 0xffff) voice->k2 = 0xffff; + } + /* update the filter constant counter */ + voice->filtcount++; + +} + +inline void es5505_device::update_envelopes(es550x_voice *voice) +{ + /* no envelopes in ES5505 */ + voice->ecount = 0; +} /********************************************************************************************** @@ -665,82 +658,144 @@ do ***********************************************************************************************/ -#define check_for_end_forward(voice, accum) \ -do \ -{ \ - /* are we past the end? */ \ - if (accum > voice->end && !(voice->control & CONTROL_LEI)) \ - { \ - /* generate interrupt if required */ \ - if (voice->control&CONTROL_IRQE) \ - voice->control |= CONTROL_IRQ; \ - \ - /* handle the different types of looping */ \ - switch (voice->control & CONTROL_LOOPMASK) \ - { \ - /* non-looping */ \ - case 0: \ - voice->control |= CONTROL_STOP0; \ - goto alldone; \ - \ - /* uni-directional looping */ \ - case CONTROL_LPE: \ - accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \ - break; \ - \ - /* trans-wave looping */ \ - case CONTROL_BLE: \ - accum = (voice->start + (accum - voice->end)) & voice->accum_mask; \ - voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ - break; \ - \ - /* bi-directional looping */ \ - case CONTROL_LPE | CONTROL_BLE: \ - accum = (voice->end - (accum - voice->end)) & voice->accum_mask; \ - voice->control ^= CONTROL_DIR; \ - goto reverse; \ - } \ - } \ -} while (0) - - -#define check_for_end_reverse(voice, accum) \ -do \ -{ \ - /* are we past the end? */ \ - if (accum < voice->start && !(voice->control & CONTROL_LEI)) \ - { \ - /* generate interrupt if required */ \ - if (voice->control&CONTROL_IRQE) \ - voice->control |= CONTROL_IRQ; \ - \ - /* handle the different types of looping */ \ - switch (voice->control & CONTROL_LOOPMASK) \ - { \ - /* non-looping */ \ - case 0: \ - voice->control |= CONTROL_STOP0; \ - goto alldone; \ - \ - /* uni-directional looping */ \ - case CONTROL_LPE: \ - accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \ - break; \ - \ - /* trans-wave looping */ \ - case CONTROL_BLE: \ - accum = (voice->end - (voice->start - accum)) & voice->accum_mask; \ - voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI;\ - break; \ - \ - /* bi-directional looping */ \ - case CONTROL_LPE | CONTROL_BLE: \ - accum = (voice->start + (voice->start - accum)) & voice->accum_mask;\ - voice->control ^= CONTROL_DIR; \ - goto reverse; \ - } \ - } \ -} while (0) +inline void es5506_device::check_for_end_forward(es550x_voice *voice, u32 &accum) +{ + /* are we past the end? */ + if (accum > voice->end && !(voice->control & CONTROL_LEI)) + { + /* generate interrupt if required */ + if (voice->control & CONTROL_IRQE) + voice->control |= CONTROL_IRQ; + + /* handle the different types of looping */ + switch (voice->control & CONTROL_LOOPMASK) + { + /* non-looping */ + case 0: + voice->control |= CONTROL_STOP0; + break; + + /* uni-directional looping */ + case CONTROL_LPE: + accum = (voice->start + (accum - voice->end)) & m_accum_mask; + break; + + /* trans-wave looping */ + case CONTROL_BLE: + accum = (voice->start + (accum - voice->end)) & m_accum_mask; + voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI; + break; + + /* bi-directional looping */ + case CONTROL_LPE | CONTROL_BLE: + accum = (voice->end - (accum - voice->end)) & m_accum_mask; + voice->control ^= CONTROL_DIR; + break; + } + } +} + +inline void es5506_device::check_for_end_reverse(es550x_voice *voice, u32 &accum) +{ + /* are we past the end? */ + if (accum < voice->start && !(voice->control & CONTROL_LEI)) + { + /* generate interrupt if required */ + if (voice->control & CONTROL_IRQE) + voice->control |= CONTROL_IRQ; + + /* handle the different types of looping */ + switch (voice->control & CONTROL_LOOPMASK) + { + /* non-looping */ + case 0: + voice->control |= CONTROL_STOP0; + break; + + /* uni-directional looping */ + case CONTROL_LPE: + accum = (voice->end - (voice->start - accum)) & m_accum_mask; + break; + + /* trans-wave looping */ + case CONTROL_BLE: + accum = (voice->end - (voice->start - accum)) & m_accum_mask; + voice->control = (voice->control & ~CONTROL_LOOPMASK) | CONTROL_LEI; + break; + + /* bi-directional looping */ + case CONTROL_LPE | CONTROL_BLE: + accum = (voice->start + (voice->start - accum)) & m_accum_mask; + voice->control ^= CONTROL_DIR; + break; + } + } +} + +// ES5505 : BLE is ignored when LPE = 0 +inline void es5505_device::check_for_end_forward(es550x_voice *voice, u32 &accum) +{ + /* are we past the end? */ + if (accum > voice->end) + { + /* generate interrupt if required */ + if (voice->control & CONTROL_IRQE) + voice->control |= CONTROL_IRQ; + + /* handle the different types of looping */ + switch (voice->control & CONTROL_LOOPMASK) + { + /* non-looping */ + case 0: + case CONTROL_BLE: + voice->control |= CONTROL_STOP0; + break; + + /* uni-directional looping */ + case CONTROL_LPE: + accum = (voice->start + (accum - voice->end)) & m_accum_mask; + break; + + /* bi-directional looping */ + case CONTROL_LPE | CONTROL_BLE: + accum = (voice->end - (accum - voice->end)) & m_accum_mask; + voice->control ^= CONTROL_DIR; + break; + } + } +} + +inline void es5505_device::check_for_end_reverse(es550x_voice *voice, u32 &accum) +{ + /* are we past the end? */ + if (accum < voice->start) + { + /* generate interrupt if required */ + if (voice->control & CONTROL_IRQE) + voice->control |= CONTROL_IRQ; + + /* handle the different types of looping */ + switch (voice->control & CONTROL_LOOPMASK) + { + /* non-looping */ + case 0: + case CONTROL_BLE: + voice->control |= CONTROL_STOP0; + break; + + /* uni-directional looping */ + case CONTROL_LPE: + accum = (voice->end - (voice->start - accum)) & m_accum_mask; + break; + + /* bi-directional looping */ + case CONTROL_LPE | CONTROL_BLE: + accum = (voice->start + (voice->start - accum)) & m_accum_mask; + voice->control ^= CONTROL_DIR; + break; + } + } +} @@ -750,163 +805,148 @@ do ***********************************************************************************************/ -void es550x_device::generate_dummy(es550x_voice *voice, uint16_t *base, int32_t *lbuffer, int32_t *rbuffer, int samples) +void es550x_device::generate_dummy(es550x_voice *voice, u16 *base, s32 *lbuffer, s32 *rbuffer, int samples) { - uint32_t freqcount = voice->freqcount; - uint32_t accum = voice->accum & voice->accum_mask; + const u32 freqcount = voice->freqcount; + u32 accum = voice->accum & m_accum_mask; /* outer loop, in case we switch directions */ - while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + if (!(voice->control & CONTROL_STOPMASK)) { -reverse: /* two cases: first case is forward direction */ if (!(voice->control & CONTROL_DIR)) { - /* loop while we still have samples to generate */ - while (samples--) - { - /* fetch two samples */ - accum = (accum + freqcount) & voice->accum_mask; + /* fetch two samples */ + accum = (accum + freqcount) & m_accum_mask; - /* update filters/volumes */ - if (voice->ecount != 0) - update_envelopes(voice, 1); + /* update filters/volumes */ + if (voice->ecount != 0) + update_envelopes(voice); - /* check for loop end */ - check_for_end_forward(voice, accum); - } + /* check for loop end */ + check_for_end_forward(voice, accum); } /* two cases: second case is backward direction */ else { - /* loop while we still have samples to generate */ - while (samples--) - { - /* fetch two samples */ - accum = (accum - freqcount) & voice->accum_mask; + /* fetch two samples */ + accum = (accum - freqcount) & m_accum_mask; - /* update filters/volumes */ - if (voice->ecount != 0) - update_envelopes(voice, 1); + /* update filters/volumes */ + if (voice->ecount != 0) + update_envelopes(voice); - /* check for loop end */ - check_for_end_reverse(voice, accum); - } + /* check for loop end */ + check_for_end_reverse(voice, accum); } } + else + { + /* if we stopped, process any additional envelope */ + if (voice->ecount != 0) + update_envelopes(voice); + } - /* if we stopped, process any additional envelope */ -alldone: voice->accum = accum; - if (samples > 0) - update_envelopes(voice, samples); } - /********************************************************************************************** generate_ulaw -- general u-law decoding routine ***********************************************************************************************/ -void es550x_device::generate_ulaw(es550x_voice *voice, uint16_t *base, int32_t *lbuffer, int32_t *rbuffer, int samples) +void es550x_device::generate_ulaw(es550x_voice *voice, u16 *base, s32 *lbuffer, s32 *rbuffer, int samples) { - uint32_t freqcount = voice->freqcount; - uint32_t accum = voice->accum & voice->accum_mask; - int32_t lvol = m_volume_lookup[voice->lvol >> 4]; - int32_t rvol = m_volume_lookup[voice->rvol >> 4]; + const u32 freqcount = voice->freqcount; + u32 accum = voice->accum & m_accum_mask; + s32 lvol = get_volume(voice->lvol); + s32 rvol = get_volume(voice->rvol); /* pre-add the bank offset */ base += voice->exbank; /* outer loop, in case we switch directions */ - while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + if (!(voice->control & CONTROL_STOPMASK)) { -reverse: /* two cases: first case is forward direction */ if (!(voice->control & CONTROL_DIR)) { - /* loop while we still have samples to generate */ - while (samples--) - { - /* fetch two samples */ - int32_t val1 = base[accum >> 11]; - int32_t val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + /* fetch two samples */ + s32 val1 = base[get_integer_addr(accum)]; + s32 val2 = base[get_integer_addr(accum, 1)]; - /* decompress u-law */ - val1 = m_ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; - val2 = m_ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; + /* decompress u-law */ + val1 = m_ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; + val2 = m_ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; - /* interpolate */ - val1 = interpolate(val1, val2, accum); - accum = (accum + freqcount) & voice->accum_mask; + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum + freqcount) & m_accum_mask; - /* apply filters */ - apply_filters(voice, val1); + /* apply filters */ + apply_filters(voice, val1); - /* update filters/volumes */ - if (voice->ecount != 0) - { - update_envelopes(voice, 1); - lvol = m_volume_lookup[voice->lvol >> 4]; - rvol = m_volume_lookup[voice->rvol >> 4]; - } + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice); + lvol = get_volume(voice->lvol); + rvol = get_volume(voice->rvol); + } - /* apply volumes and add */ - *lbuffer++ += (val1 * lvol) >> 11; - *rbuffer++ += (val1 * rvol) >> 11; + /* apply volumes and add */ + *lbuffer += (val1 * lvol) >> VOLUME_ACC_SHIFT; + *rbuffer += (val1 * rvol) >> VOLUME_ACC_SHIFT; - /* check for loop end */ - check_for_end_forward(voice, accum); - } + /* check for loop end */ + check_for_end_forward(voice, accum); } /* two cases: second case is backward direction */ else { - /* loop while we still have samples to generate */ - while (samples--) - { - /* fetch two samples */ - int32_t val1 = base[accum >> 11]; - int32_t val2 = base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + /* fetch two samples */ + s32 val1 = base[get_integer_addr(accum)]; + s32 val2 = base[get_integer_addr(accum, 1)]; - /* decompress u-law */ - val1 = m_ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; - val2 = m_ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; + /* decompress u-law */ + val1 = m_ulaw_lookup[val1 >> (16 - ULAW_MAXBITS)]; + val2 = m_ulaw_lookup[val2 >> (16 - ULAW_MAXBITS)]; - /* interpolate */ - val1 = interpolate(val1, val2, accum); - accum = (accum - freqcount) & voice->accum_mask; + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum - freqcount) & m_accum_mask; - /* apply filters */ - apply_filters(voice, val1); + /* apply filters */ + apply_filters(voice, val1); - /* update filters/volumes */ - if (voice->ecount != 0) - { - update_envelopes(voice, 1); - lvol = m_volume_lookup[voice->lvol >> 4]; - rvol = m_volume_lookup[voice->rvol >> 4]; - } + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice); + lvol = get_volume(voice->lvol); + rvol = get_volume(voice->rvol); + } - /* apply volumes and add */ - *lbuffer++ += (val1 * lvol) >> 11; - *rbuffer++ += (val1 * rvol) >> 11; + /* apply volumes and add */ + *lbuffer += (val1 * lvol) >> VOLUME_ACC_SHIFT; + *rbuffer += (val1 * rvol) >> VOLUME_ACC_SHIFT; - /* check for loop end */ - check_for_end_reverse(voice, accum); - } + /* check for loop end */ + check_for_end_reverse(voice, accum); } } + else + { + /* if we stopped, process any additional envelope */ + if (voice->ecount != 0) + update_envelopes(voice); + } - /* if we stopped, process any additional envelope */ -alldone: voice->accum = accum; - if (samples > 0) - update_envelopes(voice, samples); } @@ -917,96 +957,116 @@ alldone: ***********************************************************************************************/ -void es550x_device::generate_pcm(es550x_voice *voice, uint16_t *base, int32_t *lbuffer, int32_t *rbuffer, int samples) +void es550x_device::generate_pcm(es550x_voice *voice, u16 *base, s32 *lbuffer, s32 *rbuffer, int samples) { - uint32_t freqcount = voice->freqcount; - uint32_t accum = voice->accum & voice->accum_mask; - int32_t lvol = m_volume_lookup[voice->lvol >> 4]; - int32_t rvol = m_volume_lookup[voice->rvol >> 4]; + const u32 freqcount = voice->freqcount; + u32 accum = voice->accum & m_accum_mask; + s32 lvol = get_volume(voice->lvol); + s32 rvol = get_volume(voice->rvol); /* pre-add the bank offset */ base += voice->exbank; /* outer loop, in case we switch directions */ - while (samples > 0 && !(voice->control & CONTROL_STOPMASK)) + if (!(voice->control & CONTROL_STOPMASK)) { -reverse: /* two cases: first case is forward direction */ if (!(voice->control & CONTROL_DIR)) { - /* loop while we still have samples to generate */ - while (samples--) - { - /* fetch two samples */ - int32_t val1 = (int16_t)base[accum >> 11]; - int32_t val2 = (int16_t)base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + /* fetch two samples */ + s32 val1 = (s16)base[get_integer_addr(accum)]; + s32 val2 = (s16)base[get_integer_addr(accum, 1)]; - /* interpolate */ - val1 = interpolate(val1, val2, accum); - accum = (accum + freqcount) & voice->accum_mask; + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum + freqcount) & m_accum_mask; - /* apply filters */ - apply_filters(voice, val1); + /* apply filters */ + apply_filters(voice, val1); - /* update filters/volumes */ - if (voice->ecount != 0) - { - update_envelopes(voice, 1); - lvol = m_volume_lookup[voice->lvol >> 4]; - rvol = m_volume_lookup[voice->rvol >> 4]; - } + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice); + lvol = get_volume(voice->lvol); + rvol = get_volume(voice->rvol); + } - /* apply volumes and add */ - *lbuffer++ += (val1 * lvol) >> 11; - *rbuffer++ += (val1 * rvol) >> 11; + /* apply volumes and add */ + *lbuffer += (val1 * lvol) >> VOLUME_ACC_SHIFT; + *rbuffer += (val1 * rvol) >> VOLUME_ACC_SHIFT; - /* check for loop end */ - check_for_end_forward(voice, accum); - } + /* check for loop end */ + check_for_end_forward(voice, accum); } /* two cases: second case is backward direction */ else { - /* loop while we still have samples to generate */ - while (samples--) - { - /* fetch two samples */ - int32_t val1 = (int16_t)base[accum >> 11]; - int32_t val2 = (int16_t)base[((accum + (1 << 11)) & voice->accum_mask) >> 11]; + /* fetch two samples */ + s32 val1 = (s16)base[get_integer_addr(accum)]; + s32 val2 = (s16)base[get_integer_addr(accum, 1)]; - /* interpolate */ - val1 = interpolate(val1, val2, accum); - accum = (accum - freqcount) & voice->accum_mask; + /* interpolate */ + val1 = interpolate(val1, val2, accum); + accum = (accum - freqcount) & m_accum_mask; - /* apply filters */ - apply_filters(voice, val1); + /* apply filters */ + apply_filters(voice, val1); - /* update filters/volumes */ - if (voice->ecount != 0) - { - update_envelopes(voice, 1); - lvol = m_volume_lookup[voice->lvol >> 4]; - rvol = m_volume_lookup[voice->rvol >> 4]; - } + /* update filters/volumes */ + if (voice->ecount != 0) + { + update_envelopes(voice); + lvol = get_volume(voice->lvol); + rvol = get_volume(voice->rvol); + } - /* apply volumes and add */ - *lbuffer++ += (val1 * lvol) >> 11; - *rbuffer++ += (val1 * rvol) >> 11; + /* apply volumes and add */ + *lbuffer += (val1 * lvol) >> VOLUME_ACC_SHIFT; + *rbuffer += (val1 * rvol) >> VOLUME_ACC_SHIFT; - /* check for loop end */ - check_for_end_reverse(voice, accum); - } + /* check for loop end */ + check_for_end_reverse(voice, accum); } } + else + { + /* if we stopped, process any additional envelope */ + if (voice->ecount != 0) + update_envelopes(voice); + } - /* if we stopped, process any additional envelope */ -alldone: voice->accum = accum; - if (samples > 0) - update_envelopes(voice, samples); } +/********************************************************************************************** + + generate_irq -- general interrupt handling routine + +***********************************************************************************************/ + +inline void es550x_device::generate_irq(es550x_voice *voice, int v) +{ + /* does this voice have it's IRQ bit raised? */ + if (voice->control & CONTROL_IRQ) + { + LOG("es5506: IRQ raised on voice %d!!\n",v); + + /* only update voice vector if existing IRQ is acked by host */ + if (m_irqv & 0x80) + { + /* latch voice number into vector, and set high bit low */ + m_irqv = v & 0x1f; + + /* take down IRQ bit on voice */ + voice->control &= ~CONTROL_IRQ; + + /* inform host of irq */ + update_irq_state(); + } + } +} /********************************************************************************************** @@ -1015,10 +1075,8 @@ alldone: ***********************************************************************************************/ -void es5506_device::generate_samples(int32_t **outputs, int offset, int samples) +void es5506_device::generate_samples(s32 **outputs, int offset, int samples) { - int v; - /* skip if nothing to do */ if (!samples) return; @@ -1026,62 +1084,50 @@ void es5506_device::generate_samples(int32_t **outputs, int offset, int samples) /* clear out the accumulators */ for (int i = 0; i < m_channels << 1; i++) { - memset(outputs[i] + offset, 0, sizeof(int32_t) * samples); + memset(outputs[i] + offset, 0, sizeof(s32) * samples); } - /* loop over voices */ - for (v = 0; v <= m_active_voices; v++) + /* loop while we still have samples to generate */ + while (samples) { - es550x_voice *voice = &m_voice[v]; - uint16_t *base = m_region_base[voice->control >> 14]; - - /* special case: if end == start, stop the voice */ - if (voice->start == voice->end) - voice->control |= CONTROL_STOP0; - - int voice_channel = (voice->control & CONTROL_CAMASK) >> 10; - int channel = voice_channel % m_channels; - int l = channel << 1; - int r = l + 1; - int32_t *left = outputs[l] + offset; - int32_t *right = outputs[r] + offset; - - /* generate from the appropriate source */ - if (!base) - { - LOG("es5506: nullptr region base %d\n",voice->control >> 14); - generate_dummy(voice, base, left, right, samples); - } - else if (voice->control & 0x2000) - generate_ulaw(voice, base, left, right, samples); - else - generate_pcm(voice, base, left, right, samples); - - /* does this voice have it's IRQ bit raised? */ - if (voice->control&CONTROL_IRQ) + /* loop over voices */ + for (int v = 0; v <= m_active_voices; v++) { - LOG("es5506: IRQ raised on voice %d!!\n",v); - - /* only update voice vector if existing IRQ is acked by host */ - if (m_irqv&0x80) + es550x_voice *voice = &m_voice[v]; + u16 *base = m_region_base[get_bank(voice->control)]; + + /* special case: if end == start, stop the voice */ + if (voice->start == voice->end) + voice->control |= CONTROL_STOP0; + + const int voice_channel = get_ca(voice->control); + const int channel = voice_channel % m_channels; + const int l = channel << 1; + const int r = l + 1; + s32 *left = outputs[l] + offset; + s32 *right = outputs[r] + offset; + + /* generate from the appropriate source */ + if (!base) { - /* latch voice number into vector, and set high bit low */ - m_irqv=v&0x7f; - - /* take down IRQ bit on voice */ - voice->control&=~CONTROL_IRQ; - - /* inform host of irq */ - update_irq_state(); + LOG("es5506: nullptr region base %d\n",get_bank(voice->control)); + generate_dummy(voice, base, left, right, samples); } + else if (voice->control & CONTROL_CMPD) + generate_ulaw(voice, base, left, right, samples); + else + generate_pcm(voice, base, left, right, samples); + + /* does this voice have it's IRQ bit raised? */ + generate_irq(voice, v); } + offset++; + samples--; } } -void es5505_device::generate_samples(int32_t **outputs, int offset, int samples) +void es5505_device::generate_samples(s32 **outputs, int offset, int samples) { - int v; - /* skip if nothing to do */ if (!samples) return; @@ -1089,61 +1135,50 @@ void es5505_device::generate_samples(int32_t **outputs, int offset, int samples) /* clear out the accumulators */ for (int i = 0; i < m_channels << 1; i++) { - memset(outputs[i] + offset, 0, sizeof(int32_t) * samples); + memset(outputs[i] + offset, 0, sizeof(s32) * samples); } - /* loop over voices */ - for (v = 0; v <= m_active_voices; v++) + /* loop while we still have samples to generate */ + while (samples) { - es550x_voice *voice = &m_voice[v]; - uint16_t *base = m_region_base[voice->control >> 14]; + /* loop over voices */ + for (int v = 0; v <= m_active_voices; v++) + { + es550x_voice *voice = &m_voice[v]; + u16 *base = m_region_base[get_bank(voice->control)]; // This special case does not appear to match the behaviour observed in the es5505 in // actual Ensoniq synthesizers: those, it turns out, do set loop start and end to the // same value, and expect the voice to keep running. Examples can be found among the // transwaves on the VFX / SD-1 series of synthesizers. #if 0 - /* special case: if end == start, stop the voice */ - if (voice->start == voice->end) - voice->control |= CONTROL_STOP0; + /* special case: if end == start, stop the voice */ + if (voice->start == voice->end) + voice->control |= CONTROL_STOP0; #endif - int voice_channel = (voice->control & CONTROL_CAMASK) >> 10; - int channel = voice_channel % m_channels; - int l = channel << 1; - int r = l + 1; - int32_t *left = outputs[l] + offset; - int32_t *right = outputs[r] + offset; - - /* generate from the appropriate source */ - if (!base) - { - LOG("es5506: nullptr region base %d\n",voice->control >> 14); - generate_dummy(voice, base, left, right, samples); - } - else if (voice->control & 0x2000) - generate_ulaw(voice, base, left, right, samples); - else - generate_pcm(voice, base, left, right, samples); + const int voice_channel = get_ca(voice->control); + const int channel = voice_channel % m_channels; + const int l = channel << 1; + const int r = l + 1; + s32 *left = outputs[l] + offset; + s32 *right = outputs[r] + offset; - /* does this voice have it's IRQ bit raised? */ - if (voice->control&CONTROL_IRQ) - { - LOG("es5506: IRQ raised on voice %d!!\n",v); - - /* only update voice vector if existing IRQ is acked by host */ - if (m_irqv&0x80) + /* generate from the appropriate source */ + if (!base) { - /* latch voice number into vector, and set high bit low */ - m_irqv=v&0x7f; - - /* take down IRQ bit on voice */ - voice->control&=~CONTROL_IRQ; - - /* inform host of irq */ - update_irq_state(); + LOG("es5506: nullptr region base %d\n",get_bank(voice->control)); + generate_dummy(voice, base, left, right, samples); } + // no compressed sample support + else + generate_pcm(voice, base, left, right, samples); + + /* does this voice have it's IRQ bit raised? */ + generate_irq(voice, v); } + offset++; + samples--; } } @@ -1155,7 +1190,7 @@ void es5505_device::generate_samples(int32_t **outputs, int offset, int samples) ***********************************************************************************************/ -inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, uint32_t data) +inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, u32 data) { switch (offset) { @@ -1165,12 +1200,12 @@ inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, uin break; case 0x08/8: /* FC */ - voice->freqcount = data & 0x1ffff; - LOG("voice %d, freq count=%08x\n", m_current_page & 0x1f, voice->freqcount); + voice->freqcount = get_accum_shifted_val(data & 0x1ffff); + LOG("voice %d, freq count=%08x\n", m_current_page & 0x1f, get_accum_res(voice->freqcount)); break; case 0x10/8: /* LVOL */ - voice->lvol = data & 0xffff; + voice->lvol = data & 0xffff; // low 4 bit is used for finer envelope control LOG("voice %d, left vol=%04x\n", m_current_page & 0x1f, voice->lvol); break; @@ -1180,7 +1215,7 @@ inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, uin break; case 0x20/8: /* RVOL */ - voice->rvol = data & 0xffff; + voice->rvol = data & 0xffff; // low 4 bit is used for finer envelope control LOG("voice %d, right vol=%04x\n", m_current_page & 0x1f, voice->rvol); break; @@ -1196,7 +1231,7 @@ inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, uin break; case 0x38/8: /* K2 */ - voice->k2 = data & 0xffff; + voice->k2 = data & 0xffff; // low 4 bit is used for finer envelope control LOG("voice %d, K2=%04x\n", m_current_page & 0x1f, voice->k2); break; @@ -1206,7 +1241,7 @@ inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, uin break; case 0x48/8: /* K1 */ - voice->k1 = data & 0xffff; + voice->k1 = data & 0xffff; // low 4 bit is used for finer envelope control LOG("voice %d, K1=%04x\n", m_current_page & 0x1f, voice->k1); break; @@ -1228,7 +1263,11 @@ inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, uin } case 0x60/8: /* MODE */ - m_mode = data & 0x1f; + // [4:3] = 00 : Single, Master, Early address mode + // [4:3] = 01 : Single, Master, Normal address mode + // [4:3] = 10 : Dual, Slave, Normal address mode + // [4:3] = 11 : Dual, Master, Normal address mode + m_mode = data & 0x1f; // MODE1[4], MODE0[3], BCLK_EN[2], WCLK_EN[1], LRCLK_EN[0] break; case 0x68/8: /* PAR - read only */ @@ -1241,7 +1280,7 @@ inline void es5506_device::reg_write_low(es550x_voice *voice, offs_t offset, uin } } -inline void es5506_device::reg_write_high(es550x_voice *voice, offs_t offset, uint32_t data) +inline void es5506_device::reg_write_high(es550x_voice *voice, offs_t offset, u32 data) { switch (offset) { @@ -1251,47 +1290,47 @@ inline void es5506_device::reg_write_high(es550x_voice *voice, offs_t offset, ui break; case 0x08/8: /* START */ - voice->start = data & 0xfffff800; - LOG("voice %d, loop start=%08x\n", m_current_page & 0x1f, voice->start); + voice->start = get_accum_shifted_val(data & 0xfffff800); + LOG("voice %d, loop start=%08x\n", m_current_page & 0x1f, get_accum_res(voice->start)); break; case 0x10/8: /* END */ - voice->end = data & 0xffffff80; - LOG("voice %d, loop end=%08x\n", m_current_page & 0x1f, voice->end); + voice->end = get_accum_shifted_val(data & 0xffffff80); + LOG("voice %d, loop end=%08x\n", m_current_page & 0x1f, get_accum_res(voice->end)); break; case 0x18/8: /* ACCUM */ - voice->accum = data; - LOG("voice %d, accum=%08x\n", m_current_page & 0x1f, voice->accum); + voice->accum = get_accum_shifted_val(data); + LOG("voice %d, accum=%08x\n", m_current_page & 0x1f, get_accum_res(voice->accum)); break; case 0x20/8: /* O4(n-1) */ - voice->o4n1 = (int32_t)(data << 14) >> 14; + voice->o4n1 = (s32)(data << 14) >> 14; LOG("voice %d, O4(n-1)=%05x\n", m_current_page & 0x1f, voice->o4n1 & 0x3ffff); break; case 0x28/8: /* O3(n-1) */ - voice->o3n1 = (int32_t)(data << 14) >> 14; + voice->o3n1 = (s32)(data << 14) >> 14; LOG("voice %d, O3(n-1)=%05x\n", m_current_page & 0x1f, voice->o3n1 & 0x3ffff); break; case 0x30/8: /* O3(n-2) */ - voice->o3n2 = (int32_t)(data << 14) >> 14; + voice->o3n2 = (s32)(data << 14) >> 14; LOG("voice %d, O3(n-2)=%05x\n", m_current_page & 0x1f, voice->o3n2 & 0x3ffff); break; case 0x38/8: /* O2(n-1) */ - voice->o2n1 = (int32_t)(data << 14) >> 14; + voice->o2n1 = (s32)(data << 14) >> 14; LOG("voice %d, O2(n-1)=%05x\n", m_current_page & 0x1f, voice->o2n1 & 0x3ffff); break; case 0x40/8: /* O2(n-2) */ - voice->o2n2 = (int32_t)(data << 14) >> 14; + voice->o2n2 = (s32)(data << 14) >> 14; LOG("voice %d, O2(n-2)=%05x\n", m_current_page & 0x1f, voice->o2n2 & 0x3ffff); break; case 0x48/8: /* O1(n-1) */ - voice->o1n1 = (int32_t)(data << 14) >> 14; + voice->o1n1 = (s32)(data << 14) >> 14; LOG("voice %d, O1(n-1)=%05x\n", m_current_page & 0x1f, voice->o1n1 & 0x3ffff); break; @@ -1317,7 +1356,7 @@ inline void es5506_device::reg_write_high(es550x_voice *voice, offs_t offset, ui } } -inline void es5506_device::reg_write_test(es550x_voice *voice, offs_t offset, uint32_t data) +inline void es5506_device::reg_write_test(es550x_voice *voice, offs_t offset, u32 data) { switch (offset) { @@ -1383,7 +1422,7 @@ inline void es5506_device::reg_write_test(es550x_voice *voice, offs_t offset, ui } } -WRITE8_MEMBER( es5506_device::write ) +WRITE8_MEMBER(es5506_device::write) { es550x_voice *voice = &m_voice[m_current_page & 0x1f]; int shift = 8 * (offset & 3); @@ -1418,9 +1457,9 @@ WRITE8_MEMBER( es5506_device::write ) ***********************************************************************************************/ -inline uint32_t es5506_device::reg_read_low(es550x_voice *voice, offs_t offset) +inline u32 es5506_device::reg_read_low(es550x_voice *voice, offs_t offset) { - uint32_t result = 0; + u32 result = 0; switch (offset) { @@ -1429,7 +1468,7 @@ inline uint32_t es5506_device::reg_read_low(es550x_voice *voice, offs_t offset) break; case 0x08/8: /* FC */ - result = voice->freqcount; + result = get_accum_res(voice->freqcount); break; case 0x10/8: /* LVOL */ @@ -1478,12 +1517,13 @@ inline uint32_t es5506_device::reg_read_low(es550x_voice *voice, offs_t offset) case 0x68/8: /* PAR */ if (!m_read_port_cb.isnull()) - result = m_read_port_cb(0); + result = m_read_port_cb(0) & 0x3ff; // 10 bit, 9:0 break; case 0x70/8: /* IRQV */ result = m_irqv; - update_internal_irq_state(); + if (!machine().side_effects_disabled()) + update_internal_irq_state(); break; case 0x78/8: /* PAGE */ @@ -1494,9 +1534,9 @@ inline uint32_t es5506_device::reg_read_low(es550x_voice *voice, offs_t offset) } -inline uint32_t es5506_device::reg_read_high(es550x_voice *voice, offs_t offset) +inline u32 es5506_device::reg_read_high(es550x_voice *voice, offs_t offset) { - uint32_t result = 0; + u32 result = 0; switch (offset) { @@ -1505,15 +1545,15 @@ inline uint32_t es5506_device::reg_read_high(es550x_voice *voice, offs_t offset) break; case 0x08/8: /* START */ - result = voice->start; + result = get_accum_res(voice->start); break; case 0x10/8: /* END */ - result = voice->end; + result = get_accum_res(voice->end); break; case 0x18/8: /* ACCUM */ - result = voice->accum; + result = get_accum_res(voice->accum); break; case 0x20/8: /* O4(n-1) */ @@ -1554,12 +1594,13 @@ inline uint32_t es5506_device::reg_read_high(es550x_voice *voice, offs_t offset) case 0x68/8: /* PAR */ if (!m_read_port_cb.isnull()) - result = m_read_port_cb(0); + result = m_read_port_cb(0) & 0x3ff; // 10 bit, 9:0 break; case 0x70/8: /* IRQV */ result = m_irqv; - update_internal_irq_state(); + if (!machine().side_effects_disabled()) + update_internal_irq_state(); break; case 0x78/8: /* PAGE */ @@ -1568,15 +1609,15 @@ inline uint32_t es5506_device::reg_read_high(es550x_voice *voice, offs_t offset) } return result; } -inline uint32_t es5506_device::reg_read_test(es550x_voice *voice, offs_t offset) +inline u32 es5506_device::reg_read_test(es550x_voice *voice, offs_t offset) { - uint32_t result = 0; + u32 result = 0; switch (offset) { case 0x68/8: /* PAR */ if (!m_read_port_cb.isnull()) - result = m_read_port_cb(0); + result = m_read_port_cb(0) & 0x3ff; // 10 bit, 9:0 break; case 0x70/8: /* IRQV */ @@ -1590,7 +1631,7 @@ inline uint32_t es5506_device::reg_read_test(es550x_voice *voice, offs_t offset) return result; } -READ8_MEMBER( es5506_device::read ) +READ8_MEMBER(es5506_device::read) { es550x_voice *voice = &m_voice[m_current_page & 0x1f]; int shift = 8 * (offset & 3); @@ -1632,75 +1673,71 @@ void es5506_device::voice_bank_w(int voice, int bank) ***********************************************************************************************/ -inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, uint16_t data, uint16_t mem_mask) +inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, u16 data, u16 mem_mask) { switch (offset) { case 0x00: /* CR */ + voice->control |= 0xf000; // bit 15-12 always 1 if (ACCESSING_BITS_0_7) { #if RAINE_CHECK voice->control &= ~(CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_DIR); #else - voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ); + voice->control &= ~0x00ff; #endif - voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | - ((data << 12) & CONTROL_BS0); + voice->control |= (data & 0x00ff); } if (ACCESSING_BITS_8_15) - { - voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK); - voice->control |= ((data >> 2) & CONTROL_LPMASK) | - ((data << 2) & (CONTROL_CA0 | CONTROL_CA1)); - } + voice->control = (voice->control & ~0x0f00) | (data & 0x0f00); LOG("%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine().describe_context(), m_current_page & 0x1f, voice->control, data, mem_mask ^ 0xffff); break; case 0x01: /* FC */ if (ACCESSING_BITS_0_7) - voice->freqcount = (voice->freqcount & ~0x001fe) | ((data & 0x00ff) << 1); + voice->freqcount = (voice->freqcount & ~get_accum_shifted_val(0x00fe, 1)) | (get_accum_shifted_val((data & 0x00fe), 1)); if (ACCESSING_BITS_8_15) - voice->freqcount = (voice->freqcount & ~0x1fe00) | ((data & 0xff00) << 1); - LOG("%s:voice %d, freq count=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->freqcount); + voice->freqcount = (voice->freqcount & ~get_accum_shifted_val(0xff00, 1)) | (get_accum_shifted_val((data & 0xff00), 1)); + LOG("%s:voice %d, freq count=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_accum_res(voice->freqcount, 1)); break; case 0x02: /* STRT (hi) */ if (ACCESSING_BITS_0_7) - voice->start = (voice->start & ~0x03fc0000) | ((data & 0x00ff) << 18); + voice->start = (voice->start & ~get_accum_shifted_val(0x00ff0000)) | (get_accum_shifted_val((data & 0x00ff) << 16)); if (ACCESSING_BITS_8_15) - voice->start = (voice->start & ~0x7c000000) | ((data & 0x1f00) << 18); - LOG("%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->start); + voice->start = (voice->start & ~get_accum_shifted_val(0x1f000000)) | (get_accum_shifted_val((data & 0x1f00) << 16)); + LOG("%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_accum_res(voice->start)); break; case 0x03: /* STRT (lo) */ if (ACCESSING_BITS_0_7) - voice->start = (voice->start & ~0x00000380) | ((data & 0x00e0) << 2); + voice->start = (voice->start & ~get_accum_shifted_val(0x000000e0)) | (get_accum_shifted_val(data & 0x00e0)); if (ACCESSING_BITS_8_15) - voice->start = (voice->start & ~0x0003fc00) | ((data & 0xff00) << 2); - LOG("%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->start); + voice->start = (voice->start & ~get_accum_shifted_val(0x0000ff00)) | (get_accum_shifted_val(data & 0xff00)); + LOG("%s:voice %d, loop start=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_accum_res(voice->start)); break; case 0x04: /* END (hi) */ if (ACCESSING_BITS_0_7) - voice->end = (voice->end & ~0x03fc0000) | ((data & 0x00ff) << 18); + voice->end = (voice->end & ~get_accum_shifted_val(0x00ff0000)) | (get_accum_shifted_val((data & 0x00ff) << 16)); if (ACCESSING_BITS_8_15) - voice->end = (voice->end & ~0x7c000000) | ((data & 0x1f00) << 18); + voice->end = (voice->end & ~get_accum_shifted_val(0x1f000000)) | (get_accum_shifted_val((data & 0x1f00) << 16)); #if RAINE_CHECK voice->control |= CONTROL_STOP0; #endif - LOG("%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->end); + LOG("%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_accum_res(voice->end)); break; case 0x05: /* END (lo) */ if (ACCESSING_BITS_0_7) - voice->end = (voice->end & ~0x00000380) | ((data & 0x00e0) << 2); + voice->end = (voice->end & ~get_accum_shifted_val(0x000000e0)) | (get_accum_shifted_val(data & 0x00e0)); if (ACCESSING_BITS_8_15) - voice->end = (voice->end & ~0x0003fc00) | ((data & 0xff00) << 2); + voice->end = (voice->end & ~get_accum_shifted_val(0x0000ff00)) | (get_accum_shifted_val(data & 0xff00)); #if RAINE_CHECK voice->control |= CONTROL_STOP0; #endif - LOG("%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->end); + LOG("%s:voice %d, loop end=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_accum_res(voice->end)); break; case 0x06: /* K2 */ @@ -1708,7 +1745,7 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, uin voice->k2 = (voice->k2 & ~0x00f0) | (data & 0x00f0); if (ACCESSING_BITS_8_15) voice->k2 = (voice->k2 & ~0xff00) | (data & 0xff00); - LOG("%s:voice %d, K2=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->k2); + LOG("%s:voice %d, K2=%03x\n", machine().describe_context(), m_current_page & 0x1f, voice->k2 >> FILTER_SHIFT); break; case 0x07: /* K1 */ @@ -1716,35 +1753,35 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, uin voice->k1 = (voice->k1 & ~0x00f0) | (data & 0x00f0); if (ACCESSING_BITS_8_15) voice->k1 = (voice->k1 & ~0xff00) | (data & 0xff00); - LOG("%s:voice %d, K1=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->k1); + LOG("%s:voice %d, K1=%03x\n", machine().describe_context(), m_current_page & 0x1f, voice->k1 >> FILTER_SHIFT); break; case 0x08: /* LVOL */ if (ACCESSING_BITS_8_15) voice->lvol = (voice->lvol & ~0xff00) | (data & 0xff00); - LOG("%s:voice %d, left vol=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->lvol); + LOG("%s:voice %d, left vol=%02x\n", machine().describe_context(), m_current_page & 0x1f, voice->lvol >> VOLUME_SHIFT_ES5505); break; case 0x09: /* RVOL */ if (ACCESSING_BITS_8_15) voice->rvol = (voice->rvol & ~0xff00) | (data & 0xff00); - LOG("%s:voice %d, right vol=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->rvol); + LOG("%s:voice %d, right vol=%02x\n", machine().describe_context(), m_current_page & 0x1f, voice->rvol >> VOLUME_SHIFT_ES5505); break; case 0x0a: /* ACC (hi) */ if (ACCESSING_BITS_0_7) - voice->accum = (voice->accum & ~0x03fc0000) | ((data & 0x00ff) << 18); + voice->accum = (voice->accum & ~get_accum_shifted_val(0x00ff0000)) | (get_accum_shifted_val((data & 0x00ff) << 16)); if (ACCESSING_BITS_8_15) - voice->accum = (voice->accum & ~0x7c000000) | ((data & 0x1f00) << 18); - LOG("%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->accum); + voice->accum = (voice->accum & ~get_accum_shifted_val(0x1f000000)) | (get_accum_shifted_val((data & 0x1f00) << 16)); + LOG("%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_accum_res(voice->accum)); break; case 0x0b: /* ACC (lo) */ if (ACCESSING_BITS_0_7) - voice->accum = (voice->accum & ~0x000003fc) | ((data & 0x00ff) << 2); + voice->accum = (voice->accum & ~get_accum_shifted_val(0x000000ff)) | (get_accum_shifted_val(data & 0x00ff)); if (ACCESSING_BITS_8_15) - voice->accum = (voice->accum & ~0x0003fc00) | ((data & 0xff00) << 2); - LOG("%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, voice->accum); + voice->accum = (voice->accum & ~get_accum_shifted_val(0x0000ff00)) | (get_accum_shifted_val(data & 0xff00)); + LOG("%s:voice %d, accum=%08x\n", machine().describe_context(), m_current_page & 0x1f, get_accum_res(voice->accum)); break; case 0x0c: /* unused */ @@ -1774,23 +1811,17 @@ inline void es5505_device::reg_write_low(es550x_voice *voice, offs_t offset, uin } -inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, uint16_t data, uint16_t mem_mask) +inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, u16 data, u16 mem_mask) { switch (offset) { case 0x00: /* CR */ + voice->control |= 0xf000; // bit 15-12 always 1 if (ACCESSING_BITS_0_7) - { - voice->control &= ~(CONTROL_STOPMASK | CONTROL_BS0 | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ); - voice->control |= (data & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | - ((data << 12) & CONTROL_BS0); - } + voice->control = (voice->control & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) - { - voice->control &= ~(CONTROL_CA0 | CONTROL_CA1 | CONTROL_LPMASK); - voice->control |= ((data >> 2) & CONTROL_LPMASK) | - ((data << 2) & (CONTROL_CA0 | CONTROL_CA1)); - } + voice->control = (voice->control & ~0x0f00) | (data & 0x0f00); + LOG("%s:voice %d, control=%04x (raw=%04x & %04x)\n", machine().describe_context(), m_current_page & 0x1f, voice->control, data, mem_mask); break; @@ -1798,48 +1829,48 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, ui if (ACCESSING_BITS_0_7) voice->o4n1 = (voice->o4n1 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) - voice->o4n1 = (int16_t)((voice->o4n1 & ~0xff00) | (data & 0xff00)); - LOG("%s:voice %d, O4(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o4n1 & 0x3ffff); + voice->o4n1 = (s16)((voice->o4n1 & ~0xff00) | (data & 0xff00)); + LOG("%s:voice %d, O4(n-1)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o4n1 & 0xffff); break; case 0x02: /* O3(n-1) */ if (ACCESSING_BITS_0_7) voice->o3n1 = (voice->o3n1 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) - voice->o3n1 = (int16_t)((voice->o3n1 & ~0xff00) | (data & 0xff00)); - LOG("%s:voice %d, O3(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n1 & 0x3ffff); + voice->o3n1 = (s16)((voice->o3n1 & ~0xff00) | (data & 0xff00)); + LOG("%s:voice %d, O3(n-1)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n1 & 0xffff); break; case 0x03: /* O3(n-2) */ if (ACCESSING_BITS_0_7) voice->o3n2 = (voice->o3n2 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) - voice->o3n2 = (int16_t)((voice->o3n2 & ~0xff00) | (data & 0xff00)); - LOG("%s:voice %d, O3(n-2)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n2 & 0x3ffff); + voice->o3n2 = (s16)((voice->o3n2 & ~0xff00) | (data & 0xff00)); + LOG("%s:voice %d, O3(n-2)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o3n2 & 0xffff); break; case 0x04: /* O2(n-1) */ if (ACCESSING_BITS_0_7) voice->o2n1 = (voice->o2n1 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) - voice->o2n1 = (int16_t)((voice->o2n1 & ~0xff00) | (data & 0xff00)); - LOG("%s:voice %d, O2(n-1)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n1 & 0x3ffff); + voice->o2n1 = (s16)((voice->o2n1 & ~0xff00) | (data & 0xff00)); + LOG("%s:voice %d, O2(n-1)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n1 & 0xffff); break; case 0x05: /* O2(n-2) */ if (ACCESSING_BITS_0_7) voice->o2n2 = (voice->o2n2 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) - voice->o2n2 = (int16_t)((voice->o2n2 & ~0xff00) | (data & 0xff00)); - LOG("%s:voice %d, O2(n-2)=%05x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n2 & 0x3ffff); + voice->o2n2 = (s16)((voice->o2n2 & ~0xff00) | (data & 0xff00)); + LOG("%s:voice %d, O2(n-2)=%04x\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n2 & 0xffff); break; case 0x06: /* O1(n-1) */ if (ACCESSING_BITS_0_7) voice->o1n1 = (voice->o1n1 & ~0x00ff) | (data & 0x00ff); if (ACCESSING_BITS_8_15) - voice->o1n1 = (int16_t)((voice->o1n1 & ~0xff00) | (data & 0xff00)); - LOG("%s:voice %d, O1(n-1)=%05x (accum=%08x)\n", machine().describe_context(), m_current_page & 0x1f, voice->o2n1 & 0x3ffff, voice->accum); + voice->o1n1 = (s16)((voice->o1n1 & ~0xff00) | (data & 0xff00)); + LOG("%s:voice %d, O1(n-1)=%04x (accum=%08x)\n", machine().describe_context(), m_current_page & 0x1f, voice->o1n1 & 0xffff, get_accum_res(voice->accum)); break; case 0x07: @@ -1874,7 +1905,7 @@ inline void es5505_device::reg_write_high(es550x_voice *voice, offs_t offset, ui } -inline void es5505_device::reg_write_test(es550x_voice *voice, offs_t offset, uint16_t data, uint16_t mem_mask) +inline void es5505_device::reg_write_test(es550x_voice *voice, offs_t offset, u16 data, u16 mem_mask) { switch (offset) { @@ -1889,7 +1920,11 @@ inline void es5505_device::reg_write_test(es550x_voice *voice, offs_t offset, ui break; case 0x08: /* SERMODE */ - m_mode = data & 0x0007; + m_mode |= 0x7f8; // bit 10-3 always 1 + if (ACCESSING_BITS_8_15) + m_mode = (m_mode & ~0xf800) | (data & 0xf800); // MSB[4:0] (unknown purpose) + if (ACCESSING_BITS_0_7) + m_mode = (m_mode & ~0x0007) | (data & 0x0007); // SONY/BB, TEST, A/D break; case 0x09: /* PAR */ @@ -1919,7 +1954,7 @@ inline void es5505_device::reg_write_test(es550x_voice *voice, offs_t offset, ui } -WRITE16_MEMBER( es5505_device::write ) +WRITE16_MEMBER(es5505_device::write) { es550x_voice *voice = &m_voice[m_current_page & 0x1f]; @@ -1945,38 +1980,34 @@ WRITE16_MEMBER( es5505_device::write ) ***********************************************************************************************/ -inline uint16_t es5505_device::reg_read_low(es550x_voice *voice, offs_t offset) +inline u16 es5505_device::reg_read_low(es550x_voice *voice, offs_t offset) { - uint16_t result = 0; + u16 result = 0; switch (offset) { case 0x00: /* CR */ - result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | - ((voice->control & CONTROL_BS0) >> 12) | - ((voice->control & CONTROL_LPMASK) << 2) | - ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) | - 0xf000; + result = voice->control | 0xf000; break; case 0x01: /* FC */ - result = voice->freqcount >> 1; + result = get_accum_res(voice->freqcount, 1); break; case 0x02: /* STRT (hi) */ - result = voice->start >> 18; + result = get_accum_res(voice->start) >> 16; break; case 0x03: /* STRT (lo) */ - result = voice->start >> 2; + result = get_accum_res(voice->start); break; case 0x04: /* END (hi) */ - result = voice->end >> 18; + result = get_accum_res(voice->end) >> 16; break; case 0x05: /* END (lo) */ - result = voice->end >> 2; + result = get_accum_res(voice->end); break; case 0x06: /* K2 */ @@ -1996,11 +2027,11 @@ inline uint16_t es5505_device::reg_read_low(es550x_voice *voice, offs_t offset) break; case 0x0a: /* ACC (hi) */ - result = voice->accum >> 18; + result = get_accum_res(voice->accum) >> 16; break; case 0x0b: /* ACC (lo) */ - result = voice->accum >> 2; + result = get_accum_res(voice->accum); break; case 0x0c: /* unused */ @@ -2012,7 +2043,8 @@ inline uint16_t es5505_device::reg_read_low(es550x_voice *voice, offs_t offset) case 0x0e: /* IRQV */ result = m_irqv; - update_internal_irq_state(); + if (!machine().side_effects_disabled()) + update_internal_irq_state(); break; case 0x0f: /* PAGE */ @@ -2023,38 +2055,34 @@ inline uint16_t es5505_device::reg_read_low(es550x_voice *voice, offs_t offset) } -inline uint16_t es5505_device::reg_read_high(es550x_voice *voice, offs_t offset) +inline u16 es5505_device::reg_read_high(es550x_voice *voice, offs_t offset) { - uint16_t result = 0; + u16 result = 0; switch (offset) { case 0x00: /* CR */ - result = (voice->control & (CONTROL_STOPMASK | CONTROL_LOOPMASK | CONTROL_IRQE | CONTROL_DIR | CONTROL_IRQ)) | - ((voice->control & CONTROL_BS0) >> 12) | - ((voice->control & CONTROL_LPMASK) << 2) | - ((voice->control & (CONTROL_CA0 | CONTROL_CA1)) >> 2) | - 0xf000; + result = voice->control | 0xf000; break; case 0x01: /* O4(n-1) */ - result = voice->o4n1; + result = voice->o4n1 & 0xffff; break; case 0x02: /* O3(n-1) */ - result = voice->o3n1; + result = voice->o3n1 & 0xffff; break; case 0x03: /* O3(n-2) */ - result = voice->o3n2; + result = voice->o3n2 & 0xffff; break; case 0x04: /* O2(n-1) */ - result = voice->o2n1; + result = voice->o2n1 & 0xffff; break; case 0x05: /* O2(n-2) */ - result = voice->o2n2; + result = voice->o2n2 & 0xffff; break; case 0x06: /* O1(n-1) */ @@ -2064,12 +2092,12 @@ inline uint16_t es5505_device::reg_read_high(es550x_voice *voice, offs_t offset) /* want to waste time filtering stopped channels, we just look for a read from */ /* this register on a stopped voice, and return the raw sample data at the */ /* accumulator */ - if ((voice->control & CONTROL_STOPMASK) && m_region_base[voice->control >> 14]) + if ((voice->control & CONTROL_STOPMASK) && m_region_base[get_bank(voice->control)]) { - voice->o1n1 = m_region_base[voice->control >> 14][voice->exbank + (voice->accum >> 11)]; - // logerror("%02x %08x ==> %08x\n",voice->o1n1,voice->control >> 14,voice->exbank + (voice->accum >> 11)); + voice->o1n1 = m_region_base[get_bank(voice->control)][voice->exbank + get_integer_addr(voice->accum)]; + // logerror("%02x %08x ==> %08x\n",voice->o1n1,get_bank(voice->control),voice->exbank + get_integer_addr(voice->accum)); } - result = voice->o1n1; + result = voice->o1n1 & 0xffff; break; case 0x07: @@ -2086,7 +2114,8 @@ inline uint16_t es5505_device::reg_read_high(es550x_voice *voice, offs_t offset) case 0x0e: /* IRQV */ result = m_irqv; - update_internal_irq_state(); + if (!machine().side_effects_disabled()) + update_internal_irq_state(); break; case 0x0f: /* PAGE */ @@ -2097,9 +2126,9 @@ inline uint16_t es5505_device::reg_read_high(es550x_voice *voice, offs_t offset) } -inline uint16_t es5505_device::reg_read_test(es550x_voice *voice, offs_t offset) +inline u16 es5505_device::reg_read_test(es550x_voice *voice, offs_t offset) { - uint16_t result = 0; + u16 result = 0; switch (offset) { @@ -2114,12 +2143,12 @@ inline uint16_t es5505_device::reg_read_test(es550x_voice *voice, offs_t offset) break; case 0x08: /* SERMODE */ - result = m_mode; + result = m_mode | 0x7f8; break; case 0x09: /* PAR */ if (!m_read_port_cb.isnull()) - result = m_read_port_cb(0); + result = m_read_port_cb(0) & 0xffc0; // 10 bit, 15:6 break; /* The following are global, and thus accessible form all pages */ @@ -2129,7 +2158,8 @@ inline uint16_t es5505_device::reg_read_test(es550x_voice *voice, offs_t offset) case 0x0e: /* IRQV */ result = m_irqv; - update_internal_irq_state(); + if (!machine().side_effects_disabled()) + update_internal_irq_state(); break; case 0x0f: /* PAGE */ @@ -2140,10 +2170,10 @@ inline uint16_t es5505_device::reg_read_test(es550x_voice *voice, offs_t offset) } -READ16_MEMBER( es5505_device::read ) +READ16_MEMBER(es5505_device::read) { es550x_voice *voice = &m_voice[m_current_page & 0x1f]; - uint16_t result; + u16 result; LOG("read from %02x/%02x -> ", m_current_page, offset); @@ -2181,10 +2211,6 @@ void es5505_device::voice_bank_w(int voice, int bank) void es550x_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { -} - -void es5506_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ #if ES5506_MAKE_WAVS /* start the logging once we have a sample rate */ if (m_sample_rate) @@ -2204,63 +2230,20 @@ void es5506_device::sound_stream_update(sound_stream &stream, stream_sample_t ** #if ES5506_MAKE_WAVS /* log the raw data */ - if (m_wavraw) { - /* determine left/right source data */ - int32_t *lsrc = m_scratch, *rsrc = m_scratch + length; - int channel; - memset(lsrc, 0, sizeof(int32_t) * length * 2); - /* loop over the output channels */ - for (channel = 0; channel < m_channels; channel++) { - int32_t *l = outputs[(channel << 1)] + offset; - int32_t *r = outputs[(channel << 1) + 1] + offset; - /* add the current channel's samples to the WAV data */ - for (samp = 0; samp < length; samp++) { - lsrc[samp] += l[samp]; - rsrc[samp] += r[samp]; - } - } - wav_add_data_32lr(m_wavraw, lsrc, rsrc, length, 4); - } -#endif - - /* account for these samples */ - offset += length; - samples -= length; - } -} - -void es5505_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ -#if ES5506_MAKE_WAVS - /* start the logging once we have a sample rate */ - if (m_sample_rate) - { - if (!m_wavraw) - m_wavraw = wav_open("raw.wav", m_sample_rate, 2); - } -#endif - - /* loop until all samples are output */ - int offset = 0; - while (samples) - { - int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples; - - generate_samples(outputs, offset, length); - -#if ES5506_MAKE_WAVS - /* log the raw data */ - if (m_wavraw) { + if (m_wavraw) + { /* determine left/right source data */ - int32_t *lsrc = m_scratch, *rsrc = m_scratch + length; + s32 *lsrc = m_scratch, *rsrc = m_scratch + length; int channel; - memset(lsrc, 0, sizeof(int32_t) * length * 2); + memset(lsrc, 0, sizeof(s32) * length * 2); /* loop over the output channels */ - for (channel = 0; channel < m_channels; channel++) { - int32_t *l = outputs[(channel << 1)] + offset; - int32_t *r = outputs[(channel << 1) + 1] + offset; + for (channel = 0; channel < m_channels; channel++) + { + s32 *l = outputs[(channel << 1)] + offset; + s32 *r = outputs[(channel << 1) + 1] + offset; /* add the current channel's samples to the WAV data */ - for (samp = 0; samp < length; samp++) { + for (samp = 0; samp < length; samp++) + { lsrc[samp] += l[samp]; rsrc[samp] += r[samp]; } diff --git a/src/devices/sound/es5506.h b/src/devices/sound/es5506.h index ddf38d19fb9..3228ce44e39 100644 --- a/src/devices/sound/es5506.h +++ b/src/devices/sound/es5506.h @@ -28,41 +28,52 @@ public: auto sample_rate_changed() { return m_sample_rate_changed_cb.bind(); } protected: + enum { + LP3 = 1, + LP4 = 2, + LP_MASK = LP3 | LP4 + }; + + const unsigned FINE_VOLUME_BIT = 16; + const unsigned VOLUME_INTEGER_BIT = 12; + const unsigned VOLUME_INTEGER_SHIFT = FINE_VOLUME_BIT - VOLUME_INTEGER_BIT; + const unsigned VOLUME_ACC_SHIFT = 23 - VOLUME_INTEGER_BIT; + const unsigned ADDRESS_FRAC_BIT = 11; + // struct describing a single playing voice struct es550x_voice { es550x_voice() { } // external state - uint32_t control = 0; // control register - uint32_t freqcount = 0; // frequency count register - uint32_t start = 0; // start register - uint32_t lvol = 0; // left volume register - uint32_t end = 0; // end register - uint32_t lvramp = 0; // left volume ramp register - uint32_t accum = 0; // accumulator register - uint32_t rvol = 0; // right volume register - uint32_t rvramp = 0; // right volume ramp register - uint32_t ecount = 0; // envelope count register - uint32_t k2 = 0; // k2 register - uint32_t k2ramp = 0; // k2 ramp register - uint32_t k1 = 0; // k1 register - uint32_t k1ramp = 0; // k1 ramp register - int32_t o4n1 = 0; // filter storage O4(n-1) - int32_t o3n1 = 0; // filter storage O3(n-1) - int32_t o3n2 = 0; // filter storage O3(n-2) - int32_t o2n1 = 0; // filter storage O2(n-1) - int32_t o2n2 = 0; // filter storage O2(n-2) - int32_t o1n1 = 0; // filter storage O1(n-1) - uint32_t exbank = 0; // external address bank + u32 control = 0; // control register + u64 freqcount = 0; // frequency count register + u64 start = 0; // start register + u32 lvol = 0; // left volume register + u64 end = 0; // end register + u32 lvramp = 0; // left volume ramp register + u64 accum = 0; // accumulator register + u32 rvol = 0; // right volume register + u32 rvramp = 0; // right volume ramp register + u32 ecount = 0; // envelope count register + u32 k2 = 0; // k2 register + u32 k2ramp = 0; // k2 ramp register + u32 k1 = 0; // k1 register + u32 k1ramp = 0; // k1 ramp register + s32 o4n1 = 0; // filter storage O4(n-1) + s32 o3n1 = 0; // filter storage O3(n-1) + s32 o3n2 = 0; // filter storage O3(n-2) + s32 o2n1 = 0; // filter storage O2(n-1) + s32 o2n2 = 0; // filter storage O2(n-2) + s32 o1n1 = 0; // filter storage O1(n-1) + u64 exbank = 0; // external address bank // internal state - uint8_t index = 0; // index of this voice - uint8_t filtcount = 0; // filter count - uint32_t accum_mask = 0; + u8 index = 0; // index of this voice + u8 filtcount = 0; // filter count }; - es550x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + es550x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock); // device-level overrides virtual void device_start() override; @@ -77,32 +88,45 @@ protected: void update_internal_irq_state(); void compute_tables(); - void generate_dummy(es550x_voice *voice, uint16_t *base, int32_t *lbuffer, int32_t *rbuffer, int samples); - void generate_ulaw(es550x_voice *voice, uint16_t *base, int32_t *lbuffer, int32_t *rbuffer, int samples); - void generate_pcm(es550x_voice *voice, uint16_t *base, int32_t *lbuffer, int32_t *rbuffer, int samples); + virtual inline u32 get_bank(u32 control) { return 0; } + virtual inline u32 get_ca(u32 control) { return 0; } + virtual inline u32 get_lp(u32 control) { return 0; } + + inline u32 get_volume(u16 volume) { return m_volume_lookup[volume >> VOLUME_INTEGER_SHIFT]; } + inline unsigned get_accum_shifted_val(unsigned val, int bias = 0) { return val << (m_accum_shift - bias); } + inline unsigned get_accum_res(unsigned val, int bias = 0) { return val >> (m_accum_shift - bias); } + inline u32 get_integer_addr(u32 accum, s32 bias = 0) { return ((accum + (bias << ADDRESS_FRAC_BIT)) & m_accum_mask) >> ADDRESS_FRAC_BIT; } + + inline s32 interpolate(s32 sample1, s32 sample2, u32 accum); + inline void apply_filters(es550x_voice *voice, s32 &sample); + virtual inline void update_envelopes(es550x_voice *voice) {}; + virtual inline void check_for_end_forward(es550x_voice *voice, u32 &accum) {}; + virtual inline void check_for_end_reverse(es550x_voice *voice, u32 &accum) {}; + void generate_dummy(es550x_voice *voice, u16 *base, s32 *lbuffer, s32 *rbuffer, int samples); + void generate_ulaw(es550x_voice *voice, u16 *base, s32 *lbuffer, s32 *rbuffer, int samples); + void generate_pcm(es550x_voice *voice, u16 *base, s32 *lbuffer, s32 *rbuffer, int samples); + inline void generate_irq(es550x_voice *voice, int v); + virtual void generate_samples(s32 **outputs, int offset, int samples) {}; // internal state sound_stream *m_stream; /* which stream are we using */ - int m_sample_rate; /* current sample rate */ - uint16_t * m_region_base[4]; /* pointer to the base of the region */ - uint32_t m_write_latch; /* currently accumulated data for write */ - uint32_t m_read_latch; /* currently accumulated data for read */ - uint32_t m_master_clock; /* master clock frequency */ + int m_sample_rate; /* current sample rate */ + u16 * m_region_base[4]; /* pointer to the base of the region */ + u32 m_master_clock; /* master clock frequency */ + u64 m_accum_shift; + u64 m_accum_mask; - uint8_t m_current_page; /* current register page */ - uint8_t m_active_voices; /* number of active voices */ - uint8_t m_mode; /* MODE register */ - uint8_t m_wst; /* W_ST register */ - uint8_t m_wend; /* W_END register */ - uint8_t m_lrend; /* LR_END register */ - uint8_t m_irqv; /* IRQV register */ + u8 m_current_page; /* current register page */ + u8 m_active_voices; /* number of active voices */ + u16 m_mode; /* MODE register */ + u8 m_irqv; /* IRQV register */ - es550x_voice m_voice[32]; /* the 32 voices */ + es550x_voice m_voice[32]; /* the 32 voices */ - std::unique_ptr<int32_t[]> m_scratch; + std::unique_ptr<s32[]> m_scratch; - std::unique_ptr<int16_t[]> m_ulaw_lookup; - std::unique_ptr<uint16_t[]> m_volume_lookup; + std::unique_ptr<s16[]> m_ulaw_lookup; + std::unique_ptr<u32[]> m_volume_lookup; #if ES5506_MAKE_WAVS void * m_wavraw; /* raw waveform */ @@ -122,30 +146,45 @@ protected: class es5506_device : public es550x_device { public: - es5506_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + es5506_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); ~es5506_device() {} - DECLARE_READ8_MEMBER( read ); - DECLARE_WRITE8_MEMBER( write ); + DECLARE_READ8_MEMBER(read); + DECLARE_WRITE8_MEMBER(write); void voice_bank_w(int voice, int bank); protected: // device-level overrides virtual void device_start() override; + virtual void device_reset() override; - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + const unsigned VOLUME_BIT_ES5506 = 16; + const unsigned VOLUME_SHIFT_ES5506 = FINE_VOLUME_BIT - VOLUME_BIT_ES5506; + const unsigned ADDRESS_FRAC_BIT_ES5506 = ADDRESS_FRAC_BIT; + virtual inline u32 get_bank(u32 control) override { return (control >> 14) & 3; } + virtual inline u32 get_ca(u32 control) override { return (control >> 10) & 7; } + virtual inline u32 get_lp(u32 control) override { return (control >> 8) & LP_MASK; } - void generate_samples(int32_t **outputs, int offset, int samples); + virtual inline void update_envelopes(es550x_voice *voice) override; + virtual inline void check_for_end_forward(es550x_voice *voice, u32 &accum) override; + virtual inline void check_for_end_reverse(es550x_voice *voice, u32 &accum) override; + virtual void generate_samples(s32 **outputs, int offset, int samples) override; private: - inline void reg_write_low(es550x_voice *voice, offs_t offset, uint32_t data); - inline void reg_write_high(es550x_voice *voice, offs_t offset, uint32_t data); - inline void reg_write_test(es550x_voice *voice, offs_t offset, uint32_t data); - inline uint32_t reg_read_low(es550x_voice *voice, offs_t offset); - inline uint32_t reg_read_high(es550x_voice *voice, offs_t offset); - inline uint32_t reg_read_test(es550x_voice *voice, offs_t offset); + inline void reg_write_low(es550x_voice *voice, offs_t offset, u32 data); + inline void reg_write_high(es550x_voice *voice, offs_t offset, u32 data); + inline void reg_write_test(es550x_voice *voice, offs_t offset, u32 data); + inline u32 reg_read_low(es550x_voice *voice, offs_t offset); + inline u32 reg_read_high(es550x_voice *voice, offs_t offset); + inline u32 reg_read_test(es550x_voice *voice, offs_t offset); + + // ES5506 specific registers + u32 m_write_latch; /* currently accumulated data for write */ + u32 m_read_latch; /* currently accumulated data for read */ + u8 m_wst; /* W_ST register */ + u8 m_wend; /* W_END register */ + u8 m_lrend; /* LR_END register */ }; DECLARE_DEVICE_TYPE(ES5506, es5506_device) @@ -154,28 +193,37 @@ DECLARE_DEVICE_TYPE(ES5506, es5506_device) class es5505_device : public es550x_device { public: - es5505_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + es5505_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock); - DECLARE_READ16_MEMBER( read ); - DECLARE_WRITE16_MEMBER( write ); + DECLARE_READ16_MEMBER(read); + DECLARE_WRITE16_MEMBER(write); void voice_bank_w(int voice, int bank); protected: // device-level overrides virtual void device_start() override; - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + const unsigned VOLUME_BIT_ES5505 = 8; + const unsigned VOLUME_SHIFT_ES5505 = FINE_VOLUME_BIT - VOLUME_BIT_ES5505; + const unsigned ADDRESS_FRAC_BIT_ES5505 = 9; + + virtual inline u32 get_lp(u32 control) override { return (control >> 10) & LP_MASK; } + virtual inline u32 get_ca(u32 control) override { return (control >> 8) & 3; } + virtual inline u32 get_bank(u32 control) override { return (control >> 2) & 1; } - void generate_samples(int32_t **outputs, int offset, int samples); + virtual inline void update_envelopes(es550x_voice *voice) override; + virtual inline void check_for_end_forward(es550x_voice *voice, u32 &accum) override; + virtual inline void check_for_end_reverse(es550x_voice *voice, u32 &accum) override; + virtual void generate_samples(s32 **outputs, int offset, int samples) override; private: // internal state - inline void reg_write_low(es550x_voice *voice, offs_t offset, uint16_t data, uint16_t mem_mask); - inline void reg_write_high(es550x_voice *voice, offs_t offset, uint16_t data, uint16_t mem_mask); - inline void reg_write_test(es550x_voice *voice, offs_t offset, uint16_t data, uint16_t mem_mask); - inline uint16_t reg_read_low(es550x_voice *voice, offs_t offset); - inline uint16_t reg_read_high(es550x_voice *voice, offs_t offset); - inline uint16_t reg_read_test(es550x_voice *voice, offs_t offset); + inline void reg_write_low(es550x_voice *voice, offs_t offset, u16 data, u16 mem_mask); + inline void reg_write_high(es550x_voice *voice, offs_t offset, u16 data, u16 mem_mask); + inline void reg_write_test(es550x_voice *voice, offs_t offset, u16 data, u16 mem_mask); + inline u16 reg_read_low(es550x_voice *voice, offs_t offset); + inline u16 reg_read_high(es550x_voice *voice, offs_t offset); + inline u16 reg_read_test(es550x_voice *voice, offs_t offset); }; DECLARE_DEVICE_TYPE(ES5505, es5505_device) |