493 lines
12 KiB
C++
493 lines
12 KiB
C++
/*
|
|
License: Zlib
|
|
see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details
|
|
|
|
Copyright holder(s): cam900
|
|
Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst
|
|
Konami SCC emulation core
|
|
|
|
modified by tildearrow...
|
|
*/
|
|
|
|
#include "scc.hpp"
|
|
#include "../../../../../src/engine/dispatch.h"
|
|
|
|
// shared SCC features
|
|
void scc_core::tick(const int cycles, blip_buffer_t* bb, DivDispatchOscBuffer** oscBuf)
|
|
{
|
|
for (int elem=0; elem<5; elem++)
|
|
{
|
|
m_voice[elem].tick(cycles,bb,oscBuf[elem]);
|
|
}
|
|
}
|
|
|
|
void scc_core::voice_t::updateOut(const int pos) {
|
|
int out=0;
|
|
// get output
|
|
if (m_enable)
|
|
{
|
|
out = (m_wave[m_addr] * m_volume) >> 4; // scale to 11 bit digital output
|
|
}
|
|
else
|
|
{
|
|
out = 0;
|
|
}
|
|
|
|
m_oscBuf->putSample(pos,out<<7);
|
|
|
|
if (out!=m_out) {
|
|
blip_add_delta(m_bb,pos,(out-m_out)<<5);
|
|
m_out=out;
|
|
}
|
|
}
|
|
|
|
void scc_core::voice_t::tick(const int amt, blip_buffer_t* bb, DivDispatchOscBuffer* oscBuf)
|
|
{
|
|
m_bb=bb;
|
|
m_oscBuf=oscBuf;
|
|
|
|
if (m_pitch >= 9) // or voice is halted
|
|
{
|
|
int rem=amt;
|
|
for (int pos=0; pos<amt; pos++) {
|
|
int cycles=m_host.m_test.freq_4bit()?(m_counter&15):(m_host.m_test.freq_8bit()?(m_counter&0xff):(m_counter&0xfff));
|
|
if (cycles>rem) cycles=rem;
|
|
if (cycles<1) cycles=1;
|
|
|
|
// update counter - Post decrement
|
|
const u16 temp = m_counter;
|
|
if (m_host.m_test.freq_4bit()) // 4 bit frequency mode
|
|
{
|
|
m_counter = (m_counter & ~0x0ff) | (bitfield(bitfield(m_counter, 0, 8) - cycles, 0, 8) << 0);
|
|
m_counter = (m_counter & ~0xf00) | (bitfield(bitfield(m_counter, 8, 4) - cycles, 0, 4) << 8);
|
|
}
|
|
else
|
|
{
|
|
m_counter = bitfield(m_counter - cycles, 0, 12);
|
|
}
|
|
|
|
// handle counter carry
|
|
const bool carry = (temp<cycles) || (m_host.m_test.freq_8bit()
|
|
? (bitfield(temp, 0, 8) == 0)
|
|
: (m_host.m_test.freq_4bit() ? (bitfield(temp, 8, 4) == 0)
|
|
: (bitfield(temp, 0, 12) == 0)));
|
|
if (carry)
|
|
{
|
|
m_addr = bitfield(m_addr + 1, 0, 5);
|
|
m_counter = m_pitch - ((temp<cycles)?(cycles-temp-1):0);
|
|
while (m_counter>m_pitch) {
|
|
m_addr = bitfield(m_addr + 1, 0, 5);
|
|
m_counter+=m_pitch-1;
|
|
}
|
|
}
|
|
pos+=cycles-1;
|
|
rem-=cycles;
|
|
updateOut(pos);
|
|
}
|
|
} else {
|
|
updateOut(0);
|
|
}
|
|
}
|
|
|
|
void scc_core::reset()
|
|
{
|
|
for (int elem=0; elem<5; elem++)
|
|
{
|
|
m_voice[elem].reset();
|
|
}
|
|
|
|
m_test.reset();
|
|
m_out = 0;
|
|
memset(m_reg,0,256);
|
|
}
|
|
|
|
void scc_core::voice_t::reset()
|
|
{
|
|
memset(m_wave,0,32);
|
|
m_enable = false;
|
|
m_pitch = 0;
|
|
m_volume = 0;
|
|
m_addr = 0;
|
|
m_counter = 0;
|
|
m_out = 0;
|
|
}
|
|
|
|
// SCC accessors
|
|
u8 scc_core::wave_r(bool is_sccplus, u8 address)
|
|
{
|
|
u8 ret = 0xff;
|
|
const u8 voice = bitfield(address, 5, 3);
|
|
if (voice > 4)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
u8 wave_addr = bitfield(address, 0, 5);
|
|
|
|
if (m_test.rotate())
|
|
{ // rotate flag
|
|
wave_addr = bitfield(wave_addr + m_voice[voice].addr(), 0, 5);
|
|
}
|
|
|
|
if (!is_sccplus)
|
|
{
|
|
if (voice == 3) // rotate voice 3~4 flag
|
|
{
|
|
if (m_test.rotate4() || m_test.rotate())
|
|
{ // rotate flag
|
|
wave_addr =
|
|
bitfield(bitfield(address, 0, 5) + m_voice[3 + m_test.rotate()].addr(), 0, 5);
|
|
}
|
|
}
|
|
}
|
|
ret = m_voice[voice].wave(wave_addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void scc_core::wave_w(bool is_sccplus, u8 address, u8 data)
|
|
{
|
|
if (m_test.rotate())
|
|
{ // write protected
|
|
return;
|
|
}
|
|
|
|
const u8 voice = bitfield(address, 5, 3);
|
|
if (voice > 4)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const u8 wave_addr = bitfield(address, 0, 5);
|
|
|
|
if (!is_sccplus)
|
|
{
|
|
if (((voice >= 3) && m_test.rotate4()) || (voice >= 4))
|
|
{ // Ignore if write protected, or voice 4
|
|
return;
|
|
}
|
|
if (voice >= 3) // voice 3, 4 shares waveform
|
|
{
|
|
m_voice[3].set_wave(wave_addr, data);
|
|
m_voice[4].set_wave(wave_addr, data);
|
|
}
|
|
else
|
|
{
|
|
m_voice[voice].set_wave(wave_addr, data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_voice[voice].set_wave(wave_addr, data);
|
|
}
|
|
}
|
|
|
|
void scc_core::freq_vol_enable_w(u8 address, u8 data)
|
|
{
|
|
// *0-*f Pitch, Volume, Enable
|
|
address = bitfield(address, 0, 4); // mask address to 4 bit
|
|
const u8 voice_freq = bitfield(address, 1, 3);
|
|
switch (address)
|
|
{
|
|
case 0x0: // 0x*0 Voice 0 Pitch LSB
|
|
case 0x2: // 0x*2 Voice 1 Pitch LSB
|
|
case 0x4: // 0x*4 Voice 2 Pitch LSB
|
|
case 0x6: // 0x*6 Voice 3 Pitch LSB
|
|
case 0x8: // 0x*8 Voice 4 Pitch LSB
|
|
if (m_test.resetpos())
|
|
{ // Reset address
|
|
m_voice[voice_freq].reset_addr();
|
|
}
|
|
m_voice[voice_freq].set_pitch(data, 0x0ff);
|
|
break;
|
|
case 0x1: // 0x*1 Voice 0 Pitch MSB
|
|
case 0x3: // 0x*3 Voice 1 Pitch MSB
|
|
case 0x5: // 0x*5 Voice 2 Pitch MSB
|
|
case 0x7: // 0x*7 Voice 3 Pitch MSB
|
|
case 0x9: // 0x*9 Voice 4 Pitch MSB
|
|
if (m_test.resetpos())
|
|
{ // Reset address
|
|
m_voice[voice_freq].reset_addr();
|
|
}
|
|
m_voice[voice_freq].set_pitch(u16(bitfield(data, 0, 4)) << 8, 0xf00);
|
|
break;
|
|
case 0xa: // 0x*a Voice 0 Volume
|
|
case 0xb: // 0x*b Voice 1 Volume
|
|
case 0xc: // 0x*c Voice 2 Volume
|
|
case 0xd: // 0x*d Voice 3 Volume
|
|
case 0xe: // 0x*e Voice 4 Volume
|
|
m_voice[address - 0xa].set_volume(bitfield(data, 0, 4));
|
|
break;
|
|
case 0xf: // 0x*f Enable/Disable flag
|
|
m_voice[0].set_enable(bitfield(data, 0));
|
|
m_voice[1].set_enable(bitfield(data, 1));
|
|
m_voice[2].set_enable(bitfield(data, 2));
|
|
m_voice[3].set_enable(bitfield(data, 3));
|
|
m_voice[4].set_enable(bitfield(data, 4));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void k051649_scc_core::scc_w(bool is_sccplus, u8 address, u8 data)
|
|
{
|
|
const u8 voice = bitfield(address, 5, 3);
|
|
switch (voice)
|
|
{
|
|
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
|
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
|
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
|
case 0b011: // 0x60-0x7f Voice 3/4 Waveform
|
|
wave_w(false, address, data);
|
|
break;
|
|
case 0b100: // 0x80-0x9f Pitch, Volume, Enable
|
|
freq_vol_enable_w(address, data);
|
|
break;
|
|
case 0b111: // 0xe0-0xff Test register
|
|
m_test.set_freq_4bit(bitfield(data, 0));
|
|
m_test.set_freq_8bit(bitfield(data, 1));
|
|
m_test.set_resetpos(bitfield(data, 5));
|
|
m_test.set_rotate(bitfield(data, 6));
|
|
m_test.set_rotate4(bitfield(data, 7));
|
|
break;
|
|
}
|
|
m_reg[address] = data;
|
|
}
|
|
|
|
void k052539_scc_core::scc_w(bool is_sccplus, u8 address, u8 data)
|
|
{
|
|
const u8 voice = bitfield(address, 5, 3);
|
|
if (is_sccplus)
|
|
{
|
|
switch (voice)
|
|
{
|
|
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
|
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
|
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
|
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
|
case 0b100: // 0x80-0x9f Voice 4 Waveform
|
|
wave_w(true, address, data);
|
|
break;
|
|
case 0b101: // 0xa0-0xbf Pitch, Volume, Enable
|
|
freq_vol_enable_w(address, data);
|
|
break;
|
|
case 0b110: // 0xc0-0xdf Test register
|
|
m_test.set_freq_4bit(bitfield(data, 0));
|
|
m_test.set_freq_8bit(bitfield(data, 1));
|
|
m_test.set_resetpos(bitfield(data, 5));
|
|
m_test.set_rotate(bitfield(data, 6));
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (voice)
|
|
{
|
|
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
|
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
|
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
|
case 0b011: // 0x60-0x7f Voice 3/4 Waveform
|
|
wave_w(false, address, data);
|
|
break;
|
|
case 0b100: // 0x80-0x9f Pitch, Volume, Enable
|
|
freq_vol_enable_w(address, data);
|
|
break;
|
|
case 0b110: // 0xc0-0xdf Test register
|
|
m_test.set_freq_4bit(bitfield(data, 0));
|
|
m_test.set_freq_8bit(bitfield(data, 1));
|
|
m_test.set_resetpos(bitfield(data, 5));
|
|
m_test.set_rotate(bitfield(data, 6));
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
m_reg[address] = data;
|
|
}
|
|
|
|
u8 k051649_scc_core::scc_r(bool is_sccplus, u8 address)
|
|
{
|
|
const u8 voice = bitfield(address, 5, 3);
|
|
const u8 wave = bitfield(address, 0, 5);
|
|
u8 ret = 0xff;
|
|
switch (voice)
|
|
{
|
|
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
|
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
|
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
|
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
|
case 0b101: // 0xa0-0xbf Voice 4 Waveform
|
|
ret = wave_r(false, (std::min<u8>(4, voice) << 5) | wave);
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
u8 k052539_scc_core::scc_r(bool is_sccplus, u8 address)
|
|
{
|
|
const u8 voice = bitfield(address, 5, 3);
|
|
const u8 wave = bitfield(address, 0, 5);
|
|
u8 ret = 0xff;
|
|
if (is_sccplus)
|
|
{
|
|
switch (voice)
|
|
{
|
|
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
|
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
|
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
|
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
|
case 0b100: // 0x80-0x9f Voice 4 Waveform
|
|
ret = wave_r(true, address);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (voice)
|
|
{
|
|
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
|
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
|
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
|
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
|
case 0b101: // 0xa0-0xbf Voice 4 Waveform
|
|
ret = wave_r(false, (std::min<u8>(4, voice) << 5) | wave);
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Mapper
|
|
void k051649_core::reset()
|
|
{
|
|
k051649_scc_core::reset();
|
|
m_mapper.reset();
|
|
m_scc_enable = false;
|
|
}
|
|
|
|
void k052539_core::reset()
|
|
{
|
|
k052539_scc_core::reset();
|
|
m_mapper.reset();
|
|
m_scc_enable = false;
|
|
m_is_sccplus = false;
|
|
}
|
|
|
|
void k051649_core::k051649_mapper_t::reset()
|
|
{
|
|
m_bank[0] = 0;
|
|
m_bank[1] = 1;
|
|
m_bank[2] = 2;
|
|
m_bank[3] = 3;
|
|
}
|
|
|
|
void k052539_core::k052539_mapper_t::reset()
|
|
{
|
|
m_bank[0] = 0;
|
|
m_bank[1] = 1;
|
|
m_bank[2] = 2;
|
|
m_bank[3] = 3;
|
|
std::fill(m_ram_enable.begin(), m_ram_enable.end(), false);
|
|
}
|
|
|
|
// Mapper accessors
|
|
u8 k051649_core::read(u16 address)
|
|
{
|
|
if ((bitfield(address, 11, 5) == 0b10011) && m_scc_enable)
|
|
{
|
|
return scc_r(false, u8(address));
|
|
}
|
|
|
|
return m_intf.read_byte((u32(m_mapper.bank(bitfield(address, 13, 2) ^ 2)) << 13) |
|
|
bitfield(address, 0, 13));
|
|
}
|
|
|
|
u8 k052539_core::read(u16 address)
|
|
{
|
|
if ((bitfield(address, 11, 5) == 0b10011) && m_scc_enable && (!m_is_sccplus))
|
|
{
|
|
return scc_r(false, u8(address));
|
|
}
|
|
|
|
if ((bitfield(address, 11, 5) == 0b10111) && m_scc_enable && m_is_sccplus)
|
|
{
|
|
return scc_r(true, u8(address));
|
|
}
|
|
|
|
return m_intf.read_byte((u32(m_mapper.bank(bitfield(address, 13, 2) ^ 2)) << 13) |
|
|
bitfield(address, 0, 13));
|
|
}
|
|
|
|
void k051649_core::write(u16 address, u8 data)
|
|
{
|
|
const u16 bank = bitfield(address, 13, 2) ^ 2;
|
|
switch (bitfield(address, 11, 5))
|
|
{
|
|
case 0b01010: // 0x5000-0x57ff Bank 0
|
|
case 0b01110: // 0x7000-0x77ff Bank 1
|
|
case 0b10010: // 0x9000-0x97ff Bank 2
|
|
case 0b10110: // 0xb000-0xb7ff Bank 3
|
|
m_mapper.set_bank(bank, data);
|
|
m_scc_enable = (bitfield(m_mapper.bank(2), 0, 6) == 0x3f);
|
|
break;
|
|
case 0b10011: // 0x9800-9fff SCC
|
|
if (m_scc_enable)
|
|
{
|
|
scc_w(false, u8(address), data);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void k052539_core::write(u16 address, u8 data)
|
|
{
|
|
u8 prev = 0;
|
|
bool update = false;
|
|
const u16 bank = bitfield(address, 13, 2) ^ 2;
|
|
const bool ram_enable = m_mapper.ram_enable(bank);
|
|
if (ram_enable)
|
|
{
|
|
m_intf.write_byte((u32(m_mapper.bank(bank)) << 13) | bitfield(address, 0, 13), data);
|
|
}
|
|
switch (bitfield(address, 11, 5))
|
|
{
|
|
case 0b01010: // 0x5000-0x57ff Bank 0
|
|
case 0b01110: // 0x7000-0x77ff Bank 1
|
|
case 0b10010: // 0x9000-0x97ff Bank 2
|
|
case 0b10110: // 0xb000-0xb7ff Bank 3
|
|
if (!ram_enable)
|
|
{
|
|
prev = m_mapper.bank(bank);
|
|
m_mapper.set_bank(bank, data);
|
|
update = prev ^ m_mapper.bank(bank);
|
|
}
|
|
break;
|
|
case 0b10011: // 0x9800-0x9fff SCC
|
|
if ((!ram_enable) && m_scc_enable && (!m_is_sccplus))
|
|
{
|
|
scc_w(false, u8(address), data);
|
|
}
|
|
break;
|
|
case 0b10111: // 0xb800-0xbfff SCC+, Mapper configuration
|
|
if (bitfield(address, 1, 10) == 0x3ff)
|
|
{
|
|
m_mapper.set_ram_enable(0, bitfield(data, 4) || bitfield(data, 0));
|
|
m_mapper.set_ram_enable(1, bitfield(data, 4) || bitfield(data, 1));
|
|
m_mapper.set_ram_enable(2, bitfield(data, 4) || bitfield(data, 2));
|
|
m_mapper.set_ram_enable(3, bitfield(data, 4));
|
|
prev = (m_is_sccplus ? 1 : 0);
|
|
m_is_sccplus = bitfield(data, 5);
|
|
update = prev ^ (m_is_sccplus ? 1 : 0);
|
|
}
|
|
else if ((!ram_enable) && m_scc_enable && m_is_sccplus)
|
|
{
|
|
scc_w(true, u8(address), data);
|
|
}
|
|
break;
|
|
}
|
|
if (update)
|
|
{
|
|
m_scc_enable =
|
|
m_is_sccplus ? bitfield(m_mapper.bank(3), 7) : (bitfield(m_mapper.bank(2), 0, 6) == 0x3f);
|
|
}
|
|
}
|