1339 lines
49 KiB
C#
1339 lines
49 KiB
C#
using System;
|
|
|
|
namespace MAME.Core
|
|
{
|
|
public unsafe class FMOpl
|
|
{
|
|
public struct OPL_SLOT
|
|
{
|
|
public uint ar;
|
|
public uint dr;
|
|
public uint rr;
|
|
public byte KSR;
|
|
public byte ksl;
|
|
public byte ksr;
|
|
public byte mul;
|
|
|
|
public uint Cnt;
|
|
public uint Incr;
|
|
public byte FB;
|
|
public int iconnect;
|
|
public int[] op1_out;
|
|
public byte CON;
|
|
|
|
public byte eg_type;
|
|
public byte state;
|
|
public uint TL;
|
|
public int TLL;
|
|
public int volume;
|
|
public uint sl;
|
|
public byte eg_sh_ar;
|
|
public byte eg_sel_ar;
|
|
public byte eg_sh_dr;
|
|
public byte eg_sel_dr;
|
|
public byte eg_sh_rr;
|
|
public byte eg_sel_rr;
|
|
public uint key;
|
|
|
|
public uint AMmask;
|
|
public byte vib;
|
|
|
|
public ushort wavetable;
|
|
}
|
|
public struct OPL_CH
|
|
{
|
|
public OPL_SLOT[] SLOT;
|
|
public uint block_fnum;
|
|
public uint fc;
|
|
public uint ksl_base;
|
|
public byte kcode;
|
|
}
|
|
public class FM_OPL
|
|
{
|
|
public OPL_CH[] P_CH;
|
|
|
|
public uint eg_cnt;
|
|
public uint eg_timer;
|
|
public uint eg_timer_add;
|
|
public uint eg_timer_overflow;
|
|
|
|
public byte rhythm;
|
|
|
|
public uint[] fn_tab;
|
|
|
|
public byte lfo_am_depth;
|
|
public byte lfo_pm_depth_range;
|
|
public uint lfo_am_cnt;
|
|
public uint lfo_am_inc;
|
|
public uint lfo_pm_cnt;
|
|
public uint lfo_pm_inc;
|
|
|
|
public uint noise_rng;
|
|
public uint noise_p;
|
|
public uint noise_f;
|
|
|
|
public byte wavesel;
|
|
|
|
public uint[] T;
|
|
public byte[] st;
|
|
|
|
public YMDeltat.YM_DELTAT deltat;
|
|
|
|
public byte portDirection;
|
|
public byte portLatch;
|
|
public OPL_PORTHANDLER_R porthandler_r;
|
|
public OPL_PORTHANDLER_W porthandler_w;
|
|
public OPL_PORTHANDLER_R keyboardhandler_r;
|
|
public OPL_PORTHANDLER_W keyboardhandler_w;
|
|
|
|
public OPL_TIMERHANDLER timer_handler;
|
|
public OPL_IRQHANDLER IRQHandler;
|
|
public OPL_UPDATEHANDLER UpdateHandler;
|
|
|
|
public byte type;
|
|
public byte address;
|
|
public byte status;
|
|
public byte statusmask;
|
|
public byte mode;
|
|
|
|
public int clock;
|
|
public int rate;
|
|
public double freqbase;
|
|
public Atime TimerBase;
|
|
public void OPLResetChip()
|
|
{
|
|
int c, s;
|
|
int i;
|
|
eg_timer = 0;
|
|
eg_cnt = 0;
|
|
noise_rng = 1;
|
|
mode = 0;
|
|
OPL_STATUS_RESET(0x7f);
|
|
OPLWriteReg(0x01, 0);
|
|
OPLWriteReg(0x02, 0);
|
|
OPLWriteReg(0x03, 0);
|
|
OPLWriteReg(0x04, 0);
|
|
for (i = 0xff; i >= 0x20; i--)
|
|
{
|
|
OPLWriteReg(i, 0);
|
|
}
|
|
for (c = 0; c < 9; c++)
|
|
{
|
|
for (s = 0; s < 2; s++)
|
|
{
|
|
P_CH[c].SLOT[s].wavetable = 0;
|
|
P_CH[c].SLOT[s].state = 0;
|
|
P_CH[c].SLOT[s].volume = 0x1ff;
|
|
}
|
|
}
|
|
}
|
|
public void OPL_initalize()
|
|
{
|
|
int i;
|
|
freqbase = (rate != 0) ? ((double)clock / 72.0) / rate : 0;
|
|
TimerBase = Attotime.attotime_mul(Attotime.ATTOTIME_IN_HZ((int)clock), 72);
|
|
for (i = 0; i < 1024; i++)
|
|
{
|
|
fn_tab[i] = (uint)((double)i * 64 * freqbase * (1 << (16 - 10)));
|
|
}
|
|
lfo_am_inc = (uint)(0x40000 * freqbase);
|
|
lfo_pm_inc = (uint)(0x4000 * freqbase);
|
|
noise_f = (uint)(0x10000 * freqbase);
|
|
eg_timer_add = (uint)(0x10000 * freqbase);
|
|
eg_timer_overflow = 0x10000;
|
|
}
|
|
private void FM_KEYON(int chan, int slot, uint key_set)
|
|
{
|
|
if (P_CH[chan].SLOT[slot].key == 0)
|
|
{
|
|
P_CH[chan].SLOT[slot].Cnt = 0;
|
|
P_CH[chan].SLOT[slot].state = 4;
|
|
}
|
|
P_CH[chan].SLOT[slot].key |= key_set;
|
|
}
|
|
private void FM_KEYOFF(int chan, int slot, uint key_clr)
|
|
{
|
|
if (P_CH[chan].SLOT[slot].key != 0)
|
|
{
|
|
P_CH[chan].SLOT[slot].key &= key_clr;
|
|
if (P_CH[chan].SLOT[slot].key == 0)
|
|
{
|
|
if (P_CH[chan].SLOT[slot].state > 1)
|
|
P_CH[chan].SLOT[slot].state = 1;
|
|
}
|
|
}
|
|
}
|
|
private void CALC_FCSLOT(int chan, int slot)
|
|
{
|
|
int ksr;
|
|
P_CH[chan].SLOT[slot].Incr = P_CH[chan].fc * P_CH[chan].SLOT[slot].mul;
|
|
ksr = P_CH[chan].kcode >> P_CH[chan].SLOT[slot].KSR;
|
|
if (P_CH[chan].SLOT[slot].ksr != ksr)
|
|
{
|
|
P_CH[chan].SLOT[slot].ksr = (byte)ksr;
|
|
if ((P_CH[chan].SLOT[slot].ar + P_CH[chan].SLOT[slot].ksr) < 78)
|
|
{
|
|
P_CH[chan].SLOT[slot].eg_sh_ar = eg_rate_shift[P_CH[chan].SLOT[slot].ar + P_CH[chan].SLOT[slot].ksr];
|
|
P_CH[chan].SLOT[slot].eg_sel_ar = eg_rate_select[P_CH[chan].SLOT[slot].ar + P_CH[chan].SLOT[slot].ksr];
|
|
}
|
|
else
|
|
{
|
|
P_CH[chan].SLOT[slot].eg_sh_ar = 0;
|
|
P_CH[chan].SLOT[slot].eg_sel_ar = 13 * 8;
|
|
}
|
|
P_CH[chan].SLOT[slot].eg_sh_dr = eg_rate_shift[P_CH[chan].SLOT[slot].dr + P_CH[chan].SLOT[slot].ksr];
|
|
P_CH[chan].SLOT[slot].eg_sel_dr = eg_rate_select[P_CH[chan].SLOT[slot].dr + P_CH[chan].SLOT[slot].ksr];
|
|
P_CH[chan].SLOT[slot].eg_sh_rr = eg_rate_shift[P_CH[chan].SLOT[slot].rr + P_CH[chan].SLOT[slot].ksr];
|
|
P_CH[chan].SLOT[slot].eg_sel_rr = eg_rate_select[P_CH[chan].SLOT[slot].rr + P_CH[chan].SLOT[slot].ksr];
|
|
}
|
|
}
|
|
private void set_mul(int slot, int v)
|
|
{
|
|
P_CH[slot / 2].SLOT[slot & 1].mul = mul_tab[v & 0x0f];
|
|
P_CH[slot / 2].SLOT[slot & 1].KSR = (byte)((v & 0x10) != 0 ? 0 : 2);
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_type = (byte)(v & 0x20);
|
|
P_CH[slot / 2].SLOT[slot & 1].vib = (byte)(v & 0x40);
|
|
P_CH[slot / 2].SLOT[slot & 1].AMmask = (uint)((v & 0x80) != 0 ? ~0 : 0);
|
|
CALC_FCSLOT(slot / 2, slot & 1);
|
|
}
|
|
private void set_ksl_tl(int slot, int v)
|
|
{
|
|
int ksl = v >> 6;
|
|
P_CH[slot / 2].SLOT[slot & 1].ksl = (byte)(ksl != 0 ? 3 - ksl : 31);
|
|
P_CH[slot / 2].SLOT[slot & 1].TL = (uint)((v & 0x3f) << 2);
|
|
P_CH[slot / 2].SLOT[slot & 1].TLL = (int)(P_CH[slot / 2].SLOT[slot & 1].TL + (P_CH[slot / 2].ksl_base >> P_CH[slot / 2].SLOT[slot & 1].ksl));
|
|
}
|
|
private void set_ar_dr(int slot, int v)
|
|
{
|
|
P_CH[slot / 2].SLOT[slot & 1].ar = (uint)((v >> 4) != 0 ? 16 + ((v >> 4) << 2) : 0);
|
|
if ((P_CH[slot / 2].SLOT[slot & 1].ar + P_CH[slot / 2].SLOT[slot & 1].ksr) < 16 + 62)
|
|
{
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_sh_ar = eg_rate_shift[P_CH[slot / 2].SLOT[slot & 1].ar + P_CH[slot / 2].SLOT[slot & 1].ksr];
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_sel_ar = eg_rate_select[P_CH[slot / 2].SLOT[slot & 1].ar + P_CH[slot / 2].SLOT[slot & 1].ksr];
|
|
}
|
|
else
|
|
{
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_sh_ar = 0;
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_sel_ar = 13 * 8;
|
|
}
|
|
P_CH[slot / 2].SLOT[slot & 1].dr = (uint)((v & 0x0f) != 0 ? 16 + ((v & 0x0f) << 2) : 0);
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_sh_dr = eg_rate_shift[P_CH[slot / 2].SLOT[slot & 1].dr + P_CH[slot / 2].SLOT[slot & 1].ksr];
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_sel_dr = eg_rate_select[P_CH[slot / 2].SLOT[slot & 1].dr + P_CH[slot / 2].SLOT[slot & 1].ksr];
|
|
}
|
|
private void set_sl_rr(int slot, int v)
|
|
{
|
|
P_CH[slot / 2].SLOT[slot & 1].sl = sl_tab[v >> 4];
|
|
P_CH[slot / 2].SLOT[slot & 1].rr = (uint)((v & 0x0f) != 0 ? 16 + ((v & 0x0f) << 2) : 0);
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_sh_rr = eg_rate_shift[P_CH[slot / 2].SLOT[slot & 1].rr + P_CH[slot / 2].SLOT[slot & 1].ksr];
|
|
P_CH[slot / 2].SLOT[slot & 1].eg_sel_rr = eg_rate_select[P_CH[slot / 2].SLOT[slot & 1].rr + P_CH[slot / 2].SLOT[slot & 1].ksr];
|
|
}
|
|
public void OPLWriteReg(int r, int v)
|
|
{
|
|
int slot;
|
|
int block_fnum;
|
|
r &= 0xff;
|
|
v &= 0xff;
|
|
switch (r & 0xe0)
|
|
{
|
|
case 0x00:
|
|
switch (r & 0x1f)
|
|
{
|
|
case 0x01:
|
|
if ((type & 0x01) != 0)
|
|
{
|
|
wavesel = (byte)(v & 0x20);
|
|
}
|
|
break;
|
|
case 0x02:
|
|
T[0] = (uint)((256 - v) * 4);
|
|
break;
|
|
case 0x03:
|
|
T[1] = (uint)((256 - v) * 16);
|
|
break;
|
|
case 0x04:
|
|
if ((v & 0x80) != 0)
|
|
{
|
|
OPL_STATUS_RESET(0x7f - 0x08);
|
|
}
|
|
else
|
|
{
|
|
byte st1 = (byte)(v & 1);
|
|
byte st2 = (byte)((v >> 1) & 1);
|
|
OPL_STATUS_RESET(v & (0x78 - 0x08));
|
|
OPL_STATUSMASK_SET((~v) & 0x78);
|
|
if (st[1] != st2)
|
|
{
|
|
Atime period = st2 != 0 ? Attotime.attotime_mul(TimerBase, T[1]) : Attotime.ATTOTIME_ZERO;
|
|
st[1] = st2;
|
|
if (timer_handler != null)
|
|
{
|
|
timer_handler(1, period);
|
|
}
|
|
}
|
|
if (st[0] != st1)
|
|
{
|
|
Atime period = st1 != 0 ? Attotime.attotime_mul(TimerBase, T[0]) : Attotime.ATTOTIME_ZERO;
|
|
st[0] = st1;
|
|
if (timer_handler != null)
|
|
{
|
|
timer_handler(0, period);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 0x08:
|
|
mode = (byte)v;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case 0x20:
|
|
slot = slot_array[r & 0x1f];
|
|
if (slot < 0)
|
|
{
|
|
return;
|
|
}
|
|
set_mul(slot, v);
|
|
break;
|
|
case 0x40:
|
|
slot = slot_array[r & 0x1f];
|
|
if (slot < 0)
|
|
{
|
|
return;
|
|
}
|
|
set_ksl_tl(slot, v);
|
|
break;
|
|
case 0x60:
|
|
slot = slot_array[r & 0x1f];
|
|
if (slot < 0)
|
|
{
|
|
return;
|
|
}
|
|
set_ar_dr(slot, v);
|
|
break;
|
|
case 0x80:
|
|
slot = slot_array[r & 0x1f];
|
|
if (slot < 0)
|
|
{
|
|
return;
|
|
}
|
|
set_sl_rr(slot, v);
|
|
break;
|
|
case 0xa0:
|
|
if (r == 0xbd)
|
|
{
|
|
lfo_am_depth = (byte)(v & 0x80);
|
|
lfo_pm_depth_range = (byte)((v & 0x40) != 0 ? 8 : 0);
|
|
rhythm = (byte)(v & 0x3f);
|
|
if ((rhythm & 0x20) != 0)
|
|
{
|
|
if ((v & 0x10) != 0)
|
|
{
|
|
FM_KEYON(6, 0, 2);
|
|
FM_KEYON(6, 1, 2);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(6, 0, unchecked((uint)(~2)));
|
|
FM_KEYOFF(6, 1, unchecked((uint)(~2)));
|
|
}
|
|
if ((v & 0x01) != 0)
|
|
{
|
|
FM_KEYON(7, 0, 2);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(7, 0, unchecked((uint)(~2)));
|
|
}
|
|
if ((v & 0x08) != 0)
|
|
{
|
|
FM_KEYON(7, 1, 2);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(7, 1, unchecked((uint)(~2)));
|
|
}
|
|
if ((v & 0x04) != 0)
|
|
{
|
|
FM_KEYON(8, 0, 2);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(8, 0, unchecked((uint)(~2)));
|
|
}
|
|
if ((v & 0x02) != 0)
|
|
{
|
|
FM_KEYON(8, 1, 2);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(8, 1, unchecked((uint)(~2)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(6, 0, unchecked((uint)(~2)));
|
|
FM_KEYOFF(6, 1, unchecked((uint)(~2)));
|
|
FM_KEYOFF(7, 0, unchecked((uint)(~2)));
|
|
FM_KEYOFF(7, 1, unchecked((uint)(~2)));
|
|
FM_KEYOFF(8, 0, unchecked((uint)(~2)));
|
|
FM_KEYOFF(8, 1, unchecked((uint)(~2)));
|
|
}
|
|
return;
|
|
}
|
|
if ((r & 0x0f) > 8)
|
|
{
|
|
return;
|
|
}
|
|
if ((r & 0x10) == 0)
|
|
{
|
|
block_fnum = (int)((P_CH[r & 0x0f].block_fnum & 0x1f00) | (uint)v);
|
|
}
|
|
else
|
|
{
|
|
block_fnum = (int)((uint)((v & 0x1f) << 8) | (P_CH[r & 0x0f].block_fnum & 0xff));
|
|
if ((v & 0x20) != 0)
|
|
{
|
|
FM_KEYON(r & 0x0f, 0, 1);
|
|
FM_KEYON(r & 0x0f, 1, 1);
|
|
}
|
|
else
|
|
{
|
|
FM_KEYOFF(r & 0x0f, 0, unchecked((uint)(~1)));
|
|
FM_KEYOFF(r & 0x0f, 1, unchecked((uint)(~1)));
|
|
}
|
|
}
|
|
if (P_CH[r & 0x0f].block_fnum != block_fnum)
|
|
{
|
|
byte block = (byte)(block_fnum >> 10);
|
|
P_CH[r & 0x0f].block_fnum = (uint)block_fnum;
|
|
P_CH[r & 0x0f].ksl_base = ksl_tab[block_fnum >> 6];
|
|
P_CH[r & 0x0f].fc = fn_tab[block_fnum & 0x03ff] >> (7 - block);
|
|
P_CH[r & 0x0f].kcode = (byte)((P_CH[r & 0x0f].block_fnum & 0x1c00) >> 9);
|
|
if ((mode & 0x40) != 0)
|
|
{
|
|
P_CH[r & 0x0f].kcode |= (byte)((P_CH[r & 0x0f].block_fnum & 0x100) >> 8);
|
|
}
|
|
else
|
|
{
|
|
P_CH[r & 0x0f].kcode |= (byte)((P_CH[r & 0x0f].block_fnum & 0x200) >> 9);
|
|
}
|
|
P_CH[r & 0x0f].SLOT[0].TLL = (int)(P_CH[r & 0x0f].SLOT[0].TL + (P_CH[r & 0x0f].ksl_base >> P_CH[r & 0x0f].SLOT[0].ksl));
|
|
P_CH[r & 0x0f].SLOT[1].TLL = (int)(P_CH[r & 0x0f].SLOT[1].TL + (P_CH[r & 0x0f].ksl_base >> P_CH[r & 0x0f].SLOT[1].ksl));
|
|
CALC_FCSLOT(r & 0x0f, 0);
|
|
CALC_FCSLOT(r & 0x0f, 1);
|
|
}
|
|
break;
|
|
case 0xc0:
|
|
if ((r & 0x0f) > 8)
|
|
{
|
|
return;
|
|
}
|
|
P_CH[r & 0x0f].SLOT[0].FB = (byte)(((v >> 1) & 7) != 0 ? ((v >> 1) & 7) + 7 : 0);
|
|
P_CH[r & 0x0f].SLOT[0].CON = (byte)(v & 1);
|
|
P_CH[r & 0x0f].SLOT[0].iconnect = P_CH[r & 0x0f].SLOT[0].CON != 0 ? 1 : 2;
|
|
break;
|
|
case 0xe0:
|
|
if (wavesel != 0)
|
|
{
|
|
slot = slot_array[r & 0x1f];
|
|
if (slot < 0)
|
|
{
|
|
return;
|
|
}
|
|
P_CH[slot / 2].SLOT[slot & 1].wavetable = (ushort)((v & 0x03) * 0x400);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
public int OPLWrite(int a, int v)
|
|
{
|
|
if ((a & 1) == 0)
|
|
{
|
|
address = (byte)(v & 0xff);
|
|
}
|
|
else
|
|
{
|
|
if (UpdateHandler != null)
|
|
{
|
|
UpdateHandler(0);
|
|
}
|
|
OPLWriteReg(address, v);
|
|
}
|
|
return status >> 7;
|
|
}
|
|
public byte OPLRead(int a)
|
|
{
|
|
if ((a & 1) == 0)
|
|
{
|
|
return (byte)(status & (statusmask | 0x80));
|
|
}
|
|
return 0xff;
|
|
}
|
|
public int op_calc1(uint phase, int env, int pm, int wave_tab)
|
|
{
|
|
uint p;
|
|
p = (uint)((env << 4) + sin_tab[wave_tab + ((((int)((phase & ~0xffff) + pm)) >> 16) & 0x3ff)]);
|
|
if (p >= 0x1800)
|
|
{
|
|
return 0;
|
|
}
|
|
return tl_tab[p];
|
|
}
|
|
public uint volume_calc(int chan, int slot)
|
|
{
|
|
uint i1;
|
|
i1 = (uint)(P_CH[chan].SLOT[slot].TLL + (uint)P_CH[chan].SLOT[slot].volume + (LFO_AM & P_CH[chan].SLOT[slot].AMmask));
|
|
return i1;
|
|
}
|
|
public void OPL_CALC_CH(int chan)
|
|
{
|
|
uint env;
|
|
int out1;
|
|
phase_modulation = 0;
|
|
env = volume_calc(chan, 0);
|
|
out1 = P_CH[chan].SLOT[0].op1_out[0] + P_CH[chan].SLOT[0].op1_out[1];
|
|
P_CH[chan].SLOT[0].op1_out[0] = P_CH[chan].SLOT[0].op1_out[1];
|
|
if (P_CH[chan].SLOT[0].iconnect == 1)
|
|
{
|
|
output0 += P_CH[chan].SLOT[0].op1_out[0];
|
|
}
|
|
else if (P_CH[chan].SLOT[0].iconnect == 2)
|
|
{
|
|
phase_modulation += P_CH[chan].SLOT[0].op1_out[0];
|
|
}
|
|
else
|
|
{
|
|
|
|
}
|
|
P_CH[chan].SLOT[0].op1_out[1] = 0;
|
|
if (env < 0x180)
|
|
{
|
|
if (P_CH[chan].SLOT[0].FB == 0)
|
|
{
|
|
out1 = 0;
|
|
}
|
|
P_CH[chan].SLOT[0].op1_out[1] = op_calc1(P_CH[chan].SLOT[0].Cnt, (int)env, (out1 << P_CH[chan].SLOT[0].FB), P_CH[chan].SLOT[0].wavetable);
|
|
}
|
|
env = volume_calc(chan, 1);
|
|
if (env < 0x180)
|
|
{
|
|
output0 += op_calc(P_CH[chan].SLOT[1].Cnt, (int)env, phase_modulation, P_CH[chan].SLOT[1].wavetable);
|
|
}
|
|
}
|
|
public void OPL_CALC_RH(uint noise)
|
|
{
|
|
int out1;
|
|
uint env;
|
|
phase_modulation = 0;
|
|
env = volume_calc(6, 0);
|
|
out1 = P_CH[6].SLOT[0].op1_out[0] + P_CH[6].SLOT[0].op1_out[1];
|
|
P_CH[6].SLOT[0].op1_out[0] = P_CH[6].SLOT[0].op1_out[1];
|
|
if (P_CH[6].SLOT[0].CON == 0)
|
|
{
|
|
phase_modulation = P_CH[6].SLOT[0].op1_out[0];
|
|
}
|
|
P_CH[6].SLOT[0].op1_out[1] = 0;
|
|
if (env < 0x180)
|
|
{
|
|
if (P_CH[6].SLOT[0].FB == 0)
|
|
{
|
|
out1 = 0;
|
|
}
|
|
P_CH[6].SLOT[0].op1_out[1] = op_calc1(P_CH[6].SLOT[0].Cnt, (int)env, (out1 << P_CH[6].SLOT[0].FB), P_CH[6].SLOT[0].wavetable);
|
|
}
|
|
env = volume_calc(6, 1);
|
|
if (env < 0x180)
|
|
{
|
|
output0 += op_calc(P_CH[6].SLOT[1].Cnt, (int)env, phase_modulation, P_CH[6].SLOT[1].wavetable) * 2;
|
|
}
|
|
env = volume_calc(7, 0);
|
|
if (env < 0x180)
|
|
{
|
|
byte bit7 = (byte)(((P_CH[7].SLOT[0].Cnt >> 16) >> 7) & 1);
|
|
byte bit3 = (byte)(((P_CH[7].SLOT[0].Cnt >> 16) >> 3) & 1);
|
|
byte bit2 = (byte)(((P_CH[7].SLOT[0].Cnt >> 16) >> 2) & 1);
|
|
byte res1 = (byte)((bit2 ^ bit7) | bit3);
|
|
uint phase = (uint)(res1 != 0 ? (0x200 | (0xd0 >> 2)) : 0xd0);
|
|
byte bit5e = (byte)(((P_CH[8].SLOT[1].Cnt >> 16) >> 5) & 1);
|
|
byte bit3e = (byte)(((P_CH[8].SLOT[1].Cnt >> 16) >> 3) & 1);
|
|
byte res2 = (byte)(bit3e ^ bit5e);
|
|
if (res2 != 0)
|
|
{
|
|
phase = (0x200 | (0xd0 >> 2));
|
|
}
|
|
if ((phase & 0x200) != 0)
|
|
{
|
|
if (noise != 0)
|
|
{
|
|
phase = 0x200 | 0xd0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (noise != 0)
|
|
{
|
|
phase = 0xd0 >> 2;
|
|
}
|
|
}
|
|
output0 += op_calc(phase << 16, (int)env, 0, P_CH[7].SLOT[0].wavetable) * 2;
|
|
}
|
|
env = volume_calc(7, 1);
|
|
if (env < 0x180)
|
|
{
|
|
byte bit8 = (byte)(((P_CH[7].SLOT[0].Cnt >> 16) >> 8) & 1);
|
|
uint phase = (uint)(bit8 != 0 ? 0x200 : 0x100);
|
|
if (noise != 0)
|
|
{
|
|
phase ^= 0x100;
|
|
}
|
|
output0 += op_calc(phase << 16, (int)env, 0, P_CH[7].SLOT[1].wavetable) * 2;
|
|
}
|
|
env = volume_calc(8, 0);
|
|
if (env < 0x180)
|
|
{
|
|
output0 += op_calc(P_CH[8].SLOT[0].Cnt, (int)env, 0, P_CH[8].SLOT[0].wavetable) * 2;
|
|
}
|
|
env = volume_calc(8, 1);
|
|
if (env < 0x180)
|
|
{
|
|
byte bit7 = (byte)(((P_CH[7].SLOT[0].Cnt >> 16) >> 7) & 1);
|
|
byte bit3 = (byte)(((P_CH[7].SLOT[0].Cnt >> 16) >> 3) & 1);
|
|
byte bit2 = (byte)(((P_CH[7].SLOT[0].Cnt >> 16) >> 2) & 1);
|
|
byte res1 = (byte)((bit2 ^ bit7) | bit3);
|
|
uint phase = (uint)(res1 != 0 ? 0x300 : 0x100);
|
|
byte bit5e = (byte)(((P_CH[8].SLOT[1].Cnt >> 16) >> 5) & 1);
|
|
byte bit3e = (byte)(((P_CH[8].SLOT[1].Cnt >> 16) >> 3) & 1);
|
|
byte res2 = (byte)(bit3e ^ bit5e);
|
|
if (res2 != 0)
|
|
{
|
|
phase = 0x300;
|
|
}
|
|
output0 += op_calc(phase << 16, (int)env, 0, P_CH[8].SLOT[1].wavetable) * 2;
|
|
}
|
|
}
|
|
public void OPL_STATUS_SET(int flag)
|
|
{
|
|
status |= (byte)flag;
|
|
if ((status & 0x80) == 0)
|
|
{
|
|
if ((status & statusmask) != 0)
|
|
{
|
|
status |= 0x80;
|
|
if (IRQHandler != null)
|
|
{
|
|
IRQHandler(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public void OPL_STATUS_RESET(int flag)
|
|
{
|
|
status &= (byte)(~flag);
|
|
if ((status & 0x80) != 0)
|
|
{
|
|
if ((status & statusmask) == 0)
|
|
{
|
|
status &= 0x7f;
|
|
if (IRQHandler != null)
|
|
{
|
|
IRQHandler(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public void OPL_STATUSMASK_SET(int flag)
|
|
{
|
|
statusmask = (byte)flag;
|
|
OPL_STATUS_SET(0);
|
|
OPL_STATUS_RESET(0);
|
|
}
|
|
public void advance_lfo()
|
|
{
|
|
byte tmp;
|
|
lfo_am_cnt += lfo_am_inc;
|
|
if (lfo_am_cnt >= ((uint)210 << 24))
|
|
{
|
|
lfo_am_cnt -= ((uint)210 << 24);
|
|
}
|
|
tmp = lfo_am_table[lfo_am_cnt >> 24];
|
|
if (lfo_am_depth != 0)
|
|
{
|
|
LFO_AM = tmp;
|
|
}
|
|
else
|
|
{
|
|
LFO_AM = (uint)(tmp >> 2);
|
|
}
|
|
lfo_pm_cnt += lfo_pm_inc;
|
|
LFO_PM = (int)(((lfo_pm_cnt >> 24) & 7) | lfo_pm_depth_range);
|
|
}
|
|
public void advance()
|
|
{
|
|
int i;
|
|
eg_timer += eg_timer_add;
|
|
while (eg_timer >= eg_timer_overflow)
|
|
{
|
|
eg_timer -= eg_timer_overflow;
|
|
eg_cnt++;
|
|
for (i = 0; i < 9 * 2; i++)
|
|
{
|
|
switch (P_CH[i / 2].SLOT[i & 1].state)
|
|
{
|
|
case 4:
|
|
if ((eg_cnt & ((1 << P_CH[i / 2].SLOT[i & 1].eg_sh_ar) - 1)) == 0)
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].volume += (~P_CH[i / 2].SLOT[i & 1].volume * (eg_inc[P_CH[i / 2].SLOT[i & 1].eg_sel_ar + ((eg_cnt >> P_CH[i / 2].SLOT[i & 1].eg_sh_ar) & 7)])) >> 3;
|
|
if (P_CH[i / 2].SLOT[i & 1].volume <= 0)
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].volume = 0;
|
|
P_CH[i / 2].SLOT[i & 1].state = 3;
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
if ((eg_cnt & ((1 << P_CH[i / 2].SLOT[i & 1].eg_sh_dr) - 1)) == 0)
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].volume += eg_inc[P_CH[i / 2].SLOT[i & 1].eg_sel_dr + ((eg_cnt >> P_CH[i / 2].SLOT[i & 1].eg_sh_dr) & 7)];
|
|
if (P_CH[i / 2].SLOT[i & 1].volume >= P_CH[i / 2].SLOT[i & 1].sl)
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].state = 2;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
if (P_CH[i / 2].SLOT[i & 1].eg_type != 0)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
if ((eg_cnt & ((1 << P_CH[i / 2].SLOT[i & 1].eg_sh_rr) - 1)) == 0)
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].volume += eg_inc[P_CH[i / 2].SLOT[i & 1].eg_sel_rr + ((eg_cnt >> P_CH[i / 2].SLOT[i & 1].eg_sh_rr) & 7)];
|
|
if (P_CH[i / 2].SLOT[i & 1].volume >= 0x1ff)
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].volume = 0x1ff;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
if ((eg_cnt & ((1 << P_CH[i / 2].SLOT[i & 1].eg_sh_rr) - 1)) == 0)
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].volume += eg_inc[P_CH[i / 2].SLOT[i & 1].eg_sel_rr + ((eg_cnt >> P_CH[i / 2].SLOT[i & 1].eg_sh_rr) & 7)];
|
|
if (P_CH[i / 2].SLOT[i & 1].volume >= 0x1ff)
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].volume = 0x1ff;
|
|
P_CH[i / 2].SLOT[i & 1].state = 0;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < 9 * 2; i++)
|
|
{
|
|
if (P_CH[i / 2].SLOT[i & 1].vib != 0)
|
|
{
|
|
byte block;
|
|
uint block_fnum = P_CH[i / 2].block_fnum;
|
|
uint fnum_lfo = (block_fnum & 0x0380) >> 7;
|
|
int lfo_fn_table_index_offset = lfo_pm_table[LFO_PM + 16 * fnum_lfo];
|
|
if (lfo_fn_table_index_offset != 0)
|
|
{
|
|
block_fnum += (uint)lfo_fn_table_index_offset;
|
|
block = (byte)((block_fnum & 0x1c00) >> 10);
|
|
P_CH[i / 2].SLOT[i & 1].Cnt += (fn_tab[block_fnum & 0x03ff] >> (7 - block)) * P_CH[i / 2].SLOT[i & 1].mul;
|
|
}
|
|
else
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].Cnt += P_CH[i / 2].SLOT[i & 1].Incr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
P_CH[i / 2].SLOT[i & 1].Cnt += P_CH[i / 2].SLOT[i & 1].Incr;
|
|
}
|
|
}
|
|
noise_p += noise_f;
|
|
i = (int)(noise_p >> 16);
|
|
noise_p &= 0xffff;
|
|
while (i != 0)
|
|
{
|
|
if ((noise_rng & 1) != 0)
|
|
{
|
|
noise_rng ^= 0x800302;
|
|
}
|
|
noise_rng >>= 1;
|
|
i--;
|
|
}
|
|
}
|
|
private void CSMKeyControll(int chan)
|
|
{
|
|
FM_KEYON(chan, 0, 4);
|
|
FM_KEYON(chan, 1, 4);
|
|
FM_KEYOFF(chan, 0, 4);
|
|
FM_KEYOFF(chan, 1, 4);
|
|
}
|
|
public int OPLTimerOver(int c)
|
|
{
|
|
if (c != 0)
|
|
{
|
|
OPL_STATUS_SET(0x20);
|
|
}
|
|
else
|
|
{
|
|
OPL_STATUS_SET(0x40);
|
|
if ((mode & 0x80) != 0)
|
|
{
|
|
int ch;
|
|
if (UpdateHandler != null)
|
|
{
|
|
UpdateHandler(0);
|
|
}
|
|
for (ch = 0; ch < 9; ch++)
|
|
{
|
|
CSMKeyControll(ch);
|
|
}
|
|
}
|
|
}
|
|
if (timer_handler != null)
|
|
{
|
|
timer_handler(c, Attotime.attotime_mul(TimerBase, T[c]));
|
|
}
|
|
return status >> 7;
|
|
}
|
|
public void OPLSetTimerHandler(OPL_TIMERHANDLER _timer_handler)
|
|
{
|
|
timer_handler = _timer_handler;
|
|
}
|
|
public void OPLSetIRQHandler(OPL_IRQHANDLER _IRQHandler)
|
|
{
|
|
IRQHandler = _IRQHandler;
|
|
}
|
|
public void OPLSetUpdateHandler(OPL_UPDATEHANDLER _UpdateHandler)
|
|
{
|
|
UpdateHandler = _UpdateHandler;
|
|
}
|
|
}
|
|
public static FM_OPL YM3812, YM3526;
|
|
public static int[] slot_array = new int[32]
|
|
{
|
|
0, 2, 4, 1, 3, 5,-1,-1,
|
|
6, 8,10, 7, 9,11,-1,-1,
|
|
12,14,16,13,15,17,-1,-1,
|
|
-1,-1,-1,-1,-1,-1,-1,-1
|
|
};
|
|
public static uint[] ksl_tab = new uint[8 * 16]
|
|
{
|
|
0,0,0,0,
|
|
0,0,0,0,
|
|
0,0,0,0,
|
|
0,0,0,0,
|
|
|
|
0,0,0,0,
|
|
0,0,0,0,
|
|
0,8,12,16,
|
|
20,24,28,32,
|
|
|
|
0,0,0,0,
|
|
0,12,20,28,
|
|
32,40,44,48,
|
|
52,56,60,64,
|
|
|
|
0,0,0,20,
|
|
32,44,52,60,
|
|
64,72,76,80,
|
|
84,88,92,96,
|
|
|
|
0,0,32,52,
|
|
64,76,84,92,
|
|
96,104,108,112,
|
|
116,120,124,128,
|
|
|
|
0,32,64,84,
|
|
96,108,116,124,
|
|
128,136,140,144,
|
|
148,152,156,160,
|
|
|
|
0,64,96,116,
|
|
128,140,148,156,
|
|
160,168,172,176,
|
|
180,184,188,192,
|
|
|
|
0,96,128,148,
|
|
160,172,180,188,
|
|
192,200,204,208,
|
|
212,216,220,224
|
|
};
|
|
public static uint[] sl_tab = new uint[16]
|
|
{
|
|
0*16, 1*16, 2*16,3 *16,4 *16,5 *16,6 *16, 7*16,
|
|
8*16, 9*16,10*16,11*16,12*16,13*16,14*16,31*16
|
|
};
|
|
public static byte[] eg_inc = new byte[15 * 8]
|
|
{
|
|
0,1, 0,1, 0,1, 0,1,
|
|
0,1, 0,1, 1,1, 0,1,
|
|
0,1, 1,1, 0,1, 1,1,
|
|
0,1, 1,1, 1,1, 1,1,
|
|
|
|
1,1, 1,1, 1,1, 1,1,
|
|
1,1, 1,2, 1,1, 1,2,
|
|
1,2, 1,2, 1,2, 1,2,
|
|
1,2, 2,2, 1,2, 2,2,
|
|
|
|
2,2, 2,2, 2,2, 2,2,
|
|
2,2, 2,4, 2,2, 2,4,
|
|
2,4, 2,4, 2,4, 2,4,
|
|
2,4, 4,4, 2,4, 4,4,
|
|
|
|
4,4, 4,4, 4,4, 4,4,
|
|
8,8, 8,8, 8,8, 8,8,
|
|
0,0, 0,0, 0,0, 0,0,
|
|
};
|
|
public static byte[] eg_rate_select = new byte[16 + 64 + 16]
|
|
{
|
|
14*8,14*8,14*8,14*8,14*8,14*8,14*8,14*8,
|
|
14*8,14*8,14*8,14*8,14*8,14*8,14*8,14*8,
|
|
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
0, 1*8, 2*8, 3*8,
|
|
|
|
4*8, 5*8, 6*8, 7*8,
|
|
|
|
8*8, 9*8,10*8,11*8,
|
|
|
|
12*8,12*8,12*8,12*8,
|
|
|
|
12*8,12*8,12*8,12*8,12*8,12*8,12*8,12*8,
|
|
12*8,12*8,12*8,12*8,12*8,12*8,12*8,12*8,
|
|
};
|
|
public static byte[] eg_rate_shift = new byte[16 + 64 + 16]
|
|
{
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,0,0,
|
|
|
|
12,12,12,12,
|
|
11,11,11,11,
|
|
10,10,10,10,
|
|
9, 9, 9, 9,
|
|
8, 8, 8, 8,
|
|
7, 7, 7, 7,
|
|
6, 6, 6, 6,
|
|
5, 5, 5, 5,
|
|
4, 4, 4, 4,
|
|
3, 3, 3, 3,
|
|
2, 2, 2, 2,
|
|
1, 1, 1, 1,
|
|
0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
};
|
|
public static byte[] mul_tab = new byte[16]
|
|
{
|
|
1, 2, 4, 6, 8, 10, 12, 14,
|
|
16, 18,20,20,24,24,30,30
|
|
};
|
|
public static int[] tl_tab;
|
|
public static uint[] sin_tab;
|
|
public static byte[] lfo_am_table = new byte[210]
|
|
{
|
|
0,0,0,0,0,0,0,
|
|
1,1,1,1,
|
|
2,2,2,2,
|
|
3,3,3,3,
|
|
4,4,4,4,
|
|
5,5,5,5,
|
|
6,6,6,6,
|
|
7,7,7,7,
|
|
8,8,8,8,
|
|
9,9,9,9,
|
|
10,10,10,10,
|
|
11,11,11,11,
|
|
12,12,12,12,
|
|
13,13,13,13,
|
|
14,14,14,14,
|
|
15,15,15,15,
|
|
16,16,16,16,
|
|
17,17,17,17,
|
|
18,18,18,18,
|
|
19,19,19,19,
|
|
20,20,20,20,
|
|
21,21,21,21,
|
|
22,22,22,22,
|
|
23,23,23,23,
|
|
24,24,24,24,
|
|
25,25,25,25,
|
|
26,26,26,
|
|
25,25,25,25,
|
|
24,24,24,24,
|
|
23,23,23,23,
|
|
22,22,22,22,
|
|
21,21,21,21,
|
|
20,20,20,20,
|
|
19,19,19,19,
|
|
18,18,18,18,
|
|
17,17,17,17,
|
|
16,16,16,16,
|
|
15,15,15,15,
|
|
14,14,14,14,
|
|
13,13,13,13,
|
|
12,12,12,12,
|
|
11,11,11,11,
|
|
10,10,10,10,
|
|
9,9,9,9,
|
|
8,8,8,8,
|
|
7,7,7,7,
|
|
6,6,6,6,
|
|
5,5,5,5,
|
|
4,4,4,4,
|
|
3,3,3,3,
|
|
2,2,2,2,
|
|
1,1,1,1
|
|
};
|
|
public static sbyte[] lfo_pm_table = new sbyte[8 * 8 * 2]
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
1, 0, 0, 0,-1, 0, 0, 0,
|
|
|
|
1, 0, 0, 0,-1, 0, 0, 0,
|
|
2, 1, 0,-1,-2,-1, 0, 1,
|
|
|
|
1, 0, 0, 0,-1, 0, 0, 0,
|
|
3, 1, 0,-1,-3,-1, 0, 1,
|
|
|
|
2, 1, 0,-1,-2,-1, 0, 1,
|
|
4, 2, 0,-2,-4,-2, 0, 2,
|
|
|
|
2, 1, 0,-1,-2,-1, 0, 1,
|
|
5, 2, 0,-2,-5,-2, 0, 2,
|
|
|
|
3, 1, 0,-1,-3,-1, 0, 1,
|
|
6, 3, 0,-3,-6,-3, 0, 3,
|
|
|
|
3, 1, 0,-1,-3,-1, 0, 1,
|
|
7, 3, 0,-3,-7,-3, 0, 3
|
|
};
|
|
public static int num_lock = 0;
|
|
public static int phase_modulation;
|
|
public static int output0;
|
|
public static uint LFO_AM;
|
|
public static int LFO_PM;
|
|
public delegate byte OPL_PORTHANDLER_R();
|
|
public delegate void OPL_PORTHANDLER_W(byte data);
|
|
public delegate void OPL_TIMERHANDLER(int timer, Atime period);
|
|
public delegate void OPL_IRQHANDLER(int irq);
|
|
public delegate void OPL_UPDATEHANDLER(int min_interval_us);
|
|
private static int limit(int val, int max, int min)
|
|
{
|
|
if (val > max)
|
|
{
|
|
return max;
|
|
}
|
|
else if (val < min)
|
|
{
|
|
return min;
|
|
}
|
|
else
|
|
{
|
|
return val;
|
|
}
|
|
}
|
|
private static int op_calc(uint phase, int env, int pm, int wave_tab)
|
|
{
|
|
uint p;
|
|
p = (uint)((env << 4) + sin_tab[wave_tab + ((((int)((phase & ~0xffff) + (pm << 16))) >> 16) & 0x3ff)]);
|
|
if (p >= 0x1800)
|
|
{
|
|
return 0;
|
|
}
|
|
return tl_tab[p];
|
|
}
|
|
private static int init_tables()
|
|
{
|
|
int i, x;
|
|
int n;
|
|
double o, m;
|
|
for (x = 0; x < 0x100; x++)
|
|
{
|
|
m = (1 << 16) / Math.Pow(2, (x + 1) * (1.0 / 32) / 8.0);
|
|
m = Math.Floor(m);
|
|
n = (int)m;
|
|
n >>= 4;
|
|
if ((n & 1) != 0)
|
|
{
|
|
n = (n >> 1) + 1;
|
|
}
|
|
else
|
|
{
|
|
n = n >> 1;
|
|
}
|
|
n <<= 1;
|
|
tl_tab[x * 2 + 0] = n;
|
|
tl_tab[x * 2 + 1] = -tl_tab[x * 2 + 0];
|
|
for (i = 1; i < 12; i++)
|
|
{
|
|
tl_tab[x * 2 + 0 + i * 2 * 0x100] = tl_tab[x * 2 + 0] >> i;
|
|
tl_tab[x * 2 + 1 + i * 2 * 0x100] = -tl_tab[x * 2 + 0 + i * 2 * 0x100];
|
|
}
|
|
}
|
|
for (i = 0; i < 0x400; i++)
|
|
{
|
|
m = Math.Sin(((i * 2) + 1) * Math.PI / 0x400);
|
|
if (m > 0.0)
|
|
{
|
|
o = 8 * Math.Log(1.0 / m) / Math.Log(2);
|
|
}
|
|
else
|
|
{
|
|
o = 8 * Math.Log(-1.0 / m) / Math.Log(2);
|
|
}
|
|
o = o * 32;
|
|
n = (int)(2.0 * o);
|
|
if ((n & 1) != 0)
|
|
{
|
|
n = (n >> 1) + 1;
|
|
}
|
|
else
|
|
{
|
|
n = n >> 1;
|
|
}
|
|
sin_tab[i] = (uint)(n * 2 + (m >= 0.0 ? 0 : 1));
|
|
}
|
|
for (i = 0; i < 0x400; i++)
|
|
{
|
|
if ((i & 0x200) != 0)
|
|
{
|
|
sin_tab[0x400 + i] = 0x1800;
|
|
}
|
|
else
|
|
{
|
|
sin_tab[0x400 + i] = sin_tab[i];
|
|
}
|
|
sin_tab[2 * 0x400 + i] = sin_tab[i & (0x3ff >> 1)];
|
|
if ((i & 0x100) != 0)
|
|
{
|
|
sin_tab[3 * 0x400 + i] = 0x1800;
|
|
}
|
|
else
|
|
{
|
|
sin_tab[3 * 0x400 + i] = sin_tab[i & (0x3ff >> 2)];
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
private static int OPL_LockTable()
|
|
{
|
|
num_lock++;
|
|
if (num_lock > 1)
|
|
{
|
|
return 0;
|
|
}
|
|
if (init_tables() == 0)
|
|
{
|
|
num_lock--;
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
private static void OPL_UnLockTable()
|
|
{
|
|
if (num_lock != 0)
|
|
{
|
|
num_lock--;
|
|
}
|
|
}
|
|
private static FM_OPL OPLCreate(int type, int clock, int rate)
|
|
{
|
|
int i;
|
|
FM_OPL OPL = new FM_OPL();
|
|
OPL_LockTable();
|
|
OPL = new FM_OPL();
|
|
OPL.P_CH = new OPL_CH[9];
|
|
OPL.fn_tab = new uint[1024];
|
|
OPL.T = new uint[2];
|
|
OPL.st = new byte[2];
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
OPL.P_CH[i].SLOT = new OPL_SLOT[2];
|
|
OPL.P_CH[i].SLOT[0].op1_out = new int[2];
|
|
OPL.P_CH[i].SLOT[1].op1_out = new int[2];
|
|
}
|
|
OPL.type = (byte)type;
|
|
OPL.clock = clock;
|
|
OPL.rate = rate;
|
|
OPL.OPL_initalize();
|
|
return OPL;
|
|
}
|
|
private static void OPLDestroy()
|
|
{
|
|
OPL_UnLockTable();
|
|
}
|
|
public static void ym3812_init(int sndindex, int clock, int rate)
|
|
{
|
|
YM3812 = OPLCreate(1, clock, rate);
|
|
ym3812_reset_chip();
|
|
}
|
|
private static void ym3812_shutdown()
|
|
{
|
|
OPLDestroy();
|
|
}
|
|
public static void ym3812_reset_chip()
|
|
{
|
|
YM3812.OPLResetChip();
|
|
num_lock = 0;
|
|
}
|
|
public static int ym3812_write(int a, int v)
|
|
{
|
|
return YM3812.OPLWrite(a, v);
|
|
}
|
|
public static byte ym3812_read(int a)
|
|
{
|
|
return (byte)(YM3812.OPLRead(a) | 0x06);
|
|
}
|
|
public static int ym3812_timer_over(int c)
|
|
{
|
|
return YM3812.OPLTimerOver(c);
|
|
}
|
|
public static void ym3812_set_timer_handler(OPL_TIMERHANDLER timer_handler)
|
|
{
|
|
YM3812.OPLSetTimerHandler(timer_handler);
|
|
}
|
|
public static void ym3812_set_irq_handler(OPL_IRQHANDLER IRQHandler)
|
|
{
|
|
YM3812.OPLSetIRQHandler(IRQHandler);
|
|
}
|
|
public static void ym3812_set_update_handler(OPL_UPDATEHANDLER UpdateHandler)
|
|
{
|
|
YM3812.OPLSetUpdateHandler(UpdateHandler);
|
|
}
|
|
public static void ym3812_update_one(int offset, int length)
|
|
{
|
|
byte rhythm = (byte)(YM3812.rhythm & 0x20);
|
|
int i;
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
int lt;
|
|
output0 = 0;
|
|
YM3812.advance_lfo();
|
|
YM3812.OPL_CALC_CH(0);
|
|
YM3812.OPL_CALC_CH(1);
|
|
YM3812.OPL_CALC_CH(2);
|
|
YM3812.OPL_CALC_CH(3);
|
|
YM3812.OPL_CALC_CH(4);
|
|
YM3812.OPL_CALC_CH(5);
|
|
if (rhythm == 0)
|
|
{
|
|
YM3812.OPL_CALC_CH(6);
|
|
YM3812.OPL_CALC_CH(7);
|
|
YM3812.OPL_CALC_CH(8);
|
|
}
|
|
else
|
|
{
|
|
YM3812.OPL_CALC_RH((YM3812.noise_rng >> 0) & 1);
|
|
}
|
|
lt = output0;
|
|
lt = limit(lt, 32767, -32768);
|
|
Sound.ym3812stream.streamoutput_Ptrs[0][offset + i] = lt;
|
|
YM3812.advance();
|
|
}
|
|
}
|
|
public static FM_OPL ym3526_init(int sndindex, int clock, int rate)
|
|
{
|
|
YM3526 = OPLCreate(0, clock, rate);
|
|
ym3526_reset_chip();
|
|
return YM3526;
|
|
}
|
|
private static void ym3526_shutdown()
|
|
{
|
|
OPLDestroy();
|
|
}
|
|
public static void ym3526_reset_chip()
|
|
{
|
|
YM3526.OPLResetChip();
|
|
num_lock = 0;
|
|
}
|
|
public static int ym3526_write(int a, int v)
|
|
{
|
|
return YM3526.OPLWrite(a, v);
|
|
}
|
|
public static byte ym3526_read(int a)
|
|
{
|
|
return (byte)(YM3526.OPLRead(a) | 0x06);
|
|
}
|
|
public static void ym3526_set_timer_handler(OPL_TIMERHANDLER timer_handler)
|
|
{
|
|
YM3526.OPLSetTimerHandler(timer_handler);
|
|
}
|
|
public static void ym3526_set_irq_handler(OPL_IRQHANDLER IRQHandler)
|
|
{
|
|
YM3526.OPLSetIRQHandler(IRQHandler);
|
|
}
|
|
public static void ym3526_set_update_handler(OPL_UPDATEHANDLER UpdateHandler)
|
|
{
|
|
YM3526.OPLSetUpdateHandler(UpdateHandler);
|
|
}
|
|
public static int ym3526_timer_over(int c)
|
|
{
|
|
return YM3526.OPLTimerOver(c);
|
|
}
|
|
public static void ym3526_update_one(int offset, int length)
|
|
{
|
|
byte rhythm = (byte)(YM3526.rhythm & 0x20);
|
|
int i;
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
int lt;
|
|
output0 = 0;
|
|
YM3526.advance_lfo();
|
|
YM3526.OPL_CALC_CH(0);
|
|
YM3526.OPL_CALC_CH(1);
|
|
YM3526.OPL_CALC_CH(2);
|
|
YM3526.OPL_CALC_CH(3);
|
|
YM3526.OPL_CALC_CH(4);
|
|
YM3526.OPL_CALC_CH(5);
|
|
if (rhythm == 0)
|
|
{
|
|
YM3526.OPL_CALC_CH(6);
|
|
YM3526.OPL_CALC_CH(7);
|
|
YM3526.OPL_CALC_CH(8);
|
|
}
|
|
else
|
|
{
|
|
YM3526.OPL_CALC_RH((YM3526.noise_rng >> 0) & 1);
|
|
}
|
|
lt = output0;
|
|
lt = limit(lt, 32767, -32768);
|
|
Sound.ym3526stream.streamoutput_Ptrs[0][offset + i] = lt;
|
|
YM3526.advance();
|
|
}
|
|
}
|
|
}
|
|
}
|