AxibugEmuOnline/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs

249 lines
7.3 KiB
C#
Raw Normal View History

2024-08-29 17:20:01 +08:00
using Codice.CM.Client.Differences;
using System;
using RECTANGLE = VirtualNes.Core.APU_VRC6.RECTANGLE;
2024-08-07 18:31:11 +08:00
using SAWTOOTH = VirtualNes.Core.APU_VRC6.SAWTOOTH;
namespace VirtualNes.Core
2024-07-26 17:52:33 +08:00
{
public class APU_N106 : APU_INTERFACE
{
2024-08-29 17:20:01 +08:00
CHANNEL[] op = new CHANNEL[8];
const int CHANNEL_VOL_SHIFT = 6;
2024-08-07 18:31:11 +08:00
float cpu_clock;
2024-08-29 17:20:01 +08:00
uint cycle_rate;
byte addrinc;
byte address;
byte channel_use;
byte[] tone = new byte[0x100];
2024-08-07 18:31:11 +08:00
public APU_N106()
{
2024-08-29 17:20:01 +08:00
// 仮設定
cpu_clock = APU_CLOCK;
cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * 22050.0f));
2024-08-07 18:31:11 +08:00
}
2024-07-26 17:52:33 +08:00
public override void Reset(float fClock, int nRate)
{
2024-08-29 17:20:01 +08:00
for (int i = 0; i < 8; i++)
{
op[i].ZeroMemory();
op[i].tonelen = 0x10 << 18;
}
address = 0;
addrinc = 1;
channel_use = 8;
2024-08-07 18:31:11 +08:00
Setup(fClock, nRate);
2024-08-29 17:20:01 +08:00
// TONEの初期化はしない...
2024-07-26 17:52:33 +08:00
}
public override void Setup(float fClock, int nRate)
{
2024-08-07 18:31:11 +08:00
cpu_clock = fClock;
2024-08-29 17:20:01 +08:00
cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * nRate));
2024-07-26 17:52:33 +08:00
}
public override void Write(ushort addr, byte data)
{
2024-08-29 17:20:01 +08:00
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)
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
if (addrinc != 0)
{
address = (byte)((address + 1) & 0x7F);
}
2024-08-07 18:31:11 +08:00
}
2024-08-29 17:20:01 +08:00
return (byte)(addr >> 8);
2024-07-26 17:52:33 +08:00
}
public override int Process(int channel)
{
2024-08-29 17:20:01 +08:00
if (channel >= (8 - channel_use) && channel < 8)
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
return ChannelRender(ref op[channel]);
2024-08-07 18:31:11 +08:00
}
return 0;
}
public override int GetFreq(int channel)
{
2024-08-29 17:20:01 +08:00
if (channel < 8)
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
channel &= 7;
if (channel < (8 - channel_use))
2024-08-07 18:31:11 +08:00
return 0;
2024-08-29 17:20:01 +08:00
ref CHANNEL ch = ref op[channel & 0x07];
if (ch.freq == 0 || ch.vol == 0)
2024-08-07 18:31:11 +08:00
return 0;
2024-08-29 17:20:01 +08:00
int temp = channel_use * (8 - ch.databuf) * 4 * 45;
if (temp == 0)
2024-08-07 18:31:11 +08:00
return 0;
2024-08-29 17:20:01 +08:00
return (int)(256.0 * (double)cpu_clock * 12.0 * ch.freq / ((double)0x40000 * temp));
2024-08-07 18:31:11 +08:00
}
2024-07-30 11:57:09 +08:00
return 0;
2024-07-26 17:52:33 +08:00
}
2024-08-07 18:31:11 +08:00
2024-08-29 17:20:01 +08:00
private int ChannelRender(ref CHANNEL ch)
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
uint phasespd = (uint)(channel_use << 20);
ch.phaseacc -= (int)cycle_rate;
if (ch.phaseacc >= 0)
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
if (ch.volupdate != 0)
{
ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT;
ch.volupdate = 0;
}
return ch.output;
2024-08-07 18:31:11 +08:00
}
2024-08-29 17:20:01 +08:00
while (ch.phaseacc < 0)
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
ch.phaseacc += (int)phasespd;
ch.phase += ch.freq;
2024-08-07 18:31:11 +08:00
}
2024-08-29 17:20:01 +08:00
while (ch.tonelen != 0 && (ch.phase >= ch.tonelen))
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
ch.phase -= ch.tonelen;
2024-08-07 18:31:11 +08:00
}
2024-08-29 17:20:01 +08:00
ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT;
2024-08-07 18:31:11 +08:00
2024-08-29 17:20:01 +08:00
return ch.output;
}
2024-08-07 18:31:11 +08:00
2024-08-29 17:20:01 +08:00
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);
2024-08-07 18:31:11 +08:00
2024-08-29 17:20:01 +08:00
buffer.Write(tone);
2024-08-07 18:31:11 +08:00
}
2024-08-29 17:20:01 +08:00
public struct CHANNEL : IStateBufferObject
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
public int phaseacc;
2024-08-07 18:31:11 +08:00
2024-08-29 17:20:01 +08:00
public uint freq;
public uint phase;
public uint tonelen;
2024-08-07 18:31:11 +08:00
2024-08-29 17:20:01 +08:00
public int output;
public byte toneadr;
public byte volupdate;
2024-08-07 18:31:11 +08:00
2024-08-29 17:20:01 +08:00
public byte vol;
public byte databuf;
internal void ZeroMemory()
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
phaseacc = 0;
freq = 0;
phase = 0;
tonelen = 0;
output = 0;
toneadr = 0;
volupdate = 0;
vol = 0;
databuf = 0;
2024-08-07 18:31:11 +08:00
}
2024-08-29 17:20:01 +08:00
public uint GetSize()
2024-08-07 18:31:11 +08:00
{
2024-08-29 17:20:01 +08:00
return 4 * 5 + 4;
2024-08-07 18:31:11 +08:00
}
2024-08-29 17:20:01 +08:00
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);
}
2024-08-07 18:31:11 +08:00
}
2024-07-26 17:52:33 +08:00
}
}