summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/c140.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/c140.cpp')
-rw-r--r--src/devices/sound/c140.cpp790
1 files changed, 473 insertions, 317 deletions
diff --git a/src/devices/sound/c140.cpp b/src/devices/sound/c140.cpp
index ca3bdc90977..688ebec6459 100644
--- a/src/devices/sound/c140.cpp
+++ b/src/devices/sound/c140.cpp
@@ -1,100 +1,111 @@
// license:BSD-3-Clause
// copyright-holders:R. Belmont
/*
-C140.c
+
+c140.cpp
Simulator based on AMUSE sources.
The C140 sound chip is used by Namco System 2 and System 21
The 219 ASIC (which incorporates a modified C140) is used by Namco NA-1 and NA-2
This chip controls 24 channels (C140) or 16 (219) of PCM.
16 bytes are associated with each channel.
-Channels can be 8 bit signed PCM, or 12 bit signed PCM.
+Channels can be 8 bit compressed PCM, or 12 bit signed PCM.
+
+2000.06.26: CAB fixed compressed pcm playback
+2002.07.20: R. Belmont added support for multiple banking types
+2006.01.08: R. Belmont added support for NA-1/2 "219" derivative
+2020.05.06: cam900 implemented some features from QuattroPlay sources, by superctr
-TODO: What does the INT0 pin do? Normally Namco tied it to VOL0 (with VOL1 = VCC).
+TODO:
+- What does the INT0 pin do? Normally Namco tied it to VOL0 (with VOL1 = VCC).
+- Acknowledge A9 bit (9th address bit) of host interface
+- Verify data bus bits of C219
+- Verify C219 LFSR algorithm (same as c352.cpp?)
+- Verify unknown mode bits (0x40 for C140, 0x02 for C219)
--------------
- ASIC "219" notes
+ASIC "219" notes
- On the 219 ASIC used on NA-1 and NA-2, the high registers have the following
- meaning instead:
- 0x1f7: bank for voices 0-3
- 0x1f1: bank for voices 4-7
- 0x1f3: bank for voices 8-11
- 0x1f5: bank for voices 12-15
+On the 219 ASIC used on NA-1 and NA-2, the high registers have the following
+meaning instead:
+0x1f7: bank for voices 0-3
+0x1f1: bank for voices 4-7
+0x1f3: bank for voices 8-11
+0x1f5: bank for voices 12-15
- Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers
- mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15.
+Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers
+mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15.
- Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2.
- Additionally, the 219's base pitch is the same as the C352's (42667). But these changes
- are IMO not sufficient to make this a separate file - all the other registers are
- fully compatible.
+Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2.
+Additionally, the 219's base pitch is the same as the C352's (42667). But these changes
+are IMO not sufficient to make this a separate file - all the other registers are
+fully compatible.
- Finally, the 219 only has 16 voices.
-*/
-/*
- 2000.06.26 CAB fixed compressed pcm playback
- 2002.07.20 R. Belmont added support for multiple banking types
- 2006.01.08 R. Belmont added support for NA-1/2 "219" derivative
-*/
+Finally, the 219 only has 16 voices.
+*/
#include "emu.h"
#include "c140.h"
+#include <algorithm>
+
struct voice_registers
{
- uint8_t volume_right;
- uint8_t volume_left;
- uint8_t frequency_msb;
- uint8_t frequency_lsb;
- uint8_t bank;
- uint8_t mode;
- uint8_t start_msb;
- uint8_t start_lsb;
- uint8_t end_msb;
- uint8_t end_lsb;
- uint8_t loop_msb;
- uint8_t loop_lsb;
- uint8_t reserved[4];
+ u8 volume_right;
+ u8 volume_left;
+ u8 frequency_msb;
+ u8 frequency_lsb;
+ u8 bank;
+ u8 mode;
+ u8 start_msb;
+ u8 start_lsb;
+ u8 end_msb;
+ u8 end_lsb;
+ u8 loop_msb;
+ u8 loop_lsb;
+ u8 reserved[4];
};
// device type definition
DEFINE_DEVICE_TYPE(C140, c140_device, "c140", "Namco C140")
+DEFINE_DEVICE_TYPE(C219, c219_device, "c219", "Namco C219")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
-static inline int limit(int32_t in)
-{
- if(in>0x7fff) return 0x7fff;
- else if(in<-0x8000) return -0x8000;
- return in;
-}
-
-
//-------------------------------------------------
// c140_device - constructor
//-------------------------------------------------
c140_device::c140_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, C140, tag, owner, clock)
+ : c140_device(mconfig, C140, tag, owner, clock)
+{
+}
+
+c140_device::c140_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)
- , device_rom_interface(mconfig, *this, 21)
+ , device_rom_interface(mconfig, *this)
, m_int1_callback(*this)
, m_sample_rate(0)
, m_stream(nullptr)
- , m_banking_type(C140_TYPE::SYSTEM2)
, m_mixer_buffer_left(nullptr)
, m_mixer_buffer_right(nullptr)
, m_baserate(0)
{
- memset(m_REG, 0, sizeof(uint8_t)*0x200);
- memset(m_pcmtbl, 0, sizeof(int16_t)*8);
+ std::fill(std::begin(m_REG), std::end(m_REG), 0);
+ std::fill(std::begin(m_pcmtbl), std::end(m_pcmtbl), 0);
+}
+
+c219_device::c219_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : c140_device(mconfig, C219, tag, owner, clock)
+{
+ // TODO: unknown address bus bits
}
@@ -106,51 +117,81 @@ void c140_device::device_start()
{
m_sample_rate = m_baserate = clock();
- m_int1_callback.resolve_safe();
- m_int1_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(c140_device::int1_on), this));
+ m_int1_timer = timer_alloc(FUNC(c140_device::int1_on), this);
m_stream = stream_alloc(0, 2, m_sample_rate);
- /* make decompress pcm table */ //2000.06.26 CAB
- int32_t segbase = 0;
- for(int i = 0; i < 8; i++)
+ // make decompress pcm table (Verified from Wii Virtual Console Arcade Starblade)
+ for (int i = 0; i < 256; i++)
{
- m_pcmtbl[i]=segbase; //segment base value
- segbase += 16<<i;
+ int j = (s8)i;
+ s8 s1 = j & 7;
+ s8 s2 = abs(j >> 3) & 31;
+
+ m_pcmtbl[i] = 0x80 << s1 & 0xff00;
+ m_pcmtbl[i] += s2 << (s1 ? s1 + 3 : 4);
+
+ if (j < 0)
+ m_pcmtbl[i] = -m_pcmtbl[i];
}
- memset(m_REG,0,sizeof(m_REG));
+ std::fill(std::begin(m_REG), std::end(m_REG), 0);
- for(int i = 0; i < MAX_VOICE; i++)
+ for (int i = 0; i < MAX_VOICE; i++)
{
init_voice(&m_voi[i]);
}
/* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */
- m_mixer_buffer_left = std::make_unique<int16_t[]>(m_sample_rate);
- m_mixer_buffer_right = std::make_unique<int16_t[]>(m_sample_rate);
+ m_mixer_buffer_left = std::make_unique<s16[]>(m_sample_rate);
+ m_mixer_buffer_right = std::make_unique<s16[]>(m_sample_rate);
save_item(NAME(m_REG));
- for (int i = 0; i < MAX_VOICE; i++)
+ save_item(STRUCT_MEMBER(m_voi, ptoffset));
+ save_item(STRUCT_MEMBER(m_voi, pos));
+ save_item(STRUCT_MEMBER(m_voi, key));
+ save_item(STRUCT_MEMBER(m_voi, lastdt));
+ save_item(STRUCT_MEMBER(m_voi, prevdt));
+ save_item(STRUCT_MEMBER(m_voi, dltdt));
+ save_item(STRUCT_MEMBER(m_voi, rvol));
+ save_item(STRUCT_MEMBER(m_voi, lvol));
+ save_item(STRUCT_MEMBER(m_voi, frequency));
+ save_item(STRUCT_MEMBER(m_voi, bank));
+ save_item(STRUCT_MEMBER(m_voi, mode));
+ save_item(STRUCT_MEMBER(m_voi, sample_start));
+ save_item(STRUCT_MEMBER(m_voi, sample_end));
+ save_item(STRUCT_MEMBER(m_voi, sample_loop));
+}
+
+void c219_device::device_start()
+{
+ c140_device::device_start();
+
+ // generate mulaw table (Verified from Wii Virtual Console Arcade Knuckle Heads)
+ // same as c352.cpp
+ int j = 0;
+ for (int i = 0; i < 128; i++)
{
- save_item(NAME(m_voi[i].ptoffset), i);
- save_item(NAME(m_voi[i].pos), i);
- save_item(NAME(m_voi[i].key), i);
- save_item(NAME(m_voi[i].lastdt), i);
- save_item(NAME(m_voi[i].prevdt), i);
- save_item(NAME(m_voi[i].dltdt), i);
- save_item(NAME(m_voi[i].rvol), i);
- save_item(NAME(m_voi[i].lvol), i);
- save_item(NAME(m_voi[i].frequency), i);
- save_item(NAME(m_voi[i].bank), i);
- save_item(NAME(m_voi[i].mode), i);
- save_item(NAME(m_voi[i].sample_start), i);
- save_item(NAME(m_voi[i].sample_end), i);
- save_item(NAME(m_voi[i].sample_loop), i);
+ m_pcmtbl[i] = j << 5;
+ if (i < 16)
+ j += 1;
+ else if (i < 24)
+ j += 2;
+ else if (i < 48)
+ j += 4;
+ else if (i < 100)
+ j += 8;
+ else
+ j += 16;
}
-}
+ for (int i = 0; i < 128; i++)
+ m_pcmtbl[i + 128] = (~m_pcmtbl[i]) & 0xffe0;
+ m_lfsr = 0x1234;
+
+ save_item(NAME(m_lfsr));
+}
void c140_device::device_clock_changed()
{
@@ -159,12 +200,12 @@ void c140_device::device_clock_changed()
m_stream->set_sample_rate(m_sample_rate);
/* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */
- m_mixer_buffer_left = std::make_unique<int16_t[]>(m_sample_rate);
- m_mixer_buffer_right = std::make_unique<int16_t[]>(m_sample_rate);;
+ m_mixer_buffer_left = std::make_unique<s16[]>(m_sample_rate);
+ m_mixer_buffer_right = std::make_unique<s16[]>(m_sample_rate);
}
-void c140_device::rom_bank_updated()
+void c140_device::rom_bank_pre_change()
{
m_stream->update();
}
@@ -174,183 +215,232 @@ void c140_device::rom_bank_updated()
// sound_stream_update - handle a stream update
//-------------------------------------------------
-void c140_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+void c140_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
- int i,j;
-
- int32_t rvol,lvol;
- int32_t dt;
- int32_t sdt;
- int32_t st,ed,sz;
-
- long sampleData;
- int32_t frequency,delta,offset,pos;
- int32_t cnt, voicecnt;
- int32_t lastdt,prevdt,dltdt;
- float pbase=(float)m_baserate*2.0f / (float)m_sample_rate;
+ float pbase = (float)m_baserate * 2.0f / (float)m_sample_rate;
+ s16 *lmix, *rmix;
- int16_t *lmix, *rmix;
-
- if(samples>m_sample_rate) samples=m_sample_rate;
+ int samples = outputs[0].samples();
+ if (samples > m_sample_rate) samples = m_sample_rate;
/* zap the contents of the mixer buffer */
- memset(m_mixer_buffer_left.get(), 0, samples * sizeof(int16_t));
- memset(m_mixer_buffer_right.get(), 0, samples * sizeof(int16_t));
-
- /* get the number of voices to update */
- voicecnt = (m_banking_type == C140_TYPE::ASIC219) ? 16 : 24;
+ std::fill_n(&m_mixer_buffer_left[0], samples, 0);
+ std::fill_n(&m_mixer_buffer_right[0], samples, 0);
//--- audio update
- for( i=0;i<voicecnt;i++ )
+ for (int i = 0; i < 24; i++)
{
C140_VOICE *v = &m_voi[i];
- const struct voice_registers *vreg = (struct voice_registers *)&m_REG[i*16];
+ const struct voice_registers *vreg = (struct voice_registers *)&m_REG[i * 16];
- if( v->key )
+ if (v->key)
{
- frequency= vreg->frequency_msb*256 + vreg->frequency_lsb;
+ const u16 frequency = (vreg->frequency_msb << 8) | vreg->frequency_lsb;
/* Abort voice if no frequency value set */
- if(frequency==0) continue;
+ if (frequency == 0) continue;
/* Delta = frequency * ((8MHz/374)*2 / sample rate) */
- delta=(long)((float)frequency * pbase);
+ const int delta = (int)((float)frequency * pbase);
/* Calculate left/right channel volumes */
- lvol=(vreg->volume_left*32)/MAX_VOICE; //32ch -> 24ch
- rvol=(vreg->volume_right*32)/MAX_VOICE;
+ const int lvol = (vreg->volume_left * 32) / MAX_VOICE; //32ch -> 24ch
+ const int rvol = (vreg->volume_right * 32) / MAX_VOICE;
/* Set mixer outputs base pointers */
lmix = m_mixer_buffer_left.get();
rmix = m_mixer_buffer_right.get();
/* Retrieve sample start/end and calculate size */
- st=v->sample_start;
- ed=v->sample_end;
- sz=ed-st;
+ const int st = v->sample_start;
+ const int ed = v->sample_end;
+ const int sz = ed - st;
/* Retrieve base pointer to the sample data */
- sampleData = find_sample(st, v->bank, i);
+ const int sampleData = find_sample(st, v->bank, i);
/* Fetch back previous data pointers */
- offset=v->ptoffset;
- pos=v->pos;
- lastdt=v->lastdt;
- prevdt=v->prevdt;
- dltdt=v->dltdt;
-
- /* Switch on data type - compressed PCM is only for C140 */
- if ((v->mode&8) && (m_banking_type != C140_TYPE::ASIC219))
+ int offset = v->ptoffset;
+ int pos = v->pos;
+ s32 lastdt = v->lastdt;
+ s32 prevdt = v->prevdt;
+ s32 dltdt = v->dltdt;
+
+ /* linear or compressed 12bit signed PCM */
+ for (int j = 0; j < samples; j++)
{
- //compressed PCM (maybe correct...)
- /* Loop for enough to fill sample buffer as requested */
- for(j=0;j<samples;j++)
+ offset += delta;
+ const int cnt = (offset >> 16) & 0x7fff;
+ offset &= 0xffff;
+ pos += cnt;
+ /* Check for the end of the sample */
+ if (pos >= sz)
{
- offset += delta;
- cnt = (offset>>16)&0x7fff;
- offset &= 0xffff;
- pos+=cnt;
- //for(;cnt>0;cnt--)
+ /* Check if its a looping sample, either stop or loop */
+ if (ch_looped(v))
{
- /* Check for the end of the sample */
- if(pos >= sz)
- {
- /* Check if its a looping sample, either stop or loop */
- if(v->mode&0x10)
- {
- pos = (v->sample_loop - st);
- }
- else
- {
- v->key=0;
- break;
- }
- }
-
- /* Read the chosen sample byte */
- dt = (int8_t) read_byte(sampleData + pos);
-
- /* decompress to 13bit range */ //2000.06.26 CAB
- sdt=dt>>3; //signed
- if(sdt<0) sdt = (sdt<<(dt&7)) - m_pcmtbl[dt&7];
- else sdt = (sdt<<(dt&7)) + m_pcmtbl[dt&7];
-
- prevdt=lastdt;
- lastdt=sdt;
- dltdt=(lastdt - prevdt);
+ pos = (v->sample_loop - st);
}
+ else
+ {
+ v->key = 0;
+ break;
+ }
+ }
- /* Caclulate the sample value */
- dt=((dltdt*offset)>>16)+prevdt;
-
- /* Write the data to the sample buffers */
- *lmix++ +=(dt*lvol)>>(5+5);
- *rmix++ +=(dt*rvol)>>(5+5);
+ if (cnt)
+ {
+ const u16 sample = read_word((sampleData + pos) << 1) & 0xfff0; // 12bit
+ prevdt = lastdt;
+ lastdt = ((ch_mulaw(v)) ? m_pcmtbl[(sample >> 8) & 0xff] : s16(sample)) >> 4;
+ dltdt = (lastdt - prevdt);
}
+
+ /* Caclulate the sample value */
+ s32 dt = ((dltdt * offset) >> 16) + prevdt;
+
+ /* Write the data to the sample buffers */
+ *lmix++ += (dt * lvol) >> (5 + 4);
+ *rmix++ += (dt * rvol) >> (5 + 4);
}
- else
+
+ /* Save positional data for next callback */
+ v->ptoffset = offset;
+ v->pos = pos;
+ v->lastdt = lastdt;
+ v->prevdt = prevdt;
+ v->dltdt = dltdt;
+ }
+ }
+
+ /* render to MAME's stream buffer */
+ lmix = m_mixer_buffer_left.get();
+ rmix = m_mixer_buffer_right.get();
+ {
+ auto &dest1 = outputs[0];
+ auto &dest2 = outputs[1];
+ for (int i = 0; i < samples; i++)
+ {
+ dest1.put_int_clamp(i, *lmix++, 32768 / 8);
+ dest2.put_int_clamp(i, *rmix++, 32768 / 8);
+ }
+ }
+}
+
+void c219_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
+{
+ float pbase = (float)m_baserate * 2.0f / (float)m_sample_rate;
+ s16 *lmix, *rmix;
+
+ int samples = outputs[0].samples();
+ if (samples > m_sample_rate) samples = m_sample_rate;
+
+ /* zap the contents of the mixer buffer */
+ std::fill_n(&m_mixer_buffer_left[0], samples, 0);
+ std::fill_n(&m_mixer_buffer_right[0], samples, 0);
+
+ //--- audio update
+ for (int i = 0; i < 16; i++)
+ {
+ C140_VOICE *v = &m_voi[i];
+ const struct voice_registers *vreg = (struct voice_registers *)&m_REG[i * 16];
+
+ if (v->key)
+ {
+ const u16 frequency = (vreg->frequency_msb << 8) | vreg->frequency_lsb;
+
+ /* Abort voice if no frequency value set */
+ if (frequency == 0) continue;
+
+ /* Delta = frequency * ((8MHz/374)*2 / sample rate) */
+ const int delta = (int)((float)frequency * pbase);
+
+ /* Calculate left/right channel volumes */
+ const int lvol = (vreg->volume_left * 32) / MAX_VOICE; //32ch -> 24ch
+ const int rvol = (vreg->volume_right * 32) / MAX_VOICE;
+
+ /* Set mixer outputs base pointers */
+ lmix = m_mixer_buffer_left.get();
+ rmix = m_mixer_buffer_right.get();
+
+ /* Retrieve sample start/end and calculate size */
+ const int st = v->sample_start;
+ const int ed = v->sample_end;
+ const int sz = ed - st;
+
+ /* Retrieve base pointer to the sample data */
+ const int sampleData = find_sample(st, v->bank, i);
+
+ /* Fetch back previous data pointers */
+ int offset = v->ptoffset;
+ int pos = v->pos;
+ s32 lastdt = v->lastdt;
+ s32 prevdt = v->prevdt;
+ s32 dltdt = v->dltdt;
+
+ /* linear or compressed 8bit signed PCM */
+ for (int j = 0; j < samples; j++)
{
- /* linear 8bit signed PCM */
- for(j=0;j<samples;j++)
+ offset += delta;
+ const int cnt = (offset >> 16) & 0x7fff;
+ offset &= 0xffff;
+ pos += cnt;
+ /* Check for the end of the sample */
+ if (pos >= sz)
{
- offset += delta;
- cnt = (offset>>16)&0x7fff;
- offset &= 0xffff;
- pos += cnt;
- /* Check for the end of the sample */
- if(pos >= sz)
+ /* Check if its a looping sample, either stop or loop */
+ if (ch_looped(v) || ch_noise(v))
{
- /* Check if its a looping sample, either stop or loop */
- if( v->mode&0x10 )
- {
- pos = (v->sample_loop - st);
- }
- else
- {
- v->key=0;
- break;
- }
+ pos = (v->sample_loop - st);
}
-
- if( cnt )
+ else
{
- prevdt=lastdt;
-
- if (m_banking_type == C140_TYPE::ASIC219)
- {
- lastdt = (int8_t) read_byte(sampleData + BYTE_XOR_BE(pos));
+ v->key = 0;
+ break;
+ }
+ }
- // Sign + magnitude format
- if ((v->mode & 0x01) && (lastdt & 0x80))
- lastdt = -(lastdt & 0x7f);
+ const int shift = ch_noise(v) ? 8 : 3;
+ if (cnt)
+ {
+ prevdt = lastdt;
- // Sign flip
- if (v->mode & 0x40)
- lastdt = -lastdt;
- }
+ if (ch_noise(v)) // noise
+ {
+ m_lfsr = (m_lfsr >> 1) ^ ((-(m_lfsr & 1)) & 0xfff6);
+ lastdt = s16(m_lfsr);
+ }
+ else
+ {
+ lastdt = s8(read_byte(sampleData + pos));
+ // 11 bit mulaw
+ if (ch_mulaw(v))
+ lastdt = m_pcmtbl[lastdt & 0xff] >> 5;
else
- {
- lastdt = (int8_t) read_byte(sampleData + pos);
- }
-
- dltdt = (lastdt - prevdt);
+ lastdt <<= 3; // scale as 11bit
}
- /* Caclulate the sample value */
- dt=((dltdt*offset)>>16)+prevdt;
+ // Sign flip
+ if (ch_inv_sign(v))
+ lastdt = -lastdt;
- /* Write the data to the sample buffers */
- *lmix++ +=(dt*lvol)>>5;
- *rmix++ +=(dt*rvol)>>5;
+ dltdt = (lastdt - prevdt);
}
+
+ /* Caclulate the sample value */
+ s32 dt = ((dltdt * offset) >> 16) + prevdt;
+
+ /* Write the data to the sample buffers */
+ *lmix++ += ((ch_inv_lout(v)) ? -(dt * lvol) : (dt * lvol)) >> (5 + shift);
+ *rmix++ += (dt * rvol) >> (5 + shift);
}
/* Save positional data for next callback */
- v->ptoffset=offset;
- v->pos=pos;
- v->lastdt=lastdt;
- v->prevdt=prevdt;
- v->dltdt=dltdt;
+ v->ptoffset = offset;
+ v->pos = pos;
+ v->lastdt = lastdt;
+ v->prevdt = prevdt;
+ v->dltdt = dltdt;
}
}
@@ -358,25 +448,44 @@ void c140_device::sound_stream_update(sound_stream &stream, stream_sample_t **in
lmix = m_mixer_buffer_left.get();
rmix = m_mixer_buffer_right.get();
{
- stream_sample_t *dest1 = outputs[0];
- stream_sample_t *dest2 = outputs[1];
- for (i = 0; i < samples; i++)
+ auto &dest1 = outputs[0];
+ auto &dest2 = outputs[1];
+ for (int i = 0; i < samples; i++)
{
- int32_t val;
-
- val = 8 * (*lmix++);
- *dest1++ = limit(val);
- val = 8 * (*rmix++);
- *dest2++ = limit(val);
+ dest1.put_int_clamp(i, *lmix++, 32768 / 8);
+ dest2.put_int_clamp(i, *rmix++, 32768 / 8);
}
}
}
+inline u8 c140_device::keyon_status_read(u16 offset)
+{
+ m_stream->update();
+ C140_VOICE const &v = m_voi[offset >> 4];
+
+ // suzuka 8 hours and final lap games read from here, expecting bit 6 to be an in-progress sample flag.
+ // four trax also expects bit 4 high for some specific channels to make engine noises to work properly
+ // (sounds kinda bogus when player crashes in an object and jump spin, needs real HW verification)
+ return (v.key ? 0x40 : 0x00) | (m_REG[offset] & 0x3f);
+}
+
u8 c140_device::c140_r(offs_t offset)
{
- offset&=0x1ff;
- return m_REG[offset];
+ offset &= 0x1ff;
+ u8 data = m_REG[offset];
+
+ if ((offset & 0xf) == 0x5 && offset < 0x180)
+ {
+ data = keyon_status_read(offset);
+ }
+ else if (offset == 0x1f8)
+ {
+ // timer reload value = written reg data + 1
+ data++;
+ }
+
+ return data;
}
@@ -384,143 +493,190 @@ void c140_device::c140_w(offs_t offset, u8 data)
{
m_stream->update();
- offset&=0x1ff;
-
- // mirror the bank registers on the 219, fixes bkrtmaq (and probably xday2 based on notes in the HLE)
- if ((offset >= 0x1f8) && BIT(offset, 0) && (m_banking_type == C140_TYPE::ASIC219))
- {
- offset -= 8;
- }
+ offset &= 0x1ff;
- m_REG[offset]=data;
- if( offset<0x180 )
+ m_REG[offset] = data;
+ if (offset < 0x180)
{
- C140_VOICE *v = &m_voi[offset>>4];
+ const u8 ch = offset >> 4;
+ C140_VOICE *v = &m_voi[ch];
- if( (offset&0xf)==0x5 )
+ if ((offset & 0xf) == 0x5)
{
- if( data&0x80 )
+ if (data & 0x80)
{
- const struct voice_registers *vreg = (struct voice_registers *) &m_REG[offset&0x1f0];
- v->key=1;
- v->ptoffset=0;
- v->pos=0;
- v->lastdt=0;
- v->prevdt=0;
- v->dltdt=0;
+ const struct voice_registers *vreg = (struct voice_registers *) &m_REG[offset & 0x1f0];
+ v->key = 1;
+ v->ptoffset = 0;
+ v->pos = 0;
+ v->lastdt = 0;
+ v->prevdt = 0;
+ v->dltdt = 0;
v->bank = vreg->bank;
v->mode = data;
- // on the 219 asic, addresses are in words
- if (m_banking_type == C140_TYPE::ASIC219)
- {
- v->sample_loop = (vreg->loop_msb*256 + vreg->loop_lsb)*2;
- v->sample_start = (vreg->start_msb*256 + vreg->start_lsb)*2;
- v->sample_end = (vreg->end_msb*256 + vreg->end_lsb)*2;
-
- #if 0
- logerror("219: play v %d mode %02x start %x loop %x end %x\n",
- offset>>4, v->mode,
- find_sample(v->sample_start, v->bank, offset>>4),
- find_sample(v->sample_loop, v->bank, offset>>4),
- find_sample(v->sample_end, v->bank, offset>>4));
- #endif
- }
- else
- {
- v->sample_loop = vreg->loop_msb*256 + vreg->loop_lsb;
- v->sample_start = vreg->start_msb*256 + vreg->start_lsb;
- v->sample_end = vreg->end_msb*256 + vreg->end_lsb;
- }
+ const u32 loop = (vreg->loop_msb << 8) + vreg->loop_lsb;
+ const u32 start = (vreg->start_msb << 8) + vreg->start_lsb;
+ const u32 end = (vreg->end_msb << 8) + vreg->end_lsb;
+ v->sample_loop = loop;
+ v->sample_start = start;
+ v->sample_end = end;
}
else
{
- v->key=0;
+ v->key = 0;
}
}
}
- else if (offset == 0x1fa)
+
+ else switch (offset)
+ {
+ // timer reload value
+ case 0x1f8:
+ break;
+
+ // set INT1 timer
+ case 0x1fa:
+ m_int1_callback(CLEAR_LINE);
+
+ if (BIT(m_REG[0x1fe], 0))
+ m_int1_timer->adjust(attotime::from_ticks((m_REG[0x1f8] + 1) * 2, m_baserate));
+
+ break;
+
+ // enable INT1 timer
+ case 0x1fe:
+ if (BIT(data, 0))
+ {
+ // kyukaidk and marvlandj want the first interrupt to happen immediately
+ if (m_int1_timer->expire().is_never())
+ m_int1_callback(ASSERT_LINE);
+ }
+ else
+ {
+ m_int1_callback(CLEAR_LINE);
+ m_int1_timer->adjust(attotime::never);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+u8 c219_device::c219_r(offs_t offset)
+{
+ offset &= 0x1ff;
+
+ // TODO: what happens here on reading unmapped voice regs?
+ u8 data = m_REG[offset];
+
+ if ((offset & 0xf) == 0x5 && offset < 0x100)
{
- m_int1_callback(CLEAR_LINE);
+ // assume same as c140
+ data = keyon_status_read(offset);
+ }
+
+ return data;
+}
+
- // timing not verified
- unsigned div = m_REG[0x1f8] != 0 ? m_REG[0x1f8] : 256;
- attotime interval = attotime::from_ticks(div * 2, m_baserate);
- if (BIT(m_REG[0x1fe], 0))
- m_int1_timer->adjust(interval);
+void c219_device::c219_w(offs_t offset, u8 data)
+{
+ m_stream->update();
+
+ offset &= 0x1ff;
+
+ // mirror the bank registers on the 219, fixes bkrtmaq (and probably xday2 based on notes in the HLE)
+ if ((offset >= 0x1f8) && BIT(offset, 0))
+ {
+ offset -= 8;
}
- else if (offset == 0x1fe)
+
+ m_REG[offset] = data;
+ if (offset < 0x100) // only 16 voices
{
- if (BIT(data, 0))
- {
- // kyukaidk and marvlandj want the first interrupt to happen immediately
- if (!m_int1_timer->enabled())
- m_int1_callback(ASSERT_LINE);
- }
- else
+ const u8 ch = offset >> 4;
+ C140_VOICE *v = &m_voi[ch];
+
+ if ((offset & 0xf) == 0x5)
{
- m_int1_callback(CLEAR_LINE);
- m_int1_timer->enable(false);
+ if (data & 0x80)
+ {
+ const struct voice_registers *vreg = (struct voice_registers *) &m_REG[offset & 0x1f0];
+ v->key = 1;
+ v->ptoffset = 0;
+ v->pos = 0;
+ v->lastdt = 0;
+ v->prevdt = 0;
+ v->dltdt = 0;
+ v->bank = vreg->bank;
+ v->mode = data;
+
+ const u32 loop = (vreg->loop_msb << 8) + vreg->loop_lsb;
+ const u32 start = (vreg->start_msb << 8) + vreg->start_lsb;
+ const u32 end = (vreg->end_msb << 8) + vreg->end_lsb;
+ // on the 219 asic, addresses are in words
+ v->sample_loop = loop << 1;
+ v->sample_start = start << 1;
+ v->sample_end = end << 1;
+
+ #if 0
+ logerror("219: play v %d mode %02x start %x loop %x end %x\n",
+ ch, v->mode,
+ find_sample(v->sample_start, v->bank, ch),
+ find_sample(v->sample_loop, v->bank, ch),
+ find_sample(v->sample_end, v->bank, ch));
+ #endif
+ }
+ else
+ {
+ v->key = 0;
+ }
}
}
+ // TODO: No interrupt/timers?
}
-
TIMER_CALLBACK_MEMBER(c140_device::int1_on)
{
m_int1_callback(ASSERT_LINE);
}
-void c140_device::init_voice( C140_VOICE *v )
+void c140_device::init_voice(C140_VOICE *v)
{
- v->key=0;
- v->ptoffset=0;
- v->rvol=0;
- v->lvol=0;
- v->frequency=0;
- v->bank=0;
- v->mode=0;
- v->sample_start=0;
- v->sample_end=0;
- v->sample_loop=0;
+ v->key = 0;
+ v->ptoffset = 0;
+ v->rvol = 0;
+ v->lvol = 0;
+ v->frequency = 0;
+ v->bank = 0;
+ v->mode = 0;
+ v->sample_start = 0;
+ v->sample_end = 0;
+ v->sample_loop = 0;
}
/*
find_sample: compute the actual address of a sample given it's
- address and banking registers, as well as the board type.
-
- I suspect in "real life" this works like the Sega MultiPCM where the banking
- is done by a small PAL or GAL external to the sound chip, which can be switched
- per-game or at least per-PCB revision as addressing range needs grow.
+ address and banking registers, as well as the chip type.
*/
-long c140_device::find_sample(long adrs, long bank, int voice)
+int c140_device::find_sample(int adrs, int bank, int voice)
{
- long newadr = 0;
-
- static const int16_t asic219banks[4] = { 0x1f7, 0x1f1, 0x1f3, 0x1f5 };
+ adrs = (bank << 16) + adrs;
- adrs=(bank<<16)+adrs;
-
- switch (m_banking_type)
- {
- case C140_TYPE::SYSTEM2:
- // System 2 banking
- newadr = ((adrs&0x200000)>>2)|(adrs&0x7ffff);
- break;
+ return adrs;
+}
- case C140_TYPE::SYSTEM21:
- // System 21 banking.
- // similar to System 2's.
- newadr = ((adrs&0x300000)>>1)+(adrs&0x7ffff);
- break;
+int c219_device::find_sample(int adrs, int bank, int voice)
+{
+ static const s16 asic219banks[4] = { 0x1f7, 0x1f1, 0x1f3, 0x1f5 };
- case C140_TYPE::ASIC219:
- // ASIC219's banking is fairly simple
- newadr = ((m_REG[asic219banks[voice/4]]&0x3) * 0x20000) + adrs;
- break;
- }
+ adrs = (bank << 16) + adrs;
- return (newadr);
+ // ASIC219's banking is fairly simple
+ return ((m_REG[asic219banks[voice / 4]] & 0x3) * 0x20000) + adrs;
}