taito_zm.cpp / zsg2.cpp - more improvements (#3866)

* taito_zm.cpp : Updates
Add DSP, Reduce MCFGs, Add device_mixer_interface for sound gain, Add imperfect_features related to DSP, Add notes

* taito_zm.cpp : Fix TMS57002 clock

* Improve Taito Zoom ZSG-2 sound emulation

zsg2.cpp: implement emphasis filter, this is a noise reduction scheme
that amplifies higher frequncies to reduce quantization noise.

zsg2.cpp: Add sample interpolation and another adjustable lowpass
filter. This seems to be roughly what real hardware does...

zsg2.cpp: Improve panning registers and identify DSP output gain
registers.

* zsg2: minor changes [nw]

zsg2: Register 0b appears to be status flags [nw]

zsg2: Linear ramping probably makes more sense [nw]

* zsg2: slight adjustment of emphasis filter [nw]

* zsg2: slight adjustment of emphasis filter #2 [nw]

* zsg2: more sober ramping algorithm [nw]

* tms57002: add instructions 3c/3d, make them behave as NOP as they're undocumented and not understood

* tms57002: Add dready callback for superctr (nw)

* tms57002: Fixes to make Taito Zoom DSP working

tms57002: Add undocumented instruction saom / raom, they set saturation
mode for the ALU.

tms57002: Implement MACC pipeline.

tms57002: Add callbacks for EMPTY and PC0 pins.

tms57002: Add a few unimplemented instructions.

tms57002: Proper behavior of CMEM UPLOAD mode.

tms57002: Fix an issue where program is not properly loaded if PLOAD is
set after a program has already been written.

* Documentation fix, properly identified registers as ramping control, will implement that soon [nw]

* taito_zm: Working DSP emulation

Pretty much OST quality now. A pretty decent upgrade from how it was
previously, I'd say.

* typo [nw]

* just adding some quick notes about the WIP [nw]

* Fix build [nw]

* zsg2: Proper ramping implemenation, add register map, minor cleanups

* oops [nw]

* taito_zm.cpp / zsg2.cpp - more improvements

zsg2.cpp: Attempt to reduce clicks

zsg2.cpp: Made the emphasis filter much more simple. I think this
 matches hardware, as a filter like this could be implemented with
 very few gates in hardware. Also reset the filter state when
 the sample position reaches the start address, this fixes raycris
 song #9

taito_zm.cpp: Adjust volume balance, hopefully fixing psyvarrv.
 I would need hardware recording for proper verification though

tms57002: forgot MACC pipelining for some instructions
This commit is contained in:
superctr 2018-08-18 22:14:11 +02:00 committed by R. Belmont
parent c07731b3d4
commit 3492e6ace4
5 changed files with 114 additions and 68 deletions

View File

@ -530,7 +530,7 @@ int64_t tms57002_device::macc_to_output_3s(int64_t rounding, uint64_t rmask)
int64_t tms57002_device::check_macc_overflow_0()
{
int64_t m = macc;
int64_t m = macc_read;
uint64_t m1;
// Overflow detection
@ -543,7 +543,7 @@ int64_t tms57002_device::check_macc_overflow_0()
int64_t tms57002_device::check_macc_overflow_1()
{
int64_t m = macc;
int64_t m = macc_read;
uint64_t m1;
// Overflow detection
@ -556,7 +556,7 @@ int64_t tms57002_device::check_macc_overflow_1()
int64_t tms57002_device::check_macc_overflow_2()
{
int64_t m = macc;
int64_t m = macc_read;
uint64_t m1;
// Overflow detection
@ -569,12 +569,12 @@ int64_t tms57002_device::check_macc_overflow_2()
int64_t tms57002_device::check_macc_overflow_3()
{
return macc;
return macc_read;
}
int64_t tms57002_device::check_macc_overflow_0s()
{
int64_t m = macc;
int64_t m = macc_read;
uint64_t m1;
// Overflow detection
@ -591,7 +591,7 @@ int64_t tms57002_device::check_macc_overflow_0s()
int64_t tms57002_device::check_macc_overflow_1s()
{
int64_t m = macc;
int64_t m = macc_read;
uint64_t m1;
// Overflow detection
@ -608,7 +608,7 @@ int64_t tms57002_device::check_macc_overflow_1s()
int64_t tms57002_device::check_macc_overflow_2s()
{
int64_t m = macc;
int64_t m = macc_read;
uint64_t m1;
// Overflow detection
@ -625,7 +625,35 @@ int64_t tms57002_device::check_macc_overflow_2s()
int64_t tms57002_device::check_macc_overflow_3s()
{
return macc;
return macc_read;
}
uint32_t tms57002_device::get_cmem(uint8_t addr)
{
if(sa == addr && update_counter_head != update_counter_tail)
sti |= S_UPDATE;
if(sti & S_UPDATE)
{
cmem[addr] = update[update_counter_tail];
update_counter_tail = (update_counter_tail + 1) & 0x0f;
update_empty();
if(update_counter_head == update_counter_tail)
sti &= ~S_UPDATE;
return cmem[addr]; // The value of crm is ignored during an update.
}
else
{
int crm = (st1 & ST1_CRM) >> ST1_CRM_SHIFT;
uint32_t cvar = cmem[addr];
if(crm == 1)
return (cvar & 0xffff0000);
else if(crm == 2)
return (cvar << 16);
return cvar;
}
}
uint32_t tms57002_device::get_cmem(uint8_t addr)

View File

@ -24,14 +24,14 @@
+00c : xxxxxxxx xxxxxxxx : End address
+00e : xxxxxxxx -------- : DSP ch 2 (Left) output gain
: -------- xxxxxxxx : Loop address (high)
+010 : xxxxxxxx xxxxxxxx : Filter time constant (latch)
+012 : -------- -------- : Unknown register (usually cleared)
+014 : xxxxxxxx xxxxxxxx : Volume (latch)
+016 : --x----- -------- : Key on status flag (only read)
+018 : xxxxxxxx xxxxxxxx : Filter time constant (Ramping target)
+010 : xxxxxxxx xxxxxxxx : Initial filter time constant
+012 : xxxxxxxx xxxxxxxx : Current filter time constant
+014 : xxxxxxxx xxxxxxxx : Initial volume
+016 : xxxxxxxx xxxxxxxx : Current volume
+018 : xxxxxxxx xxxxxxxx : Target filter time constant
+01a : xxxxxxxx -------- : DSP ch 1 (chorus) output gain
: -------- xxxxxxxx : Filter ramping speed
+01c : xxxxxxxx xxxxxxxx : Volume (target)
+01c : xxxxxxxx xxxxxxxx : Target volume
+01e : xxxxxxxx -------- : DSP ch 0 (reverb) output gain
: -------- xxxxxxxx : Filter ramping speed
600-604 : Key on flags (each bit corresponds to a channel)
@ -97,8 +97,11 @@ TODO:
#include <fstream>
#include <cmath>
#define EMPHASIS_CUTOFF_BASE 0x800
#define EMPHASIS_OUTPUT_SHIFT 15
//#define EMPHASIS_CUTOFF_BASE 0x0800
//#define EMPHASIS_INITIAL_BIAS 0x00400000
#define EMPHASIS_INITIAL_BIAS 0
#define EMPHASIS_OUTPUT_SHIFT (16-11)
#define SAMPLE_OUTPUT_SHIFT 12
// device type definition
DEFINE_DEVICE_TYPE(ZSG2, zsg2_device, "zsg2", "ZOOM ZSG-2")
@ -138,13 +141,12 @@ void zsg2_device::device_start()
save_item(NAME(m_read_address));
// Generate the output gain table. Assuming -1dB per step for now.
for (int i = 0, history=0; i < 32; i++)
for (int i = 1; i < 32; i++)
{
double val = pow(10, -(31 - i) / 20.) * 65535.;
gain_tab[i] = val;
gain_tab_frac[i] = val-history;
history = val;
m_gain_tab[i] = val;
}
m_gain_tab[0] = 0;
for (int ch = 0; ch < 48; ch++)
{
@ -175,7 +177,7 @@ void zsg2_device::device_start()
save_item(NAME(m_chan[ch].samples), ch);
}
save_item(NAME(m_sample_count));
}
@ -204,11 +206,11 @@ void zsg2_device::device_reset()
FILE* f;
f = fopen("zoom_samples.bin","wb");
fwrite(m_mem_copy,1,m_mem_blocks*4,f);
fwrite(m_mem_copy.get(),1,m_mem_blocks*4,f);
fclose(f);
f = fopen("zoom_samples.raw","wb");
fwrite(m_full_samples,2,m_mem_blocks*4,f);
fwrite(m_full_samples.get(),2,m_mem_blocks*4,f);
fclose(f);
#endif
}
@ -267,10 +269,8 @@ void zsg2_device::filter_samples(zchan *ch)
{
ch->samples[i+1] = raw_samples[i];
// not sure if the filter works exactly this way, however I am pleased
// with the output for now.
ch->emphasis_filter_state += (raw_samples[i]-(ch->emphasis_filter_state>>16)) * EMPHASIS_CUTOFF_BASE;
ch->samples[i+1] = (ch->emphasis_filter_state) >> EMPHASIS_OUTPUT_SHIFT;
ch->emphasis_filter_state += raw_samples[i]-(ch->emphasis_filter_state>>EMPHASIS_OUTPUT_SHIFT);
ch->samples[i+1] = ch->emphasis_filter_state >> EMPHASIS_OUTPUT_SHIFT;
}
}
@ -285,20 +285,15 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
{
int32_t mix[4] = {};
int ch = 0;
// loop over all channels
for (auto & elem : m_chan)
//auto & elem = m_chan[0];
{
ch++;
if (!(elem.status & STATUS_ACTIVE))
if(~elem.status & STATUS_ACTIVE)
continue;
elem.step_ptr += elem.step;
if (elem.step_ptr & 0x10000)
if (elem.step_ptr & 0xffff0000)
{
elem.step_ptr &= 0xffff;
if (++elem.cur_pos >= elem.end_pos)
{
// loop sample
@ -306,26 +301,30 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
if ((elem.cur_pos + 1) >= elem.end_pos)
{
// end of sample
elem.vol = 0; //this should help the channel allocation just a bit
elem.status &= ~STATUS_ACTIVE;
continue;
}
if(elem.cur_pos == elem.start_pos)
elem.emphasis_filter_state = EMPHASIS_INITIAL_BIAS;
}
elem.step_ptr &= 0xffff;
filter_samples(&elem);
}
uint8_t sample_pos = elem.step_ptr >> 14 & 3;
int32_t sample; // = elem.samples[sample_pos];
int32_t sample = elem.samples[sample_pos];
// linear interpolation (hardware certainly does something similar)
sample = elem.samples[sample_pos];
sample += ((uint16_t)(elem.step_ptr<<2&0xffff) * (int16_t)(elem.samples[sample_pos+1] - sample))>>16;
sample = (sample * elem.vol) >> 16;
// another filter...
elem.output_filter_state += (sample - (elem.output_filter_state>>16)) * elem.output_cutoff;
sample = elem.output_filter_state >> 16;
sample = (sample * elem.vol)>>16;
for(int output=0; output<4; output++)
{
int output_gain = elem.output_gain[output] & 0x1f; // left / right
@ -334,7 +333,7 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
if (elem.output_gain[output] & 0x80) // perhaps ?
output_sample = -output_sample;
mix[output] += (output_sample * gain_tab[output_gain&0x1f]) >> 13;
mix[output] += (output_sample * m_gain_tab[output_gain&0x1f]) >> SAMPLE_OUTPUT_SHIFT;
}
// Apply ramping every other update
@ -346,8 +345,6 @@ void zsg2_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
}
}
ch = 0;
for(int output=0; output<4; output++)
outputs[output][i] = mix[output];
@ -379,7 +376,9 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
break;
case 0x3:
// unknown, always 0x0400
// unknown, always 0x0400. is this a flag?
m_chan[ch].status &= 0x8000;
m_chan[ch].status |= data & 0x7fff;
break;
case 0x4:
@ -412,7 +411,8 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
break;
case 0x9:
// no function? always 0
// writes 0 at key on
m_chan[ch].output_cutoff = data;
break;
case 0xa:
@ -421,8 +421,8 @@ void zsg2_device::chan_w(int ch, int reg, uint16_t data)
break;
case 0xb:
// always writes 0
// this register is read-only
// writes 0 at key on
m_chan[ch].vol = data;
break;
case 0xc:
@ -460,8 +460,16 @@ uint16_t zsg2_device::chan_r(int ch, int reg)
{
switch (reg)
{
case 0xb: // Only later games (taitogn) read this register...
case 0x3:
// no games read from this.
return m_chan[ch].status;
case 0x9:
// pretty certain, though no games actually read from this.
return m_chan[ch].output_cutoff;
case 0xb: // Only later games (taitogn) read this register...
// GNet games use some of the flags to decide which channels to kill when
// all the channels are busy. (take raycris song #23 as an example)
return m_chan[ch].vol;
default:
break;
}
@ -479,14 +487,12 @@ int16_t zsg2_device::get_ramp(uint8_t val)
{
int16_t frac = val<<12; // sign extend
frac = ((frac>>12) ^ 8) << (val >> 4);
return (frac >> 4);
}
inline uint16_t zsg2_device::ramp(uint16_t current, uint16_t target, int16_t delta)
{
int32_t rampval = current + delta;
if(delta < 0 && rampval < target)
rampval = target;
else if(delta >= 0 && rampval > target)
@ -511,12 +517,12 @@ void zsg2_device::control_w(int reg, uint16_t data)
{
int ch = base | i;
m_chan[ch].status |= STATUS_ACTIVE;
m_chan[ch].cur_pos = m_chan[ch].start_pos;
m_chan[ch].step_ptr = 0;
m_chan[ch].emphasis_filter_state = 0;
m_chan[ch].vol = m_chan[ch].vol_initial;
m_chan[ch].cur_pos = m_chan[ch].start_pos - 1;
m_chan[ch].step_ptr = 0x10000;
// Ignoring the "initial volume" for now because it causes lots of clicking
m_chan[ch].vol = 0; // m_chan[ch].vol_initial;
m_chan[ch].vol_delta = 0x0400; // register 06 ?
m_chan[ch].output_cutoff = m_chan[ch].output_cutoff_initial;
filter_samples(&m_chan[ch]);
}
}
break;
@ -531,6 +537,7 @@ void zsg2_device::control_w(int reg, uint16_t data)
if (data & (1 << i))
{
int ch = base | i;
m_chan[ch].vol = 0;
m_chan[ch].status &= ~STATUS_ACTIVE;
}
}
@ -558,6 +565,8 @@ void zsg2_device::control_w(int reg, uint16_t data)
break;
default:
if(reg < 0x20)
m_reg[reg] = data;
logerror("ZSG2 control %02X = %04X\n", reg, data & 0xffff);
break;
}
@ -580,6 +589,8 @@ uint16_t zsg2_device::control_r(int reg)
return read_memory(m_read_address) >> 16;
default:
if(reg < 0x20)
return m_reg[reg];
break;
}

View File

@ -41,13 +41,12 @@ protected:
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
private:
const uint16_t STATUS_ACTIVE = 0x2000;
const uint16_t STATUS_ACTIVE = 0x8000;
// 16 registers per channel, 48 channels
struct zchan
{
uint16_t v[16];
uint16_t status;
uint32_t cur_pos;
uint32_t step_ptr;
@ -68,6 +67,7 @@ private:
int16_t output_cutoff_delta;
int32_t emphasis_filter_state;
int32_t output_filter_state;
// Attenuation for output channels
@ -76,8 +76,8 @@ private:
int16_t samples[5]; // +1 history
};
uint16_t gain_tab[256];
uint16_t gain_tab_frac[256];
uint16_t m_gain_tab[256];
uint16_t m_reg[32];
zchan m_chan[48];
uint32_t m_sample_count;

View File

@ -1,5 +1,5 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert, hap
// copyright-holders:Olivier Galibert, hap, superctr, cam900
/***************************************************************************
Taito Zoom ZSG-2 sound board
@ -18,16 +18,23 @@ Texas Instruments TMS57002DPHA DSP (QFP80)
* 1.5625MHz pin 75 and 2 [25/16] (BCKI) (BCKO)
Newer games have a Panasonic MN1020819DA,
and a Zoom Corp. ZFX-2 DSP instead of the TMS57002.
and a Zoom Corp. ZFX-2 DSP instead of the TMS57002 (Functionally identical).
TODO:
<<<<<<< HEAD
- ZSG-2 sound chip emulation might not be perfect, see zsg2.cpp
- check DSP behavior. The DSP is programmed to expect 16-bit samples, but
the range of the sample values seem really low. Normally the TMS57002 is
supposed to left-shift 16-bit samples, but is this the case here?
=======
- Raycrisis song 9 gets cut off due to clipping. Possible DSP emulation bug,
or just have to change the volumes. in the sound test, it will start to cut
at about 55%. and the timbre doesn't sound right anyway.
- check DSP behavior
- Implement the ramping control registers in zsg2.cpp
>>>>>>> upstream/master
***************************************************************************/
@ -209,8 +216,8 @@ MACHINE_CONFIG_START(taito_zoom_device::device_add_mconfig)
m_tms57002->empty_callback().set_inputline(m_soundcpu, MN10200_IRQ1).invert();
m_tms57002->set_addrmap(AS_DATA, &taito_zoom_device::tms57002_map);
m_tms57002->add_route(2, *this, 1.0, AUTO_ALLOC_INPUT, 0);
m_tms57002->add_route(3, *this, 1.0, AUTO_ALLOC_INPUT, 1);
m_tms57002->add_route(2, *this, 0.5, AUTO_ALLOC_INPUT, 0);
m_tms57002->add_route(3, *this, 0.5, AUTO_ALLOC_INPUT, 1);
#else // Unsupported opcode issue
m_tms57002->set_disable();
#endif
@ -219,11 +226,11 @@ MACHINE_CONFIG_START(taito_zoom_device::device_add_mconfig)
#ifdef USE_DSP
m_zsg2->add_route(0, *m_tms57002, 0.5, 0); // reverb effect
m_zsg2->add_route(1, *m_tms57002, 0.5, 1); // chorus effect
m_zsg2->add_route(2, *m_tms57002, 0.5, 2); // left direct
m_zsg2->add_route(3, *m_tms57002, 0.5, 3); // right direct
m_zsg2->add_route(2, *m_tms57002, 1.0, 2); // left direct
m_zsg2->add_route(3, *m_tms57002, 1.0, 3); // right direct
#else
m_zsg2->add_route(2, *this, 1.0, AUTO_ALLOC_INPUT, 0);
m_zsg2->add_route(3, *this, 1.0, AUTO_ALLOC_INPUT, 1);
m_zsg2->add_route(2, *this, 0.5, AUTO_ALLOC_INPUT, 0);
m_zsg2->add_route(3, *this, 0.5, AUTO_ALLOC_INPUT, 1);
#endif
MACHINE_CONFIG_END

View File

@ -1,5 +1,5 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert, hap
// copyright-holders:Olivier Galibert, hap, superctr, cam900
/***************************************************************************
Taito Zoom ZSG-2 sound board
@ -14,7 +14,7 @@
#include "cpu/tms57002/tms57002.h"
#include "sound/zsg2.h"
#define USE_DSP // Uncomment when DSP emulation is working
#define USE_DSP // comment out this to disable DSP emulation
class taito_zoom_device : public device_t, public device_mixer_interface