AxibugEmuOnline/AxibugEmuOnline.Client/Assets/Plugins/Mame.Core/sound/MSM5205.cs

285 lines
10 KiB
C#

using System;
using System.IO;
namespace MAME.Core
{
public unsafe class MSM5205
{
public struct MSM5205Voice
{
public vclk_delegate vclk_callback;
public int select;
public sound_stream stream; /* number of stream system */
public int index;
public int clock; /* clock rate */
public int data; /* next adpcm data */
public int vclk; /* vclk signal (external mode) */
public int reset; /* reset pin signal */
public int prescaler; /* prescaler selector S1 and S2 */
public int bitwidth; /* bit width selector -3B/4B */
public int signal; /* current ADPCM signal */
public int step; /* current ADPCM step */
};
public delegate void vclk_delegate(int i);
public static int[] diff_lookup;
public static int[] index_shift = new int[8] { -1, -1, -1, -1, 2, 4, 6, 8 };
public MSM5205Voice voice;
public static EmuTimer.emu_timer[] timer = new EmuTimer.emu_timer[2];
public static MSM5205[] mm1 = new MSM5205[2];
public static void ComputeTables()
{
int[,] nbl2bit = new int[16, 4]
{
{ 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
{ 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
{-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
{-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
};
int step, nib;
diff_lookup = new int[49 * 16];
for (step = 0; step <= 48; step++)
{
int stepval = (int)Math.Floor(16.0 * Math.Pow(11.0 / 10.0, (double)step));
for (nib = 0; nib < 16; nib++)
{
diff_lookup[step * 16 + nib] = nbl2bit[nib, 0] *
(stepval * nbl2bit[nib, 1] +
stepval / 2 * nbl2bit[nib, 2] +
stepval / 4 * nbl2bit[nib, 3] +
stepval / 8);
}
}
}
public void MSM5205_update(int offset, int length)
{
int i;
if (voice.signal != 0)
{
short val = (short)(voice.signal * 16);
for (i = 0; i < length; i++)
{
voice.stream.streamoutput_Ptrs[0][offset + i] = val;
}
}
else
{
for (i = 0; i < length; i++)
{
voice.stream.streamoutput_Ptrs[0][offset + i] = 0;
}
}
}
public static void MSM5205_vclk_callback0()
{
int val;
int new_signal;
if (mm1[0].voice.vclk_callback != null)
{
mm1[0].voice.vclk_callback(mm1[0].voice.index);
}
if (mm1[0].voice.reset != 0)
{
new_signal = 0;
mm1[0].voice.step = 0;
}
else
{
val = mm1[0].voice.data;
new_signal = mm1[0].voice.signal + diff_lookup[mm1[0].voice.step * 16 + (val & 15)];
if (new_signal > 2047)
{
new_signal = 2047;
}
else if (new_signal < -2048)
{
new_signal = -2048;
}
mm1[0].voice.step += index_shift[val & 7];
if (mm1[0].voice.step > 48)
{
mm1[0].voice.step = 48;
}
else if (mm1[0].voice.step < 0)
{
mm1[0].voice.step = 0;
}
}
if (mm1[0].voice.signal != new_signal)
{
mm1[0].voice.stream.stream_update();
mm1[0].voice.signal = new_signal;
}
}
public static void MSM5205_vclk_callback1()
{
int val;
int new_signal;
if (mm1[1].voice.vclk_callback != null)
{
mm1[1].voice.vclk_callback(mm1[1].voice.index);
}
if (mm1[1].voice.reset != 0)
{
new_signal = 0;
mm1[1].voice.step = 0;
}
else
{
val = mm1[1].voice.data;
new_signal = mm1[1].voice.signal + diff_lookup[mm1[1].voice.step * 16 + (val & 15)];
if (new_signal > 2047)
{
new_signal = 2047;
}
else if (new_signal < -2048)
{
new_signal = -2048;
}
mm1[1].voice.step += index_shift[val & 7];
if (mm1[1].voice.step > 48)
{
mm1[1].voice.step = 48;
}
else if (mm1[1].voice.step < 0)
{
mm1[1].voice.step = 0;
}
}
if (mm1[1].voice.signal != new_signal)
{
mm1[1].voice.stream.stream_update();
mm1[1].voice.signal = new_signal;
}
}
public void msm5205_reset()
{
voice.data = 0;
voice.vclk = 0;
voice.reset = 0;
voice.signal = 0;
voice.step = 0;
msm5205_playmode_w(voice.index, voice.select);
}
public static void msm5205_start(int sndindex, int clock, vclk_delegate vclk, int select)
{
//struct MSM5205Voice *voice;
/*voice = auto_malloc(sizeof(*voice));
memset(voice, 0, sizeof(*voice));
sndintrf_register_token(voice);*/
//voice.intf = config;
mm1[sndindex] = new MSM5205();
mm1[sndindex].voice.vclk_callback = vclk;
mm1[sndindex].voice.select = select;
mm1[sndindex].voice.index = sndindex;
mm1[sndindex].voice.clock = clock;
ComputeTables();
/* stream system initialize */
mm1[sndindex].voice.stream = new sound_stream(clock, 0, 1, mm1[sndindex].MSM5205_update);
if (sndindex == 0)
{
timer[0] = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.MSM5205_MSM5205_vclk_callback0, false);
}
else if (sndindex == 1)
{
timer[1] = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.MSM5205_MSM5205_vclk_callback1, false);
}
mm1[sndindex].msm5205_reset();
}
public static void null_vclk(int i)
{
}
public static void msm5205_vclk_w(int num, int vclk)
{
if (mm1[num].voice.prescaler != 0)
{
//logerror("error: msm5205_vclk_w() called with chip = %d, but VCLK selected master mode\n", num);
}
else
{
if (mm1[num].voice.vclk != vclk)
{
mm1[num].voice.vclk = vclk;
if (vclk == 0)
{
if (num == 0)
{
MSM5205_vclk_callback0();
}
else if (num == 1)
{
MSM5205_vclk_callback1();
}
}
}
}
}
public static void msm5205_reset_w(int num, int reset)
{
mm1[num].voice.reset = reset;
}
public static void msm5205_data_w(int num, int data)
{
if (mm1[num].voice.bitwidth == 4)
{
mm1[num].voice.data = data & 0x0f;
}
else
{
mm1[num].voice.data = (data & 0x07) << 1; /* unknown */
}
}
public static void msm5205_playmode_w(int num, int select)
{
int[] prescaler_table = new int[4] { 96, 48, 64, 0 };
int prescaler = prescaler_table[select & 3];
int bitwidth = ((select & 4) != 0) ? 4 : 3;
if (mm1[num].voice.prescaler != prescaler)
{
mm1[num].voice.stream.stream_update();
mm1[num].voice.prescaler = prescaler;
if (prescaler != 0)
{
Atime period = Attotime.attotime_mul(Attotime.ATTOTIME_IN_HZ(mm1[num].voice.clock), (uint)prescaler);
EmuTimer.timer_adjust_periodic(timer[num], period, period);
}
else
{
EmuTimer.timer_adjust_periodic(timer[num], Attotime.ATTOTIME_NEVER, Attotime.ATTOTIME_NEVER);
}
}
if (mm1[num].voice.bitwidth != bitwidth)
{
mm1[num].voice.stream.stream_update();
mm1[num].voice.bitwidth = bitwidth;
}
}
public void SaveStateBinary(BinaryWriter writer)
{
writer.Write(voice.select);
writer.Write(voice.index);
writer.Write(voice.clock);
writer.Write(voice.data);
writer.Write(voice.vclk);
writer.Write(voice.reset);
writer.Write(voice.prescaler);
writer.Write(voice.bitwidth);
writer.Write(voice.signal);
writer.Write(voice.step);
}
public void LoadStateBinary(BinaryReader reader)
{
voice.select = reader.ReadInt32();
voice.index = reader.ReadInt32();
voice.clock = reader.ReadInt32();
voice.data = reader.ReadInt32();
voice.vclk = reader.ReadInt32();
voice.reset = reader.ReadInt32();
voice.prescaler = reader.ReadInt32();
voice.bitwidth = reader.ReadInt32();
voice.signal = reader.ReadInt32();
voice.step = reader.ReadInt32();
}
}
}