285 lines
10 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|