261 lines
5.0 KiB
C++
261 lines
5.0 KiB
C++
/*
|
|
License: Zlib
|
|
see https://gitlab.com/cam900/vgsound_emu/-/blob/main/LICENSE for more details
|
|
|
|
Copyright holder(s): cam900
|
|
Konami VRC VI sound emulation core
|
|
*/
|
|
|
|
#include "vrcvi.hpp"
|
|
|
|
void vrcvi_core::tick()
|
|
{
|
|
m_out = 0;
|
|
if (!m_control.halt()) // Halt flag
|
|
{
|
|
// tick per each clock
|
|
for (auto &elem : m_pulse)
|
|
{
|
|
m_out += elem.get_output(); // add 4 bit pulse output
|
|
}
|
|
m_out += m_sawtooth.get_output(); // add 5 bit sawtooth output
|
|
}
|
|
if (m_timer.tick())
|
|
{
|
|
m_timer.counter_tick();
|
|
}
|
|
}
|
|
|
|
void vrcvi_core::reset()
|
|
{
|
|
for (auto &elem : m_pulse)
|
|
{
|
|
elem.reset();
|
|
}
|
|
|
|
m_sawtooth.reset();
|
|
m_timer.reset();
|
|
m_control.reset();
|
|
m_out = 0;
|
|
}
|
|
|
|
bool vrcvi_core::alu_t::tick()
|
|
{
|
|
if (m_divider.enable())
|
|
{
|
|
const u16 temp = m_counter;
|
|
// post decrement
|
|
if (bitfield(m_host.m_control.shift(), 1))
|
|
{
|
|
m_counter = (m_counter & 0x0ff) | (bitfield(bitfield(m_counter, 8, 4) - 1, 0, 4) << 8);
|
|
m_counter = (m_counter & 0xf00) | (bitfield(bitfield(m_counter, 0, 8) - 1, 0, 8) << 0);
|
|
}
|
|
else if (bitfield(m_host.m_control.shift(), 0))
|
|
{
|
|
m_counter = (m_counter & 0x00f) | (bitfield(bitfield(m_counter, 4, 8) - 1, 0, 8) << 4);
|
|
m_counter = (m_counter & 0xff0) | (bitfield(bitfield(m_counter, 0, 4) - 1, 0, 4) << 0);
|
|
}
|
|
else
|
|
{
|
|
m_counter = bitfield(bitfield(m_counter, 0, 12) - 1, 0, 12);
|
|
}
|
|
|
|
// carry handling
|
|
bool carry = bitfield(m_host.m_control.shift(), 1)
|
|
? (bitfield(temp, 8, 4) == 0)
|
|
: (bitfield(m_host.m_control.shift(), 0) ? (bitfield(temp, 4, 8) == 0)
|
|
: (bitfield(temp, 0, 12) == 0));
|
|
if (carry)
|
|
{
|
|
m_counter = m_divider.divider();
|
|
}
|
|
|
|
return carry;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool vrcvi_core::pulse_t::tick()
|
|
{
|
|
if (!m_divider.enable())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (vrcvi_core::alu_t::tick())
|
|
{
|
|
m_cycle = bitfield(m_cycle + 1, 0, 4);
|
|
}
|
|
|
|
return m_control.mode() ? true : ((m_cycle > m_control.duty()) ? true : false);
|
|
}
|
|
|
|
bool vrcvi_core::sawtooth_t::tick()
|
|
{
|
|
if (!m_divider.enable())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (vrcvi_core::alu_t::tick())
|
|
{
|
|
if (bitfield(m_cycle++, 0))
|
|
{ // Even step only
|
|
m_accum += m_rate;
|
|
}
|
|
if (m_cycle >= 14) // Reset accumulator at every 14 cycles
|
|
{
|
|
m_accum = 0;
|
|
m_cycle = 0;
|
|
}
|
|
}
|
|
return (m_accum == 0) ? false : true;
|
|
}
|
|
|
|
s8 vrcvi_core::pulse_t::get_output()
|
|
{
|
|
// add 4 bit pulse output
|
|
m_out = tick() ? m_control.volume() : 0;
|
|
return m_out;
|
|
}
|
|
|
|
s8 vrcvi_core::sawtooth_t::get_output()
|
|
{
|
|
// add 5 bit sawtooth output
|
|
m_out = tick() ? bitfield(m_accum, 3, 5) : 0;
|
|
return m_out;
|
|
}
|
|
|
|
void vrcvi_core::alu_t::reset()
|
|
{
|
|
m_divider.reset();
|
|
m_counter = 0;
|
|
m_cycle = 0;
|
|
m_out = 0;
|
|
}
|
|
|
|
void vrcvi_core::pulse_t::reset()
|
|
{
|
|
vrcvi_core::alu_t::reset();
|
|
m_control.reset();
|
|
}
|
|
|
|
void vrcvi_core::sawtooth_t::reset()
|
|
{
|
|
vrcvi_core::alu_t::reset();
|
|
m_rate = 0;
|
|
m_accum = 0;
|
|
}
|
|
|
|
bool vrcvi_core::timer_t::tick()
|
|
{
|
|
if (m_timer_control.enable())
|
|
{
|
|
if (!m_timer_control.sync()) // scanline sync mode
|
|
{
|
|
m_prescaler -= 3;
|
|
if (m_prescaler <= 0)
|
|
{
|
|
m_prescaler += 341;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return (m_timer_control.enable() && m_timer_control.sync()) ? true : false;
|
|
}
|
|
|
|
void vrcvi_core::timer_t::counter_tick()
|
|
{
|
|
if (bitfield(++m_counter, 0, 8) == 0)
|
|
{
|
|
m_counter = m_counter_latch;
|
|
irq_set();
|
|
}
|
|
}
|
|
|
|
void vrcvi_core::timer_t::reset()
|
|
{
|
|
m_timer_control.reset();
|
|
m_prescaler = 341;
|
|
m_counter = m_counter_latch = 0;
|
|
irq_clear();
|
|
}
|
|
|
|
// Accessors
|
|
|
|
void vrcvi_core::alu_t::divider_t::write(bool msb, u8 data)
|
|
{
|
|
if (msb)
|
|
{
|
|
m_divider = (m_divider & ~0xf00) | (bitfield<u32>(data, 0, 4) << 8);
|
|
m_enable = bitfield(data, 7);
|
|
}
|
|
else
|
|
{
|
|
m_divider = (m_divider & ~0x0ff) | data;
|
|
}
|
|
}
|
|
|
|
void vrcvi_core::pulse_w(u8 voice, u8 address, u8 data)
|
|
{
|
|
pulse_t &v = m_pulse[voice];
|
|
switch (address)
|
|
{
|
|
case 0x00: // Control - 0x9000 (Pulse 1), 0xa000 (Pulse 2)
|
|
v.control().write(data);
|
|
break;
|
|
case 0x01: // Pitch LSB - 0x9001/0x9002 (Pulse 1), 0xa001/0xa002 (Pulse 2)
|
|
v.divider().write(false, data);
|
|
break;
|
|
case 0x02: // Pitch MSB, Enable/Disable - 0x9002/0x9001 (Pulse 1), 0xa002/0xa001 (Pulse 2)
|
|
v.divider().write(true, data);
|
|
if (!v.divider().enable())
|
|
{ // Reset duty cycle
|
|
v.clear_cycle();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void vrcvi_core::saw_w(u8 address, u8 data)
|
|
{
|
|
switch (address)
|
|
{
|
|
case 0x00: // Sawtooth Accumulate - 0xb000
|
|
m_sawtooth.set_rate(bitfield(data, 0, 6));
|
|
break;
|
|
case 0x01: // Pitch LSB - 0xb001/0xb002 (Sawtooth)
|
|
m_sawtooth.divider().write(false, data);
|
|
break;
|
|
case 0x02: // Pitch MSB, Enable/Disable - 0xb002/0xb001 (Sawtooth)
|
|
m_sawtooth.divider().write(true, data);
|
|
if (!m_sawtooth.divider().enable())
|
|
{ // Reset accumulator
|
|
m_sawtooth.clear_accum();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void vrcvi_core::timer_w(u8 address, u8 data)
|
|
{
|
|
switch (address)
|
|
{
|
|
case 0x00: // Timer latch - 0xf000
|
|
m_timer.set_counter_latch(data);
|
|
break;
|
|
case 0x01: // Timer control - 0xf001/0xf002
|
|
m_timer.timer_control_w(data);
|
|
break;
|
|
case 0x02: // IRQ Acknowledge - 0xf002/0xf001
|
|
m_timer.irq_ack();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void vrcvi_core::control_w(u8 data)
|
|
{
|
|
// Global control - 0x9003
|
|
m_control.write(data);
|
|
}
|