diff options
Diffstat (limited to 'src/devices/sound/c140.cpp')
-rw-r--r-- | src/devices/sound/c140.cpp | 789 |
1 files changed, 468 insertions, 321 deletions
diff --git a/src/devices/sound/c140.cpp b/src/devices/sound/c140.cpp index cb3288e4fd6..9c83dac32db 100644 --- a/src/devices/sound/c140.cpp +++ b/src/devices/sound/c140.cpp @@ -1,105 +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. -Timer behavior is not yet handled. +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 -Unmapped registers: - 0x1f8:timer interval? (Nx0.1 ms) - 0x1fa:irq ack? timer restart? - 0x1fe:timer switch?(0:off 1:on) +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 } @@ -111,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() { @@ -164,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(); } @@ -179,183 +215,230 @@ 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) { - int i,j; + float pbase = (float)m_baserate * 2.0f / (float)m_sample_rate; + s16 *lmix, *rmix; - 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; - - int16_t *lmix, *rmix; - - if(samples>m_sample_rate) samples=m_sample_rate; + int samples = stream.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(); + { + for (int i = 0; i < samples; i++) + { + stream.put_int_clamp(0, i, *lmix++, 32768 / 8); + stream.put_int_clamp(1, i, *rmix++, 32768 / 8); + } + } +} + +void c219_device::sound_stream_update(sound_stream &stream) +{ + float pbase = (float)m_baserate * 2.0f / (float)m_sample_rate; + s16 *lmix, *rmix; + + int samples = stream.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; } } @@ -363,25 +446,42 @@ 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++) + for (int i = 0; i < samples; i++) { - int32_t val; - - val = 8 * (*lmix++); - *dest1++ = limit(val); - val = 8 * (*rmix++); - *dest2++ = limit(val); + stream.put_int_clamp(0, i, *lmix++, 32768 / 8); + stream.put_int_clamp(1, 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; } @@ -389,143 +489,190 @@ void c140_device::c140_w(offs_t offset, u8 data) { m_stream->update(); - offset&=0x1ff; + 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)) + m_REG[offset] = data; + if (offset < 0x180) { - offset -= 8; - } + const u8 ch = offset >> 4; + C140_VOICE *v = &m_voi[ch]; - m_REG[offset]=data; - if( offset<0x180 ) - { - C140_VOICE *v = &m_voi[offset>>4]; - - 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) { - m_int1_callback(CLEAR_LINE); + // 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; - // 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); + // 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; } - else if (offset == 0x1fe) +} + + +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) { - 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 + // assume same as c140 + data = keyon_status_read(offset); + } + + return data; +} + + +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; + } + + m_REG[offset] = data; + if (offset < 0x100) // only 16 voices + { + 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; } |