630 lines
25 KiB
C#
630 lines
25 KiB
C#
using System;
|
|
using System.IO;
|
|
|
|
namespace MAME.Core
|
|
{
|
|
public unsafe class K054539
|
|
{
|
|
public struct k054539_channel
|
|
{
|
|
public int pos;
|
|
public int pfrac;
|
|
public int val;
|
|
public int pval;
|
|
}
|
|
public struct k054539_info
|
|
{
|
|
public double[] voltab;
|
|
public double[] pantab;
|
|
|
|
public double[] k054539_gain;
|
|
public byte[][] k054539_posreg_latch;
|
|
public int k054539_flags;
|
|
|
|
public byte[] regs;
|
|
public short[] ram;
|
|
public int reverb_pos;
|
|
|
|
public int cur_ptr;
|
|
public int cur_limit;
|
|
public byte[] cur_zone;
|
|
public byte[] rom;
|
|
public int rom_size;
|
|
public uint rom_mask;
|
|
|
|
public k054539_channel[] channels;
|
|
}
|
|
public static byte[] k054539rom;
|
|
public static k054539_info info;
|
|
public static int zoneflag, zonedata;
|
|
public static apanhandler apan;
|
|
public static irqhandler irq;
|
|
public delegate void apanhandler(double d1, double d2);
|
|
public delegate void irqhandler();
|
|
public static bool k054539_regupdate()
|
|
{
|
|
return (info.regs[0x22f] & 0x80) == 0;
|
|
}
|
|
public static void k054539_keyon(int channel)
|
|
{
|
|
if (k054539_regupdate())
|
|
{
|
|
info.regs[0x22c] |= (byte)(1 << channel);
|
|
}
|
|
}
|
|
public static void k054539_keyoff(int channel)
|
|
{
|
|
if (k054539_regupdate())
|
|
{
|
|
info.regs[0x22c] &= (byte)(~(1 << channel));
|
|
}
|
|
}
|
|
public static void k054539_update(int offset, int length)
|
|
{
|
|
short[] dpcm = new short[16] {
|
|
0<<8, 1<<8, 4<<8, 9<<8, 16<<8, 25<<8, 36<<8, 49<<8,
|
|
-64<<8, -49<<8, -36<<8, -25<<8, -16<<8, -9<<8, -4<<8, -1<<8
|
|
};
|
|
int j, ch, reverb_pos;
|
|
byte[] samples;
|
|
int rom_mask;
|
|
int offset1;
|
|
int base1_offset, base2_offset;
|
|
k054539_channel chan;
|
|
int cur_pos, cur_pfrac, cur_val, cur_pval;
|
|
int delta, rdelta, fdelta, pdelta;
|
|
int vol, bval, pan, i;
|
|
double gain, lvol, rvol, rbvol;
|
|
reverb_pos = info.reverb_pos;
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
for (j = 0; j < length; j++)
|
|
{
|
|
Sound.k054539stream.streamoutput_Ptrs[i][offset + j] = 0;
|
|
}
|
|
}
|
|
samples = k054539rom;//info.rom;
|
|
rom_mask = (int)info.rom_mask;
|
|
if ((info.regs[0x22f] & 1) == 0)
|
|
{
|
|
return;
|
|
}
|
|
info.reverb_pos = (reverb_pos + length) & 0x3fff;
|
|
for (ch = 0; ch < 8; ch++)
|
|
{
|
|
if ((info.regs[0x22c] & (1 << ch)) != 0)
|
|
{
|
|
base1_offset = 0x20 * ch;
|
|
base2_offset = 0x200 + 0x2 * ch;
|
|
chan = info.channels[ch];
|
|
delta = info.regs[base1_offset + 0x00] | (info.regs[base1_offset + 0x01] << 8) | (info.regs[base1_offset + 0x02] << 16);
|
|
vol = info.regs[base1_offset + 0x03];
|
|
bval = vol + info.regs[base1_offset + 0x04];
|
|
if (bval > 255)
|
|
{
|
|
bval = 255;
|
|
}
|
|
pan = info.regs[base1_offset + 0x05];
|
|
if (pan >= 0x81 && pan <= 0x8f)
|
|
{
|
|
pan -= 0x81;
|
|
}
|
|
else if (pan >= 0x11 && pan <= 0x1f)
|
|
{
|
|
pan -= 0x11;
|
|
}
|
|
else
|
|
{
|
|
pan = 0x18 - 0x11;
|
|
}
|
|
gain = info.k054539_gain[ch];
|
|
lvol = info.voltab[vol] * info.pantab[pan] * gain;
|
|
if (lvol > 1.80)
|
|
{
|
|
lvol = 1.80;
|
|
}
|
|
rvol = info.voltab[vol] * info.pantab[0xe - pan] * gain;
|
|
if (rvol > 1.80)
|
|
{
|
|
rvol = 1.80;
|
|
}
|
|
rbvol = info.voltab[bval] * gain / 2;
|
|
if (rbvol > 1.80)
|
|
{
|
|
rbvol = 1.80;
|
|
}
|
|
rdelta = (info.regs[base1_offset + 6] | (info.regs[base1_offset + 7] << 8)) >> 3;
|
|
rdelta = (int)(rdelta + reverb_pos) & 0x3fff;
|
|
cur_pos = (info.regs[base1_offset + 0x0c] | (info.regs[base1_offset + 0x0d] << 8) | (info.regs[base1_offset + 0x0e] << 16)) & rom_mask;
|
|
offset1 = offset;
|
|
if ((info.regs[base2_offset + 0] & 0x20) != 0)
|
|
{
|
|
delta = -delta;
|
|
fdelta = +0x10000;
|
|
pdelta = -1;
|
|
}
|
|
else
|
|
{
|
|
fdelta = -0x10000;
|
|
pdelta = +1;
|
|
}
|
|
if (cur_pos != chan.pos)
|
|
{
|
|
chan.pos = cur_pos;
|
|
cur_pfrac = 0;
|
|
cur_val = 0;
|
|
cur_pval = 0;
|
|
}
|
|
else
|
|
{
|
|
cur_pfrac = chan.pfrac;
|
|
cur_val = chan.val;
|
|
cur_pval = chan.pval;
|
|
}
|
|
switch (info.regs[base2_offset + 0] & 0xc)
|
|
{
|
|
case 0x0:
|
|
{
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
cur_pfrac += delta;
|
|
while ((cur_pfrac & ~0xffff) != 0)
|
|
{
|
|
cur_pfrac += fdelta;
|
|
cur_pos += pdelta;
|
|
cur_pval = cur_val;
|
|
cur_val = (short)(samples[cur_pos] << 8);
|
|
if (cur_val == unchecked((short)0x8000))
|
|
{
|
|
if ((info.regs[base2_offset + 1] & 1) != 0)
|
|
{
|
|
cur_pos = (info.regs[base1_offset + 0x08] | (info.regs[base1_offset + 0x09] << 8) | (info.regs[base1_offset + 0x0a] << 16)) & rom_mask;
|
|
cur_val = (short)(samples[cur_pos] << 8);
|
|
if (cur_val != (int)(unchecked((short)0x8000)))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
k054539_keyoff(ch);
|
|
goto end_channel_0;
|
|
}
|
|
}
|
|
Sound.k054539stream.streamoutput_Ptrs[0][offset1] += (short)(cur_val * lvol);
|
|
Sound.k054539stream.streamoutput_Ptrs[1][offset1] += (short)(cur_val * rvol);
|
|
offset1++;
|
|
info.ram[rdelta] += (short)(cur_val * rbvol);
|
|
rdelta++;
|
|
rdelta &= 0x3fff;
|
|
}
|
|
end_channel_0:
|
|
break;
|
|
}
|
|
case 0x4:
|
|
{
|
|
pdelta <<= 1;
|
|
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
cur_pfrac += delta;
|
|
while ((cur_pfrac & ~0xffff) != 0)
|
|
{
|
|
cur_pfrac += fdelta;
|
|
cur_pos += pdelta;
|
|
|
|
cur_pval = cur_val;
|
|
cur_val = (short)(samples[cur_pos] | samples[cur_pos + 1] << 8);
|
|
if (cur_val == unchecked((short)0x8000))
|
|
{
|
|
if ((info.regs[base2_offset + 1] & 1) != 0)
|
|
{
|
|
cur_pos = (info.regs[base1_offset + 0x08] | (info.regs[base1_offset + 0x09] << 8) | (info.regs[base1_offset + 0x0a] << 16)) & rom_mask;
|
|
cur_val = (short)(samples[cur_pos] | samples[cur_pos + 1] << 8);
|
|
if (cur_val != unchecked((short)0x8000))
|
|
continue;
|
|
}
|
|
k054539_keyoff(ch);
|
|
goto end_channel_4;
|
|
}
|
|
}
|
|
Sound.k054539stream.streamoutput_Ptrs[0][offset1] += (short)(cur_val * lvol);
|
|
Sound.k054539stream.streamoutput_Ptrs[1][offset1] += (short)(cur_val * rvol);
|
|
offset1++;
|
|
info.ram[rdelta] += (short)(cur_val * rbvol);
|
|
rdelta++;
|
|
rdelta &= 0x3fff;
|
|
}
|
|
end_channel_4:
|
|
break;
|
|
}
|
|
case 0x8:
|
|
{
|
|
cur_pos <<= 1;
|
|
cur_pfrac <<= 1;
|
|
if ((cur_pfrac & 0x10000) != 0)
|
|
{
|
|
cur_pfrac &= 0xffff;
|
|
cur_pos |= 1;
|
|
}
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
cur_pfrac += delta;
|
|
while ((cur_pfrac & ~0xffff) != 0)
|
|
{
|
|
cur_pfrac += fdelta;
|
|
cur_pos += pdelta;
|
|
cur_pval = cur_val;
|
|
cur_val = samples[cur_pos >> 1];
|
|
if (cur_val == 0x88)
|
|
{
|
|
if ((info.regs[base2_offset + 1] & 1) != 0)
|
|
{
|
|
cur_pos = ((info.regs[base1_offset + 0x08] | (info.regs[base1_offset + 0x09] << 8) | (info.regs[base1_offset + 0x0a] << 16)) & rom_mask) << 1;
|
|
cur_val = samples[cur_pos >> 1];
|
|
if (cur_val != 0x88)
|
|
goto next_iter;
|
|
}
|
|
k054539_keyoff(ch);
|
|
goto end_channel_8;
|
|
}
|
|
next_iter:
|
|
if ((cur_pos & 1) != 0)
|
|
{
|
|
cur_val >>= 4;
|
|
}
|
|
else
|
|
{
|
|
cur_val &= 15;
|
|
}
|
|
cur_val = cur_pval + dpcm[cur_val];
|
|
if (cur_val < -32768)
|
|
{
|
|
cur_val = -32768;
|
|
}
|
|
else if (cur_val > 32767)
|
|
{
|
|
cur_val = 32767;
|
|
}
|
|
}
|
|
Sound.k054539stream.streamoutput_Ptrs[0][offset1] += (short)(cur_val * lvol);
|
|
Sound.k054539stream.streamoutput_Ptrs[1][offset1] += (short)(cur_val * rvol);
|
|
offset1++;
|
|
info.ram[rdelta] += (short)(cur_val * rbvol);
|
|
rdelta++;
|
|
rdelta &= 0x3fff;
|
|
}
|
|
end_channel_8:
|
|
cur_pfrac >>= 1;
|
|
if ((cur_pos & 1) != 0)
|
|
{
|
|
cur_pfrac |= 0x8000;
|
|
}
|
|
cur_pos >>= 1;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
chan.pos = cur_pos;
|
|
chan.pfrac = cur_pfrac;
|
|
chan.pval = cur_pval;
|
|
chan.val = cur_val;
|
|
if (k054539_regupdate())
|
|
{
|
|
info.regs[base1_offset + 0x0c] = (byte)(cur_pos & 0xff);
|
|
info.regs[base1_offset + 0x0d] = (byte)(cur_pos >> 8 & 0xff);
|
|
info.regs[base1_offset + 0x0e] = (byte)(cur_pos >> 16 & 0xff);
|
|
}
|
|
}
|
|
}
|
|
if ((info.k054539_flags & 2) == 0)
|
|
{
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
short val = info.ram[(i + reverb_pos) & 0x3fff];
|
|
Sound.k054539stream.streamoutput_Ptrs[0][offset + i] += val;
|
|
Sound.k054539stream.streamoutput_Ptrs[1][offset + i] += val;
|
|
}
|
|
}
|
|
if (reverb_pos + length > 0x4000)
|
|
{
|
|
i = 0x4000 - reverb_pos;
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
info.ram[reverb_pos + j] = 0;
|
|
}
|
|
for (j = 0; j < length - i; j++)
|
|
{
|
|
info.ram[j] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < length; j++)
|
|
{
|
|
info.ram[reverb_pos + j] = 0;
|
|
}
|
|
}
|
|
}
|
|
public static void k054539_irq()
|
|
{
|
|
if ((info.regs[0x22f] & 0x20) != 0)
|
|
{
|
|
irq();
|
|
}
|
|
}
|
|
public static void k054539_init_chip(int clock)
|
|
{
|
|
int i;
|
|
info.k054539_flags |= 4;
|
|
info.ram = new short[0x4000 + clock / 50];
|
|
info.reverb_pos = 0;
|
|
info.cur_ptr = 0;
|
|
info.rom_size = k054539rom.Length;
|
|
info.rom_mask = 0xffffffff;
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
if ((1U << i) >= info.rom_size)
|
|
{
|
|
info.rom_mask = (1U << i) - 1;
|
|
break;
|
|
}
|
|
}
|
|
if (irq != null)
|
|
{
|
|
EmuTimer.timer_pulse_internal(new Atime(0, (long)(1e18 / 480)), EmuTimer.TIME_ACT.K054539_k054539_irq);
|
|
}
|
|
}
|
|
static void k054539_w(int chip, int offset, byte data)
|
|
{
|
|
int latch, offs, ch, pan;
|
|
int regptr_offset;
|
|
latch = ((info.k054539_flags & 4) != 0) && ((info.regs[0x22f] & 1) != 0) ? 1 : 0;
|
|
if (latch != 0 && offset < 0x100)
|
|
{
|
|
offs = (offset & 0x1f) - 0xc;
|
|
ch = offset >> 5;
|
|
if (offs >= 0 && offs <= 2)
|
|
{
|
|
info.k054539_posreg_latch[ch][offs] = data;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0x13f:
|
|
pan = data >= 0x11 && data <= 0x1f ? data - 0x11 : 0x18 - 0x11;
|
|
if (apan != null)
|
|
{
|
|
apan(info.pantab[pan], info.pantab[0xe - pan]);
|
|
}
|
|
break;
|
|
case 0x214:
|
|
if (latch != 0)
|
|
{
|
|
for (ch = 0; ch < 8; ch++)
|
|
{
|
|
if ((data & (1 << ch)) != 0)
|
|
{
|
|
regptr_offset = (ch << 5) + 0xc;
|
|
info.regs[regptr_offset] = info.k054539_posreg_latch[ch][0];
|
|
info.regs[regptr_offset + 1] = info.k054539_posreg_latch[ch][1];
|
|
info.regs[regptr_offset + 2] = info.k054539_posreg_latch[ch][2];
|
|
k054539_keyon(ch);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (ch = 0; ch < 8; ch++)
|
|
{
|
|
if ((data & (1 << ch)) != 0)
|
|
{
|
|
k054539_keyon(ch);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 0x215:
|
|
for (ch = 0; ch < 8; ch++)
|
|
{
|
|
if ((data & (1 << ch)) != 0)
|
|
{
|
|
k054539_keyoff(ch);
|
|
}
|
|
}
|
|
break;
|
|
case 0x22d:
|
|
if (info.regs[0x22e] == 0x80)
|
|
{
|
|
if (zoneflag == 1)
|
|
{
|
|
if (info.cur_ptr % 2 == 0)
|
|
{
|
|
info.ram[info.cur_ptr / 2] = (short)((data << 8) | (info.ram[info.cur_ptr / 2] & 0xff));
|
|
}
|
|
else if (info.cur_ptr % 2 == 1)
|
|
{
|
|
info.ram[info.cur_ptr / 2] = (short)((info.ram[info.cur_ptr / 2] & 0xff00) | data);
|
|
}
|
|
}
|
|
else if (zoneflag == 2)
|
|
{
|
|
k054539rom[zonedata + info.cur_ptr] = data;
|
|
}
|
|
}
|
|
info.cur_ptr++;
|
|
if (info.cur_ptr == info.cur_limit)
|
|
{
|
|
info.cur_ptr = 0;
|
|
}
|
|
break;
|
|
case 0x22e:
|
|
if (data == 0x80)
|
|
{
|
|
zoneflag = 1;
|
|
}
|
|
else
|
|
{
|
|
zoneflag = 2;
|
|
zonedata = 0x20000 * data;
|
|
}
|
|
info.cur_limit = data == 0x80 ? 0x4000 : 0x20000;
|
|
info.cur_ptr = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
info.regs[offset] = data;
|
|
}
|
|
public static byte k054539_r(int chip, int offset)
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0x22d:
|
|
if ((info.regs[0x22f] & 0x10) != 0)
|
|
{
|
|
byte res = 0;
|
|
if (zoneflag == 1)
|
|
{
|
|
if (info.cur_ptr % 2 == 0)
|
|
{
|
|
res = (byte)(info.ram[info.cur_ptr / 2] >> 8);
|
|
}
|
|
else if (info.cur_ptr % 2 == 1)
|
|
{
|
|
res = (byte)info.ram[info.cur_ptr / 2];
|
|
}
|
|
}
|
|
else if (zoneflag == 2)
|
|
{
|
|
res = k054539rom[zonedata + info.cur_ptr];
|
|
}
|
|
info.cur_ptr++;
|
|
if (info.cur_ptr == info.cur_limit)
|
|
{
|
|
info.cur_ptr = 0;
|
|
}
|
|
return res;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
case 0x22c:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return info.regs[offset];
|
|
}
|
|
public static void k054539_start(int clock)
|
|
{
|
|
int i;
|
|
info = new k054539_info();
|
|
info.voltab = new double[256];
|
|
info.pantab = new double[0xf];
|
|
info.k054539_gain = new double[8];
|
|
info.k054539_posreg_latch = new byte[8][];
|
|
info.regs = new byte[0x230];
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
info.k054539_gain[i] = 1.0;
|
|
info.k054539_posreg_latch[i] = new byte[3];
|
|
}
|
|
info.k054539_flags = 0;
|
|
info.channels = new k054539_channel[8];
|
|
irq = null;
|
|
switch (Machine.sBoard)
|
|
{
|
|
case "Konami 68000":
|
|
switch (Machine.sName)
|
|
{
|
|
case "prmrsocr":
|
|
irq = Konami68000.sound_nmi;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
info.voltab[i] = Math.Pow(10.0, (-36.0 * (double)i / (double)0x40) / 20.0) / 4.0;
|
|
}
|
|
for (i = 0; i < 0xf; i++)
|
|
{
|
|
info.pantab[i] = Math.Sqrt(i) / Math.Sqrt(0xe);
|
|
}
|
|
k054539_init_chip(clock);
|
|
}
|
|
public static void k054539_0_w(int offset, byte data)
|
|
{
|
|
k054539_w(0, offset, data);
|
|
}
|
|
public static byte k054539_0_r(int offset)
|
|
{
|
|
return k054539_r(0, offset);
|
|
}
|
|
public static void SaveStateBinary(BinaryWriter writer)
|
|
{
|
|
int i, j;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
writer.Write(info.k054539_posreg_latch[i][j]);
|
|
}
|
|
}
|
|
writer.Write(info.k054539_flags);
|
|
writer.Write(info.regs, 0, 0x230);
|
|
for (i = 0; i < info.ram.Length; i++)
|
|
{
|
|
writer.Write(info.ram[i]);
|
|
}
|
|
writer.Write(info.reverb_pos);
|
|
writer.Write(info.cur_ptr);
|
|
writer.Write(info.cur_limit);
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
writer.Write(info.channels[i].pos);
|
|
writer.Write(info.channels[i].pfrac);
|
|
writer.Write(info.channels[i].val);
|
|
writer.Write(info.channels[i].pval);
|
|
}
|
|
writer.Write(zoneflag);
|
|
writer.Write(zonedata);
|
|
}
|
|
public static void LoadStateBinary(BinaryReader reader)
|
|
{
|
|
int i, j;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
info.k054539_posreg_latch[i][j] = reader.ReadByte();
|
|
}
|
|
}
|
|
info.k054539_flags = reader.ReadInt32();
|
|
info.regs = reader.ReadBytes(0x230);
|
|
for (i = 0; i < info.ram.Length; i++)
|
|
{
|
|
info.ram[i] = reader.ReadInt16();
|
|
}
|
|
info.reverb_pos = reader.ReadInt32();
|
|
info.cur_ptr = reader.ReadInt32();
|
|
info.cur_limit = reader.ReadInt32();
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
info.channels[i].pos = reader.ReadInt32();
|
|
info.channels[i].pfrac = reader.ReadInt32();
|
|
info.channels[i].val = reader.ReadInt32();
|
|
info.channels[i].pval = reader.ReadInt32();
|
|
}
|
|
zoneflag = reader.ReadInt32();
|
|
zonedata = reader.ReadInt32();
|
|
}
|
|
}
|
|
}
|