forked from sin365/AxibugEmuOnline
257 lines
7.6 KiB
C#
257 lines
7.6 KiB
C#
namespace VirtualNes.Core
|
|
{
|
|
public class APU_N106 : APU_INTERFACE
|
|
{
|
|
CHANNEL[] op = new CHANNEL[8];
|
|
|
|
const int CHANNEL_VOL_SHIFT = 6;
|
|
float cpu_clock;
|
|
uint cycle_rate;
|
|
|
|
byte addrinc;
|
|
byte address;
|
|
byte channel_use;
|
|
|
|
byte[] tone = new byte[0x100];
|
|
|
|
public APU_N106()
|
|
{
|
|
// 仮設定
|
|
cpu_clock = APU_CLOCK;
|
|
cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * 22050.0f));
|
|
}
|
|
|
|
public override void Reset(float fClock, int nRate)
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
op[i].ZeroMemory();
|
|
op[i].tonelen = 0x10 << 18;
|
|
}
|
|
|
|
address = 0;
|
|
addrinc = 1;
|
|
channel_use = 8;
|
|
|
|
Setup(fClock, nRate);
|
|
|
|
// TONEの初期化はしない...
|
|
}
|
|
|
|
public override void Setup(float fClock, int nRate)
|
|
{
|
|
cpu_clock = fClock;
|
|
cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * nRate));
|
|
}
|
|
|
|
public override void Write(ushort addr, byte data)
|
|
{
|
|
if (addr == 0x4800)
|
|
{
|
|
// tone[address*2+0] = (INT)(data&0x0F);
|
|
// tone[address*2+1] = (INT)(data >>4);
|
|
tone[address * 2 + 0] = (byte)(data & 0x0F);
|
|
tone[address * 2 + 1] = (byte)(data >> 4);
|
|
|
|
if (address >= 0x40)
|
|
{
|
|
int no = (address - 0x40) >> 3;
|
|
uint tonelen = 0;
|
|
ref CHANNEL ch = ref op[no];
|
|
|
|
switch (address & 7)
|
|
{
|
|
case 0x00:
|
|
ch.freq = (uint)((ch.freq & ~0x000000FF) | data);
|
|
break;
|
|
case 0x02:
|
|
ch.freq = (uint)((ch.freq & ~0x0000FF00) | ((uint)data << 8));
|
|
break;
|
|
case 0x04:
|
|
ch.freq = (uint)((ch.freq & ~0x00030000) | (((uint)data & 0x03) << 16));
|
|
tonelen = (uint)((0x20 - (data & 0x1c)) << 18);
|
|
ch.databuf = (byte)((data & 0x1c) >> 2);
|
|
if (ch.tonelen != tonelen)
|
|
{
|
|
ch.tonelen = tonelen;
|
|
ch.phase = 0;
|
|
}
|
|
break;
|
|
case 0x06:
|
|
ch.toneadr = data;
|
|
break;
|
|
case 0x07:
|
|
ch.vol = (byte)(data & 0x0f);
|
|
ch.volupdate = 0xFF;
|
|
if (no == 7)
|
|
channel_use = (byte)(((data >> 4) & 0x07) + 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (addrinc != 0)
|
|
{
|
|
address = (byte)((address + 1) & 0x7f);
|
|
}
|
|
}
|
|
else if (addr == 0xF800)
|
|
{
|
|
address = (byte)(data & 0x7F);
|
|
addrinc = (byte)(data & 0x80);
|
|
}
|
|
}
|
|
|
|
public override byte Read(ushort addr)
|
|
{
|
|
// $4800 dummy read!!
|
|
if (addr == 0x0000)
|
|
{
|
|
if (addrinc != 0)
|
|
{
|
|
address = (byte)((address + 1) & 0x7F);
|
|
}
|
|
}
|
|
|
|
return (byte)(addr >> 8);
|
|
}
|
|
|
|
public override int Process(int channel)
|
|
{
|
|
if (channel >= (8 - channel_use) && channel < 8)
|
|
{
|
|
return ChannelRender(ref op[channel]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public override int GetFreq(int channel)
|
|
{
|
|
if (channel < 8)
|
|
{
|
|
channel &= 7;
|
|
if (channel < (8 - channel_use))
|
|
return 0;
|
|
|
|
ref CHANNEL ch = ref op[channel & 0x07];
|
|
if (ch.freq == 0 || ch.vol == 0)
|
|
return 0;
|
|
int temp = channel_use * (8 - ch.databuf) * 4 * 45;
|
|
if (temp == 0)
|
|
return 0;
|
|
return (int)(256.0 * cpu_clock * 12.0 * ch.freq / ((double)0x40000 * temp));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private int ChannelRender(ref CHANNEL ch)
|
|
{
|
|
uint phasespd = (uint)(channel_use << 20);
|
|
|
|
ch.phaseacc -= (int)cycle_rate;
|
|
if (ch.phaseacc >= 0)
|
|
{
|
|
if (ch.volupdate != 0)
|
|
{
|
|
ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT;
|
|
ch.volupdate = 0;
|
|
}
|
|
return ch.output;
|
|
}
|
|
|
|
while (ch.phaseacc < 0)
|
|
{
|
|
ch.phaseacc += (int)phasespd;
|
|
ch.phase += ch.freq;
|
|
}
|
|
while (ch.tonelen != 0 && (ch.phase >= ch.tonelen))
|
|
{
|
|
ch.phase -= ch.tonelen;
|
|
}
|
|
|
|
ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT;
|
|
|
|
return ch.output;
|
|
}
|
|
|
|
public override uint GetSize()
|
|
{
|
|
return (uint)(3 * sizeof(byte) + 8 * op[0].GetSize() + tone.Length);
|
|
}
|
|
|
|
public override void SaveState(StateBuffer buffer)
|
|
{
|
|
buffer.Write(addrinc);
|
|
buffer.Write(address);
|
|
buffer.Write(channel_use);
|
|
|
|
foreach (var oneOp in op)
|
|
oneOp.SaveState(buffer);
|
|
|
|
buffer.Write(tone);
|
|
}
|
|
|
|
public struct CHANNEL : IStateBufferObject
|
|
{
|
|
public int phaseacc;
|
|
|
|
public uint freq;
|
|
public uint phase;
|
|
public uint tonelen;
|
|
|
|
public int output;
|
|
|
|
public byte toneadr;
|
|
public byte volupdate;
|
|
|
|
public byte vol;
|
|
public byte databuf;
|
|
|
|
internal void ZeroMemory()
|
|
{
|
|
phaseacc = 0;
|
|
freq = 0;
|
|
phase = 0;
|
|
tonelen = 0;
|
|
output = 0;
|
|
toneadr = 0;
|
|
volupdate = 0;
|
|
vol = 0;
|
|
databuf = 0;
|
|
}
|
|
|
|
public uint GetSize()
|
|
{
|
|
return 4 * 5 + 4;
|
|
}
|
|
|
|
public void SaveState(StateBuffer buffer)
|
|
{
|
|
buffer.Write(phaseacc);
|
|
buffer.Write(freq);
|
|
buffer.Write(phase);
|
|
buffer.Write(tonelen);
|
|
buffer.Write(output);
|
|
buffer.Write(toneadr);
|
|
buffer.Write(volupdate);
|
|
buffer.Write(vol);
|
|
buffer.Write(databuf);
|
|
}
|
|
|
|
public void LoadState(StateReader buffer)
|
|
{
|
|
phaseacc = buffer.Read_int();
|
|
freq = buffer.Read_uint();
|
|
phase = buffer.Read_uint();
|
|
tonelen = buffer.Read_uint();
|
|
output = buffer.Read_int();
|
|
toneadr = buffer.Read_byte();
|
|
volupdate = buffer.Read_byte();
|
|
vol = buffer.Read_byte();
|
|
databuf = buffer.Read_byte();
|
|
}
|
|
}
|
|
}
|
|
}
|