1383 lines
54 KiB
C#
1383 lines
54 KiB
C#
using System;
|
|
|
|
namespace MAME.Core
|
|
{
|
|
public class FM
|
|
{
|
|
public class FM_OPN
|
|
{
|
|
public int type;
|
|
public FM_ST ST;
|
|
public FM_3SLOT SL3;
|
|
public FM_CH[] CH;
|
|
public uint[] pan;
|
|
public uint eg_cnt;
|
|
public uint eg_timer;
|
|
public uint eg_timer_add;
|
|
public uint eg_timer_overflow;
|
|
public uint[] fn_table;
|
|
public int lfo_cnt;
|
|
public int lfo_inc;
|
|
public int[] lfo_freq;
|
|
public int[,] idt_tab;
|
|
private int[] iconnect1 = new int[8], iconnect2 = new int[8], iconnect3 = new int[8], iconnect4 = new int[6], imem = new int[13];
|
|
public FM_OPN()
|
|
{
|
|
int i;
|
|
CH = new FM_CH[6];
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
CH[i].SLOT = new FM_SLOT[4];
|
|
}
|
|
idt_tab = new int[6, 4];
|
|
ST = new FM_ST();
|
|
ST.dt_tab2 = new int[8, 32];
|
|
SL3 = new FM_3SLOT();
|
|
SL3.fc = new uint[3];
|
|
SL3.kcode = new byte[3];
|
|
SL3.block_fnum = new uint[3];
|
|
pan = new uint[12];
|
|
fn_table = new uint[4096];
|
|
lfo_freq = new int[8];
|
|
ST.timer_handler = null;
|
|
ST.IRQ_Handler = null;
|
|
ST.SSG.set_clock = null;
|
|
ST.SSG.write = null;
|
|
ST.SSG.read = null;
|
|
ST.SSG.reset = null;
|
|
}
|
|
private void FM_STATUS_SET(byte flag)
|
|
{
|
|
ST.status |= flag;
|
|
if ((ST.irq == 0) && ((ST.status & ST.irqmask) != 0))
|
|
{
|
|
ST.irq = 1;
|
|
if (ST.IRQ_Handler != null)
|
|
{
|
|
ST.IRQ_Handler(1);
|
|
}
|
|
}
|
|
}
|
|
public void FM_STATUS_RESET(byte flag)
|
|
{
|
|
ST.status &= (byte)~flag;
|
|
if ((ST.irq != 0) && ((ST.status & ST.irqmask) == 0))
|
|
{
|
|
ST.irq = 0;
|
|
if (ST.IRQ_Handler != null)
|
|
{
|
|
ST.IRQ_Handler(0);
|
|
}
|
|
}
|
|
}
|
|
public void FM_IRQMASK_SET(byte flag)
|
|
{
|
|
ST.irqmask = flag;
|
|
FM_STATUS_SET(0);
|
|
FM_STATUS_RESET(0);
|
|
}
|
|
private void set_timers(byte v)
|
|
{
|
|
ST.mode = v;
|
|
if ((v & 0x20) != 0)
|
|
{
|
|
FM_STATUS_RESET(0x02);
|
|
}
|
|
if ((v & 0x10) != 0)
|
|
{
|
|
FM_STATUS_RESET(0x01);
|
|
}
|
|
if ((v & 0x02) != 0)
|
|
{
|
|
if (ST.TBC == 0)
|
|
{
|
|
ST.TBC = (256 - ST.TB) << 4;
|
|
ST.timer_handler(1, ST.TBC * ST.timer_prescaler, ST.clock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ST.TBC != 0)
|
|
{
|
|
ST.TBC = 0;
|
|
ST.timer_handler(1, 0, ST.clock);
|
|
}
|
|
}
|
|
if ((v & 0x01) != 0)
|
|
{
|
|
if (ST.TAC == 0)
|
|
{
|
|
ST.TAC = (1024 - ST.TA);
|
|
ST.timer_handler(0, ST.TAC * ST.timer_prescaler, ST.clock);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ST.TAC != 0)
|
|
{
|
|
ST.TAC = 0;
|
|
ST.timer_handler(0, 0, ST.clock);
|
|
}
|
|
}
|
|
}
|
|
public void TimerAOver()
|
|
{
|
|
if ((ST.mode & 0x04) != 0)
|
|
{
|
|
FM_STATUS_SET(0x01);
|
|
}
|
|
ST.TAC = (1024 - ST.TA);
|
|
ST.timer_handler(0, ST.TAC * ST.timer_prescaler, ST.clock);
|
|
}
|
|
public void TimerBOver()
|
|
{
|
|
if ((ST.mode & 0x08) != 0)
|
|
{
|
|
FM_STATUS_SET(0x02);
|
|
}
|
|
ST.TBC = (256 - ST.TB) << 4;
|
|
ST.timer_handler(1, ST.TBC * ST.timer_prescaler, ST.clock);
|
|
}
|
|
public void FM_BUSY_CLEAR()
|
|
{
|
|
ST.busy_expiry_time = Attotime.ATTOTIME_ZERO;
|
|
}
|
|
public byte FM_STATUS_FLAG()
|
|
{
|
|
if (Attotime.attotime_compare(ST.busy_expiry_time, Attotime.ATTOTIME_ZERO) != 0)
|
|
{
|
|
if (Attotime.attotime_compare(ST.busy_expiry_time, EmuTimer.get_current_time()) > 0)
|
|
{
|
|
return (byte)(ST.status | 0x80);
|
|
}
|
|
FM_BUSY_CLEAR();
|
|
}
|
|
return ST.status;
|
|
}
|
|
public void FM_BUSY_SET(int busyclock)
|
|
{
|
|
Atime expiry_period = Attotime.attotime_mul(Attotime.ATTOTIME_IN_HZ(ST.clock), (uint)(busyclock * ST.timer_prescaler));
|
|
ST.busy_expiry_time = Attotime.attotime_add(EmuTimer.get_current_time(), expiry_period);
|
|
}
|
|
private void FM_KEYON(int type, int c, int s)
|
|
{
|
|
if (CH[c].SLOT[s].key == 0)
|
|
{
|
|
CH[c].SLOT[s].key = 1;
|
|
CH[c].SLOT[s].phase = 0;
|
|
if ((type == TYPE_YM2612) || (type == TYPE_YM2608))
|
|
{
|
|
if ((CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr) < 32 + 62)
|
|
{
|
|
CH[c].SLOT[s].state = 4;
|
|
CH[c].SLOT[s].volume = 1023;
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].state = 3;
|
|
CH[c].SLOT[s].volume = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].state = 4;
|
|
}
|
|
}
|
|
}
|
|
private void FM_KEYOFF(int c, int s)
|
|
{
|
|
if (CH[c].SLOT[s].key != 0)
|
|
{
|
|
CH[c].SLOT[s].key = 0;
|
|
if (CH[c].SLOT[s].state > 1)
|
|
{
|
|
CH[c].SLOT[s].state = 1;
|
|
}
|
|
}
|
|
}
|
|
private void set_value1(int c)
|
|
{
|
|
if (iconnect1[c] == 12)
|
|
{
|
|
out_fm[11] = out_fm[9] = out_fm[10] = CH[c].op1_out0;
|
|
}
|
|
else
|
|
{
|
|
out_fm[iconnect1[c]] = CH[c].op1_out0;
|
|
}
|
|
}
|
|
private void set_mem(int c)
|
|
{
|
|
if (imem[c] == 8 || imem[c] == 10 || imem[c] == 11)
|
|
{
|
|
out_fm[imem[c]] = CH[c].mem_value;
|
|
}
|
|
}
|
|
private void setup_connection(int ch)
|
|
{
|
|
switch (CH[ch].ALGO)
|
|
{
|
|
case 0:
|
|
iconnect1[ch] = 9;
|
|
iconnect2[ch] = 11;
|
|
iconnect3[ch] = 10;
|
|
imem[ch] = 8;
|
|
break;
|
|
case 1:
|
|
iconnect1[ch] = 11;
|
|
iconnect2[ch] = 11;
|
|
iconnect3[ch] = 10;
|
|
imem[ch] = 8;
|
|
break;
|
|
case 2:
|
|
iconnect1[ch] = 10;
|
|
iconnect2[ch] = 11;
|
|
iconnect3[ch] = 10;
|
|
imem[ch] = 8;
|
|
break;
|
|
case 3:
|
|
iconnect1[ch] = 9;
|
|
iconnect2[ch] = 11;
|
|
iconnect3[ch] = 10;
|
|
imem[ch] = 10;
|
|
break;
|
|
case 4:
|
|
iconnect1[ch] = 9;
|
|
iconnect2[ch] = ch;
|
|
iconnect3[ch] = 10;
|
|
imem[ch] = 11;
|
|
break;
|
|
case 5:
|
|
iconnect1[ch] = 12;
|
|
iconnect2[ch] = ch;
|
|
iconnect3[ch] = ch;
|
|
imem[ch] = 8;
|
|
break;
|
|
case 6:
|
|
iconnect1[ch] = 9;
|
|
iconnect2[ch] = ch;
|
|
iconnect3[ch] = ch;
|
|
imem[ch] = 11;
|
|
break;
|
|
case 7:
|
|
iconnect1[ch] = ch;
|
|
iconnect2[ch] = ch;
|
|
iconnect3[ch] = ch;
|
|
imem[ch] = 11;
|
|
break;
|
|
}
|
|
iconnect4[ch] = ch;
|
|
}
|
|
private void set_det_mul(int c, int s, byte v)
|
|
{
|
|
CH[c].SLOT[s].mul = ((v & 0x0f) != 0) ? (v & 0x0f) * 2 : 1;
|
|
idt_tab[c, s] = (v >> 4) & 7;
|
|
CH[c].SLOT[0].Incr = -1;
|
|
}
|
|
private void set_tl(int c, int s, byte v)
|
|
{
|
|
CH[c].SLOT[s].tl = (v & 0x7f) << (10 - 7);
|
|
}
|
|
private void set_ar_ksr(int c, int s, byte v)
|
|
{
|
|
byte old_KSR = CH[c].SLOT[s].KSR;
|
|
CH[c].SLOT[s].ar = ((v & 0x1f) != 0) ? 32 + ((v & 0x1f) << 1) : 0;
|
|
CH[c].SLOT[s].KSR = (byte)(3 - (v >> 6));
|
|
if (CH[c].SLOT[s].KSR != old_KSR)
|
|
{
|
|
CH[c].SLOT[0].Incr = -1;
|
|
}
|
|
if ((CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr) < 32 + 62)
|
|
{
|
|
CH[c].SLOT[s].eg_sh_ar = eg_rate_shift[CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr];
|
|
if ((type == TYPE_YM2612) || (type == TYPE_YM2608))
|
|
{
|
|
CH[c].SLOT[s].eg_sel_ar = eg_rate_select2612[CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr];
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].eg_sel_ar = eg_rate_select[CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].eg_sh_ar = 0;
|
|
CH[c].SLOT[s].eg_sel_ar = 17 * 8;
|
|
}
|
|
}
|
|
private void set_dr(int c, int s, byte v)
|
|
{
|
|
CH[c].SLOT[s].d1r = ((v & 0x1f) != 0) ? 32 + ((v & 0x1f) << 1) : 0;
|
|
CH[c].SLOT[s].eg_sh_d1r = eg_rate_shift[CH[c].SLOT[s].d1r + CH[c].SLOT[s].ksr];
|
|
if ((type == TYPE_YM2612) || (type == TYPE_YM2608))
|
|
{
|
|
CH[c].SLOT[s].eg_sel_d1r = eg_rate_select2612[CH[c].SLOT[s].d1r + CH[c].SLOT[s].ksr];
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].eg_sel_d1r = eg_rate_select[CH[c].SLOT[s].d1r + CH[c].SLOT[s].ksr];
|
|
}
|
|
}
|
|
private void set_sr(int c, int s, byte v)
|
|
{
|
|
CH[c].SLOT[s].d2r = ((v & 0x1f) != 0) ? 32 + ((v & 0x1f) << 1) : 0;
|
|
CH[c].SLOT[s].eg_sh_d2r = eg_rate_shift[CH[c].SLOT[s].d2r + CH[c].SLOT[s].ksr];
|
|
if ((type == TYPE_YM2612) || (type == TYPE_YM2608))
|
|
{
|
|
CH[c].SLOT[s].eg_sel_d2r = eg_rate_select2612[CH[c].SLOT[s].d2r + CH[c].SLOT[s].ksr];
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].eg_sel_d2r = eg_rate_select[CH[c].SLOT[s].d2r + CH[c].SLOT[s].ksr];
|
|
}
|
|
}
|
|
private void set_sl_rr(int c, int s, byte v)
|
|
{
|
|
CH[c].SLOT[s].sl = sl_table[v >> 4];
|
|
CH[c].SLOT[s].rr = 34 + ((v & 0x0f) << 2);
|
|
CH[c].SLOT[s].eg_sh_rr = eg_rate_shift[CH[c].SLOT[s].rr + CH[c].SLOT[s].ksr];
|
|
if ((type == TYPE_YM2612) || (type == TYPE_YM2608))
|
|
{
|
|
CH[c].SLOT[s].eg_sel_rr = eg_rate_select2612[CH[c].SLOT[s].rr + CH[c].SLOT[s].ksr];
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].eg_sel_rr = eg_rate_select[CH[c].SLOT[s].rr + CH[c].SLOT[s].ksr];
|
|
}
|
|
}
|
|
public void advance_lfo()
|
|
{
|
|
byte pos;
|
|
byte prev_pos;
|
|
if (lfo_inc != 0)
|
|
{
|
|
prev_pos = (byte)(lfo_cnt >> 24 & 127);
|
|
lfo_cnt += lfo_inc;
|
|
pos = (byte)((lfo_cnt >> 24) & 127);
|
|
if (pos < 64)
|
|
{
|
|
LFO_AM = (pos & 63) * 2;
|
|
}
|
|
else
|
|
{
|
|
LFO_AM = 126 - ((pos & 63) * 2);
|
|
}
|
|
prev_pos >>= 2;
|
|
pos >>= 2;
|
|
LFO_PM = pos;
|
|
}
|
|
else
|
|
{
|
|
LFO_AM = 0;
|
|
LFO_PM = 0;
|
|
}
|
|
}
|
|
public void advance_eg_channel(int c)
|
|
{
|
|
uint out1;
|
|
byte swap_flag = 0;
|
|
int i;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
switch (CH[c].SLOT[i].state)
|
|
{
|
|
case 4:
|
|
if ((eg_cnt & ((1 << CH[c].SLOT[i].eg_sh_ar) - 1)) == 0)
|
|
{
|
|
CH[c].SLOT[i].volume += (~CH[c].SLOT[i].volume * (eg_inc[CH[c].SLOT[i].eg_sel_ar + ((eg_cnt >> CH[c].SLOT[i].eg_sh_ar) & 7)])) >> 4;
|
|
if (CH[c].SLOT[i].volume <= 0)
|
|
{
|
|
CH[c].SLOT[i].volume = 0;
|
|
CH[c].SLOT[i].state = 3;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
if ((CH[c].SLOT[i].ssg & 0x08) != 0)
|
|
{
|
|
if ((eg_cnt & ((1 << CH[c].SLOT[i].eg_sh_d1r) - 1)) == 0)
|
|
{
|
|
CH[c].SLOT[i].volume += 4 * eg_inc[CH[c].SLOT[i].eg_sel_d1r + ((eg_cnt >> CH[c].SLOT[i].eg_sh_d1r) & 7)];
|
|
if (CH[c].SLOT[i].volume >= CH[c].SLOT[i].sl)
|
|
{
|
|
CH[c].SLOT[i].state = 2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((eg_cnt & ((1 << CH[c].SLOT[i].eg_sh_d1r) - 1)) == 0)
|
|
{
|
|
CH[c].SLOT[i].volume += eg_inc[CH[c].SLOT[i].eg_sel_d1r + ((eg_cnt >> CH[c].SLOT[i].eg_sh_d1r) & 7)];
|
|
if (CH[c].SLOT[i].volume >= CH[c].SLOT[i].sl)
|
|
{
|
|
CH[c].SLOT[i].state = 2;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
if ((CH[c].SLOT[i].ssg & 0x08) != 0)
|
|
{
|
|
if ((eg_cnt & ((1 << CH[c].SLOT[i].eg_sh_d2r) - 1)) == 0)
|
|
{
|
|
CH[c].SLOT[i].volume += 4 * eg_inc[CH[c].SLOT[i].eg_sel_d2r + ((eg_cnt >> CH[c].SLOT[i].eg_sh_d2r) & 7)];
|
|
if (CH[c].SLOT[i].volume >= 512)
|
|
{
|
|
CH[c].SLOT[i].volume = 0x3ff;
|
|
if ((CH[c].SLOT[i].ssg & 0x01) != 0)
|
|
{
|
|
if ((CH[c].SLOT[i].ssgn & 1) != 0)
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
swap_flag = (byte)((CH[c].SLOT[i].ssg & 0x02) | 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[i].phase = 0;
|
|
CH[c].SLOT[i].volume = 511;
|
|
CH[c].SLOT[i].state = 4;
|
|
swap_flag = (byte)(CH[c].SLOT[i].ssg & 0x02); /* bit 1 = alternate */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((eg_cnt & ((1 << CH[c].SLOT[i].eg_sh_d2r) - 1)) == 0)
|
|
{
|
|
CH[c].SLOT[i].volume += eg_inc[CH[c].SLOT[i].eg_sel_d2r + ((eg_cnt >> CH[c].SLOT[i].eg_sh_d2r) & 7)];
|
|
if (CH[c].SLOT[i].volume >= 0x3ff)
|
|
{
|
|
CH[c].SLOT[i].volume = 0x3ff;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
if ((eg_cnt & ((1 << CH[c].SLOT[i].eg_sh_rr) - 1)) == 0)
|
|
{
|
|
CH[c].SLOT[i].volume += eg_inc[CH[c].SLOT[i].eg_sel_rr + ((eg_cnt >> CH[c].SLOT[i].eg_sh_rr) & 7)];
|
|
if (CH[c].SLOT[i].volume >= 0x3ff)
|
|
{
|
|
CH[c].SLOT[i].volume = 0x3ff;
|
|
CH[c].SLOT[i].state = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
out1 = (uint)(CH[c].SLOT[i].tl + ((uint)CH[c].SLOT[i].volume));
|
|
if (((CH[c].SLOT[i].ssg & 0x08) != 0) && ((CH[c].SLOT[i].ssgn & 2) != 0) && (CH[c].SLOT[i].state != 0))
|
|
{
|
|
out1 ^= 511;
|
|
}
|
|
CH[c].SLOT[i].vol_out = out1;
|
|
CH[c].SLOT[i].ssgn ^= swap_flag;
|
|
}
|
|
}
|
|
private uint volume_calc(int c, int s)
|
|
{
|
|
int AM = LFO_AM >> CH[c].ams;
|
|
return (uint)(CH[c].SLOT[s].vol_out + (AM & CH[c].SLOT[s].AMmask));
|
|
}
|
|
private void update_phase_lfo_slot(int s, int pms, uint block_fnum)
|
|
{
|
|
uint fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
|
|
int lfo_fn_table_index_offset = lfo_pm_table[fnum_lfo + pms + LFO_PM];
|
|
if (lfo_fn_table_index_offset != 0)
|
|
{
|
|
byte blk;
|
|
uint fn;
|
|
int kc, fc;
|
|
block_fnum = (uint)(block_fnum * 2 + lfo_fn_table_index_offset);
|
|
blk = (byte)((block_fnum & 0x7000) >> 12);
|
|
fn = block_fnum & 0xfff;
|
|
kc = (blk << 2) | opn_fktable[fn >> 8];
|
|
fc = (int)((fn_table[fn] >> (7 - blk)) + ST.dt_tab2[idt_tab[2, 0], kc]);
|
|
if (fc < 0)
|
|
{
|
|
fc += fn_max;
|
|
}
|
|
CH[2].SLOT[s].phase += (uint)((fc * CH[2].SLOT[s].mul) >> 1);
|
|
}
|
|
else
|
|
{
|
|
CH[2].SLOT[s].phase += (uint)CH[2].SLOT[s].Incr;
|
|
}
|
|
}
|
|
private void update_phase_lfo_channel(FM_CH[] CH, int c)
|
|
{
|
|
uint block_fnum = CH[c].block_fnum;
|
|
uint fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
|
|
int lfo_fn_table_index_offset = lfo_pm_table[fnum_lfo + CH[c].pms + LFO_PM];
|
|
if (lfo_fn_table_index_offset != 0)
|
|
{
|
|
byte blk;
|
|
uint fn;
|
|
int kc, fc, finc;
|
|
block_fnum = (uint)(block_fnum * 2 + lfo_fn_table_index_offset);
|
|
blk = (byte)((block_fnum & 0x7000) >> 12);
|
|
fn = block_fnum & 0xfff;
|
|
kc = (blk << 2) | opn_fktable[fn >> 8];
|
|
fc = (int)(fn_table[fn] >> (7 - blk));
|
|
finc = fc + ST.dt_tab2[idt_tab[c, 0], kc];
|
|
if (finc < 0)
|
|
{
|
|
finc += fn_max;
|
|
}
|
|
CH[c].SLOT[0].phase += (uint)((finc * CH[c].SLOT[0].mul) >> 1);
|
|
finc = fc + ST.dt_tab2[idt_tab[c, 2], kc];
|
|
if (finc < 0)
|
|
{
|
|
finc += fn_max;
|
|
}
|
|
CH[c].SLOT[2].phase += (uint)((finc * CH[c].SLOT[2].mul) >> 1);
|
|
finc = fc + ST.dt_tab2[idt_tab[c, 1], kc];
|
|
if (finc < 0)
|
|
{
|
|
finc += fn_max;
|
|
}
|
|
CH[c].SLOT[1].phase += (uint)((finc * CH[c].SLOT[1].mul) >> 1);
|
|
finc = fc + ST.dt_tab2[idt_tab[c, 3], kc];
|
|
if (finc < 0)
|
|
{
|
|
finc += fn_max;
|
|
}
|
|
CH[c].SLOT[3].phase += (uint)((finc * CH[c].SLOT[3].mul) >> 1);
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[0].phase += (uint)CH[c].SLOT[0].Incr;
|
|
CH[c].SLOT[2].phase += (uint)CH[c].SLOT[2].Incr;
|
|
CH[c].SLOT[1].phase += (uint)CH[c].SLOT[1].Incr;
|
|
CH[c].SLOT[3].phase += (uint)CH[c].SLOT[3].Incr;
|
|
}
|
|
}
|
|
public void chan_calc(int c, int chnum)
|
|
{
|
|
uint eg_out;
|
|
out_fm[8] = out_fm[9] = out_fm[10] = out_fm[11] = 0;//m2 = c1 = c2 = mem = 0;
|
|
set_mem(c);
|
|
eg_out = volume_calc(c, 0);
|
|
int out1 = CH[c].op1_out0 + CH[c].op1_out1;
|
|
CH[c].op1_out0 = CH[c].op1_out1;
|
|
set_value1(c);
|
|
CH[c].op1_out1 = 0;
|
|
if (eg_out < 832)
|
|
{
|
|
if (CH[c].FB == 0)
|
|
{
|
|
out1 = 0;
|
|
}
|
|
CH[c].op1_out1 = op_calc1(CH[c].SLOT[0].phase, eg_out, (out1 << CH[c].FB));
|
|
}
|
|
eg_out = volume_calc(c, 1);
|
|
if (eg_out < 832)
|
|
{
|
|
out_fm[iconnect3[c]] += op_calc(CH[c].SLOT[1].phase, eg_out, out_fm[8]);
|
|
}
|
|
eg_out = volume_calc(c, 2);
|
|
if (eg_out < 832)
|
|
{
|
|
out_fm[iconnect2[c]] += op_calc(CH[c].SLOT[2].phase, eg_out, out_fm[9]);
|
|
}
|
|
eg_out = volume_calc(c, 3);
|
|
if (eg_out < 832)
|
|
{
|
|
out_fm[iconnect4[c]] += op_calc(CH[c].SLOT[3].phase, eg_out, out_fm[10]);
|
|
}
|
|
CH[c].mem_value = out_fm[11];//mem;
|
|
if (CH[c].pms != 0)
|
|
{
|
|
if (((ST.mode & 0xC0) != 0) && (chnum == 2))
|
|
{
|
|
update_phase_lfo_slot(0, CH[c].pms, SL3.block_fnum[1]);
|
|
update_phase_lfo_slot(2, CH[c].pms, SL3.block_fnum[2]);
|
|
update_phase_lfo_slot(1, CH[c].pms, SL3.block_fnum[0]);
|
|
update_phase_lfo_slot(3, CH[c].pms, CH[c].block_fnum);
|
|
}
|
|
else
|
|
{
|
|
update_phase_lfo_channel(CH, c);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[0].phase += (uint)CH[c].SLOT[0].Incr;
|
|
CH[c].SLOT[2].phase += (uint)CH[c].SLOT[2].Incr;
|
|
CH[c].SLOT[1].phase += (uint)CH[c].SLOT[1].Incr;
|
|
CH[c].SLOT[3].phase += (uint)CH[c].SLOT[3].Incr;
|
|
}
|
|
}
|
|
public void refresh_fc_eg_slot(int type, int c, int s, int fc, int kc)
|
|
{
|
|
int ksr = kc >> CH[c].SLOT[s].KSR;
|
|
fc += ST.dt_tab2[idt_tab[c, s], kc];
|
|
if (fc < 0)
|
|
{
|
|
fc += fn_max;
|
|
}
|
|
CH[c].SLOT[s].Incr = (fc * CH[c].SLOT[s].mul) >> 1;
|
|
if (CH[c].SLOT[s].ksr != ksr)
|
|
{
|
|
CH[c].SLOT[s].ksr = (byte)ksr;
|
|
if ((CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr) < 32 + 62)
|
|
{
|
|
CH[c].SLOT[s].eg_sh_ar = eg_rate_shift[CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr];
|
|
if ((type == TYPE_YM2612) || (type == TYPE_YM2608))
|
|
{
|
|
CH[c].SLOT[s].eg_sel_ar = eg_rate_select2612[CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr];
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].eg_sel_ar = eg_rate_select[CH[c].SLOT[s].ar + CH[c].SLOT[s].ksr];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].eg_sh_ar = 0;
|
|
CH[c].SLOT[s].eg_sel_ar = 17 * 8;
|
|
}
|
|
CH[c].SLOT[s].eg_sh_d1r = eg_rate_shift[CH[c].SLOT[s].d1r + CH[c].SLOT[s].ksr];
|
|
CH[c].SLOT[s].eg_sh_d2r = eg_rate_shift[CH[c].SLOT[s].d2r + CH[c].SLOT[s].ksr];
|
|
CH[c].SLOT[s].eg_sh_rr = eg_rate_shift[CH[c].SLOT[s].rr + CH[c].SLOT[s].ksr];
|
|
if ((type == TYPE_YM2612) || (type == TYPE_YM2608))
|
|
{
|
|
CH[c].SLOT[s].eg_sel_d1r = eg_rate_select2612[CH[c].SLOT[s].d1r + CH[c].SLOT[s].ksr];
|
|
CH[c].SLOT[s].eg_sel_d2r = eg_rate_select2612[CH[c].SLOT[s].d2r + CH[c].SLOT[s].ksr];
|
|
CH[c].SLOT[s].eg_sel_rr = eg_rate_select2612[CH[c].SLOT[s].rr + CH[c].SLOT[s].ksr];
|
|
}
|
|
else
|
|
{
|
|
CH[c].SLOT[s].eg_sel_d1r = eg_rate_select[CH[c].SLOT[s].d1r + CH[c].SLOT[s].ksr];
|
|
CH[c].SLOT[s].eg_sel_d2r = eg_rate_select[CH[c].SLOT[s].d2r + CH[c].SLOT[s].ksr];
|
|
CH[c].SLOT[s].eg_sel_rr = eg_rate_select[CH[c].SLOT[s].rr + CH[c].SLOT[s].ksr];
|
|
}
|
|
}
|
|
}
|
|
public void refresh_fc_eg_chan(int type, int c)
|
|
{
|
|
if (CH[c].SLOT[0].Incr == -1)
|
|
{
|
|
int fc = (int)CH[c].fc;
|
|
int kc = CH[c].kcode;
|
|
refresh_fc_eg_slot(type, c, 0, fc, kc);
|
|
refresh_fc_eg_slot(type, c, 2, fc, kc);
|
|
refresh_fc_eg_slot(type, c, 1, fc, kc);
|
|
refresh_fc_eg_slot(type, c, 3, fc, kc);
|
|
}
|
|
}
|
|
private void init_timetables()
|
|
{
|
|
int i, d;
|
|
double rate;
|
|
for (d = 0; d <= 3; d++)
|
|
{
|
|
for (i = 0; i <= 31; i++)
|
|
{
|
|
rate = ((double)dt_tab[d * 32 + i]) * 1024 * ST.freqbase * (1 << 16) / ((double)(1 << 20));
|
|
ST.dt_tab2[d, i] = (int)rate;
|
|
ST.dt_tab2[d + 4, i] = -ST.dt_tab2[d, i];
|
|
}
|
|
}
|
|
}
|
|
public void reset_channels(int num)
|
|
{
|
|
int c, s;
|
|
ST.mode = 0; /* normal mode */
|
|
ST.TA = 0;
|
|
ST.TAC = 0;
|
|
ST.TB = 0;
|
|
ST.TBC = 0;
|
|
for (c = 0; c < num; c++)
|
|
{
|
|
CH[c].fc = 0;
|
|
for (s = 0; s < 4; s++)
|
|
{
|
|
CH[c].SLOT[s].ssg = 0;
|
|
CH[c].SLOT[s].ssgn = 0;
|
|
CH[c].SLOT[s].state = 0;
|
|
CH[c].SLOT[s].volume = 0x3ff;
|
|
CH[c].SLOT[s].vol_out = 0x3ff;
|
|
}
|
|
}
|
|
}
|
|
public void CSMKeyControll()
|
|
{
|
|
FM_KEYON(type, 2, 0);
|
|
FM_KEYON(type, 2, 2);
|
|
FM_KEYON(type, 2, 1);
|
|
FM_KEYON(type, 2, 3);
|
|
}
|
|
public void OPNSetPres(int pres, int timer_prescaler, int SSGpres)
|
|
{
|
|
int i;
|
|
ST.freqbase = (ST.rate != 0) ? ((double)ST.clock / ST.rate) / pres : 0;
|
|
eg_timer_add = (uint)((1 << 16) * ST.freqbase);
|
|
eg_timer_overflow = (3) * (1 << 16);
|
|
ST.timer_prescaler = timer_prescaler;
|
|
if (SSGpres != 0)
|
|
{
|
|
ST.SSG.set_clock(ST.clock * 2 / SSGpres);
|
|
}
|
|
init_timetables();
|
|
for (i = 0; i < 4096; i++)
|
|
{
|
|
fn_table[i] = (uint)((double)i * 32 * ST.freqbase * (1 << (16 - 10)));
|
|
}
|
|
fn_max = (int)((uint)(((double)fn_table[0x7ff * 2] / ST.freqbase)) >> 2);//2096127
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
lfo_freq[i] = (int)((1.0 / lfo_samples_per_step[i]) * (1 << 24) * ST.freqbase);
|
|
}
|
|
}
|
|
public void OPNWriteMode(int r, byte v)
|
|
{
|
|
byte c;
|
|
switch (r)
|
|
{
|
|
case 0x21:
|
|
break;
|
|
case 0x22:
|
|
if ((type & TYPE_LFOPAN) != 0)
|
|
{
|
|
if ((v & 0x08) != 0)
|
|
{
|
|
lfo_inc = lfo_freq[v & 7];
|
|
}
|
|
else
|
|
{
|
|
lfo_inc = 0;
|
|
}
|
|
}
|
|
break;
|
|
case 0x24:
|
|
ST.TA = (ST.TA & 0x03) | (((int)v) << 2);
|
|
break;
|
|
case 0x25:
|
|
ST.TA = (ST.TA & 0x3fc) | (v & 3);
|
|
break;
|
|
case 0x26:
|
|
ST.TB = v;
|
|
break;
|
|
case 0x27:
|
|
set_timers(v);
|
|
break;
|
|
case 0x28:
|
|
c = (byte)(v & 0x03);
|
|
if (c == 3)
|
|
break;
|
|
if ((v & 0x04) != 0 && (type & TYPE_6CH) != 0)
|
|
c += 3;
|
|
if ((v & 0x10) != 0)
|
|
FM_KEYON(type, c, 0);
|
|
else
|
|
FM_KEYOFF(c, 0);
|
|
if ((v & 0x20) != 0)
|
|
FM_KEYON(type, c, 2);
|
|
else
|
|
FM_KEYOFF(c, 2);
|
|
if ((v & 0x40) != 0)
|
|
FM_KEYON(type, c, 1);
|
|
else
|
|
FM_KEYOFF(c, 1);
|
|
if ((v & 0x80) != 0)
|
|
FM_KEYON(type, c, 3);
|
|
else
|
|
FM_KEYOFF(c, 3);
|
|
break;
|
|
}
|
|
}
|
|
public void OPNWriteReg(int r, byte v)
|
|
{
|
|
byte c = (byte)(r & 3);
|
|
int s = (r >> 2) & 3;
|
|
if (c == 3)
|
|
return;
|
|
if (r >= 0x100)
|
|
c += 3;
|
|
switch (r & 0xf0)
|
|
{
|
|
case 0x30:
|
|
set_det_mul(c, s, v);
|
|
break;
|
|
case 0x40:
|
|
set_tl(c, s, v);
|
|
break;
|
|
case 0x50:
|
|
set_ar_ksr(c, s, v);
|
|
break;
|
|
case 0x60:
|
|
set_dr(c, s, v);
|
|
if ((type & TYPE_LFOPAN) != 0)
|
|
{
|
|
CH[c].SLOT[s].AMmask = ((v & 0x80) != 0) ? 0xffffffff : 0;
|
|
}
|
|
break;
|
|
case 0x70:
|
|
set_sr(c, s, v);
|
|
break;
|
|
case 0x80:
|
|
set_sl_rr(c, s, v);
|
|
break;
|
|
case 0x90:
|
|
CH[c].SLOT[s].ssg = (byte)(v & 0x0f);
|
|
CH[c].SLOT[s].ssgn = (byte)((v & 0x04) >> 1);
|
|
break;
|
|
case 0xa0:
|
|
switch ((r >> 2) & 3)
|
|
{
|
|
case 0:
|
|
{
|
|
uint fn = (((uint)((ST.fn_h) & 7)) << 8) + v;
|
|
byte blk = (byte)(ST.fn_h >> 3);
|
|
CH[c].kcode = (byte)((blk << 2) | opn_fktable[fn >> 7]);
|
|
CH[c].fc = fn_table[fn * 2] >> (7 - blk);
|
|
CH[c].block_fnum = (uint)(blk << 11) | fn;
|
|
CH[c].SLOT[0].Incr = -1;
|
|
}
|
|
break;
|
|
case 1:
|
|
ST.fn_h = (byte)(v & 0x3f);
|
|
break;
|
|
case 2:
|
|
if (r < 0x100)
|
|
{
|
|
uint fn = (((uint)(SL3.fn_h & 7)) << 8) + v;
|
|
byte blk = (byte)(SL3.fn_h >> 3);
|
|
SL3.kcode[c] = (byte)((blk << 2) | opn_fktable[fn >> 7]);
|
|
SL3.fc[c] = fn_table[fn * 2] >> (7 - blk);
|
|
SL3.block_fnum[c] = (uint)(blk << 11) | fn;
|
|
CH[c + 2].SLOT[0].Incr = -1;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (r < 0x100)
|
|
SL3.fn_h = (byte)(v & 0x3f);
|
|
break;
|
|
}
|
|
break;
|
|
case 0xb0:
|
|
switch ((r >> 2) & 3)
|
|
{
|
|
case 0:
|
|
{
|
|
int feedback = (v >> 3) & 7;
|
|
CH[c].ALGO = (byte)(v & 7);
|
|
CH[c].FB = (feedback != 0) ? (byte)(feedback + 6) : (byte)0;
|
|
setup_connection(c);
|
|
}
|
|
break;
|
|
case 1:
|
|
if ((type & TYPE_LFOPAN) != 0)
|
|
{
|
|
CH[c].pms = (v & 7) * 32;
|
|
CH[c].ams = lfo_ams_depth_shift[(v >> 4) & 0x03];
|
|
pan[c * 2] = ((v & 0x80) != 0) ? 0xffffffff : 0;
|
|
pan[c * 2 + 1] = ((v & 0x40) != 0) ? 0xffffffff : 0;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
public void OPNPrescaler_w(int addr, int pre_divider)
|
|
{
|
|
int[] opn_pres = new int[4] { 2 * 12, 2 * 12, 6 * 12, 3 * 12 };
|
|
int[] ssg_pres = new int[4] { 1, 1, 4, 2 };
|
|
int sel;
|
|
switch (addr)
|
|
{
|
|
case 0:
|
|
ST.prescaler_sel = 2;
|
|
break;
|
|
case 1:
|
|
break;
|
|
case 0x2d:
|
|
ST.prescaler_sel |= 0x02;
|
|
break;
|
|
case 0x2e:
|
|
ST.prescaler_sel |= 0x01;
|
|
break;
|
|
case 0x2f:
|
|
ST.prescaler_sel = 0;
|
|
break;
|
|
}
|
|
sel = ST.prescaler_sel & 3;
|
|
OPNSetPres(opn_pres[sel] * pre_divider, opn_pres[sel] * pre_divider, ssg_pres[sel] * pre_divider);
|
|
}
|
|
}
|
|
public struct FM_SLOT
|
|
{
|
|
public byte KSR;
|
|
public int ar;
|
|
public int d1r;
|
|
public int d2r;
|
|
public int rr;
|
|
public byte ksr;
|
|
public int mul;
|
|
public uint phase;
|
|
public int Incr;
|
|
public byte state;
|
|
public int tl;
|
|
public int volume;
|
|
public int sl;
|
|
public uint vol_out;
|
|
public byte eg_sh_ar;
|
|
public byte eg_sel_ar;
|
|
public byte eg_sh_d1r;
|
|
public byte eg_sel_d1r;
|
|
public byte eg_sh_d2r;
|
|
public byte eg_sel_d2r;
|
|
public byte eg_sh_rr;
|
|
public byte eg_sel_rr;
|
|
public byte ssg;
|
|
public byte ssgn;
|
|
public uint key;
|
|
public uint AMmask;
|
|
}
|
|
public struct FM_CH
|
|
{
|
|
public FM_SLOT[] SLOT;
|
|
public byte ALGO;
|
|
public byte FB;
|
|
public int op1_out0, op1_out1;
|
|
public int mem_value;
|
|
public int pms;
|
|
public byte ams;
|
|
public uint fc;
|
|
public byte kcode;
|
|
public uint block_fnum;
|
|
}
|
|
public struct FM_ST
|
|
{
|
|
public int clock;
|
|
public int rate;
|
|
public double freqbase;
|
|
public int timer_prescaler;
|
|
public Atime busy_expiry_time;
|
|
public byte address;
|
|
public byte irq;
|
|
public byte irqmask;
|
|
public byte status;
|
|
public byte mode;
|
|
public byte prescaler_sel;
|
|
public byte fn_h;
|
|
public int TA;
|
|
public int TAC;
|
|
public byte TB;
|
|
public int TBC;
|
|
public int[,] dt_tab2;
|
|
public FM_TIMERHANDLER timer_handler;
|
|
public FM_IRQHANDLER IRQ_Handler;
|
|
public ssg_callbacks SSG;
|
|
}
|
|
public struct FM_3SLOT
|
|
{
|
|
public uint[] fc;
|
|
public byte fn_h;
|
|
public byte[] kcode;
|
|
public uint[] block_fnum;
|
|
}
|
|
public struct ADPCM_CH
|
|
{
|
|
public byte flag;
|
|
public byte flagMask;
|
|
public byte now_data;
|
|
public uint now_addr;
|
|
public uint now_step;
|
|
public uint step;
|
|
public uint start;
|
|
public uint end;
|
|
public byte IL;
|
|
public int adpcm_acc;
|
|
public int adpcm_step;
|
|
public int adpcm_out;
|
|
public sbyte vol_mul;
|
|
public byte vol_shift;
|
|
}
|
|
public struct ssg_callbacks
|
|
{
|
|
public set_clock_handler set_clock;
|
|
public write_handler write;
|
|
public read_handler read;
|
|
public reset_handler reset;
|
|
}
|
|
|
|
public static int TYPE_SSG = 0x01, TYPE_LFOPAN = 0x02, TYPE_6CH = 0x04, TYPE_DAC = 0x08, TYPE_ADPCM = 0x10, TYPE_2610 = 0x20;
|
|
public static int TYPE_YM2203 = (TYPE_SSG), TYPE_YM2608 = (TYPE_SSG | TYPE_LFOPAN | TYPE_6CH | TYPE_ADPCM), TYPE_YM2610 = (TYPE_SSG | TYPE_LFOPAN | TYPE_6CH | TYPE_ADPCM | TYPE_2610), TYPE_YM2612 = (TYPE_DAC | TYPE_LFOPAN | TYPE_6CH);
|
|
public static int[] ipan = new int[6];
|
|
private static int[] tl_tab = new int[6656];
|
|
private static uint[] sin_tab = new uint[0x400];
|
|
private static int[] sl_table = new int[16]{
|
|
( 0*32),( 1*32),( 2*32),(3*32 ),(4*32 ),(5*32 ),(6*32 ),( 7*32),
|
|
( 8*32),( 9*32),(10*32),(11*32),(12*32),(13*32),(14*32),(31*32)
|
|
};
|
|
private static byte[] eg_inc = new byte[19 * 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,
|
|
4,4, 4,8, 4,4, 4,8,
|
|
4,8, 4,8, 4,8, 4,8,
|
|
4,8, 8,8, 4,8, 8,8,
|
|
|
|
8,8, 8,8, 8,8, 8,8,
|
|
16,16,16,16,16,16,16,16,
|
|
0,0, 0,0, 0,0, 0,0,
|
|
};
|
|
public static byte[] eg_rate_select = new byte[32 + 64 + 32]{
|
|
(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),
|
|
(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),
|
|
(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),
|
|
(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),
|
|
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
|
|
( 4*8),( 5*8),( 6*8),( 7*8),
|
|
|
|
( 8*8),( 9*8),(10*8),(11*8),
|
|
|
|
(12*8),(13*8),(14*8),(15*8),
|
|
|
|
(16*8),(16*8),(16*8),(16*8),
|
|
|
|
(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),
|
|
(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),
|
|
(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),
|
|
(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8)
|
|
};
|
|
public static byte[] eg_rate_select2612 = new byte[32 + 64 + 32]{
|
|
(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),
|
|
(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),
|
|
(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),
|
|
(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),(18*8),
|
|
|
|
( 18*8),( 18*8),( 0*8),( 0*8),
|
|
( 0*8),( 0*8),( 2*8),( 2*8),
|
|
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
( 0*8),( 1*8),( 2*8),( 3*8),
|
|
|
|
( 4*8),( 5*8),( 6*8),( 7*8),
|
|
|
|
( 8*8),( 9*8),(10*8),(11*8),
|
|
|
|
(12*8),(13*8),(14*8),(15*8),
|
|
|
|
(16),(16),(16),(16),
|
|
|
|
(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),
|
|
(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),
|
|
(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),
|
|
(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8),(16*8)
|
|
};
|
|
public static byte[] eg_rate_shift = new byte[32 + 64 + 32]{
|
|
(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),
|
|
|
|
(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),( 0),( 0),( 0),( 0),
|
|
( 0),( 0),( 0),( 0),( 0),( 0),( 0),( 0),
|
|
( 0),( 0),( 0),( 0),( 0),( 0),( 0),( 0)
|
|
};
|
|
public static byte[] dt_tab = new byte[4 * 32]{
|
|
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,
|
|
|
|
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
|
|
2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
|
|
|
|
1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
|
|
5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
|
|
|
|
2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
|
|
8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
|
|
};
|
|
private static byte[] opn_fktable = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 };
|
|
private static uint[] lfo_samples_per_step = new uint[8] { 108, 77, 71, 67, 62, 44, 8, 5 };
|
|
private static byte[] lfo_ams_depth_shift = new byte[4] { 8, 3, 1, 0 };
|
|
private static byte[,] lfo_pm_output = new byte[56, 8]{
|
|
{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},
|
|
{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, 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},
|
|
{0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 1, 1, 1, 1},
|
|
{0, 0, 1, 1, 2, 2, 2, 3},
|
|
|
|
{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},
|
|
{0, 0, 0, 0, 0, 0, 0, 1},
|
|
{0, 0, 0, 0, 1, 1, 1, 1},
|
|
{0, 0, 1, 1, 2, 2, 2, 3},
|
|
{0, 0, 2, 3, 4, 4, 5, 6},
|
|
|
|
{0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 0, 0, 1, 1},
|
|
{0, 0, 0, 0, 1, 1, 1, 1},
|
|
{0, 0, 0, 1, 1, 1, 1, 2},
|
|
{0, 0, 1, 1, 2, 2, 2, 3},
|
|
{0, 0, 2, 3, 4, 4, 5, 6},
|
|
{0, 0, 4, 6, 8, 8, 0xa, 0xc},
|
|
|
|
{0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 1, 1, 1, 1},
|
|
{0, 0, 0, 1, 1, 1, 2, 2},
|
|
{0, 0, 1, 1, 2, 2, 3, 3},
|
|
{0, 0, 1, 2, 2, 2, 3, 4},
|
|
{0, 0, 2, 3, 4, 4, 5, 6},
|
|
{0, 0, 4, 6, 8, 8, 0xa, 0xc},
|
|
{0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
|
|
|
|
{0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 2, 2, 2, 2},
|
|
{0, 0, 0, 2, 2, 2, 4, 4},
|
|
{0, 0, 2, 2, 4, 4, 6, 6},
|
|
{0, 0, 2, 4, 4, 4, 6, 8},
|
|
{0, 0, 4, 6, 8, 8, 0xa, 0xc},
|
|
{0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
|
|
{0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
|
|
|
|
{0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0, 0, 0, 0, 4, 4, 4, 4},
|
|
{0, 0, 0, 4, 4, 4, 8, 8},
|
|
{0, 0, 4, 4, 8, 8, 0xc, 0xc},
|
|
{0, 0, 4, 8, 8, 8, 0xc,0x10},
|
|
{0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
|
|
{0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
|
|
{0, 0,0x20,0x30,0x40,0x40,0x50,0x60},
|
|
};
|
|
private static int[] steps = new int[49]
|
|
{
|
|
16, 17, 19, 21, 23, 25, 28,
|
|
31, 34, 37, 41, 45, 50, 55,
|
|
60, 66, 73, 80, 88, 97, 107,
|
|
118, 130, 143, 157, 173, 190, 209,
|
|
230, 253, 279, 307, 337, 371, 408,
|
|
449, 494, 544, 598, 658, 724, 796,
|
|
876, 963, 1060, 1166, 1282, 1411, 1552
|
|
};
|
|
public static int[] step_inc = new int[8] { -1 * 16, -1 * 16, -1 * 16, -1 * 16, 2 * 16, 5 * 16, 7 * 16, 9 * 16 };
|
|
public static int[] jedi_table = new int[49 * 16];
|
|
public delegate void FM_TIMERHANDLER(int c, int cnt, int clock);
|
|
public delegate void FM_IRQHANDLER(int irq);
|
|
public delegate void set_clock_handler(int clock);
|
|
public delegate void write_handler(int address, byte data);
|
|
public delegate byte read_handler();
|
|
public delegate void reset_handler();
|
|
private static int[] lfo_pm_table = new int[128 * 8 * 32];
|
|
private static int[] iconnect1 = new int[8], iconnect2 = new int[8], iconnect3 = new int[8], iconnect4 = new int[6], imem = new int[13];
|
|
public static int[] out_fm = new int[13];
|
|
public static int[] out_adpcm = new int[4];
|
|
public static int[] out_delta = new int[4];
|
|
public static byte[] ymsndrom;
|
|
public static int LFO_AM;
|
|
public static int LFO_PM;
|
|
private static int fn_max;
|
|
public static void FM_init()
|
|
{
|
|
init_tables();
|
|
}
|
|
public 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, uint env, int pm)
|
|
{
|
|
uint p;
|
|
p = (uint)((env << 3) + sin_tab[(((int)((phase & 0xffff0000) + (pm << 15))) >> 16) & 0x3ff]);
|
|
if (p >= 6656)
|
|
{
|
|
return 0;
|
|
}
|
|
return tl_tab[p];
|
|
}
|
|
private static int op_calc1(uint phase, uint env, int pm)
|
|
{
|
|
uint p;
|
|
p = (uint)((env << 3) + sin_tab[(((int)((phase & 0xffff0000) + pm)) >> 16) & 0x3ff]);
|
|
if (p >= 6656)
|
|
{
|
|
return 0;
|
|
}
|
|
return tl_tab[p];
|
|
}
|
|
public static int init_tables()
|
|
{
|
|
int i, x, 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 <<= 2;
|
|
tl_tab[x * 2] = n;
|
|
tl_tab[x * 2 + 1] = -tl_tab[x * 2];
|
|
for (i = 1; i < 13; i++)
|
|
{
|
|
tl_tab[x * 2 + i * 2 * 0x100] = tl_tab[x * 2] >> i;
|
|
tl_tab[x * 2 + 1 + i * 2 * 0x100] = -tl_tab[x * 2 + 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 / (1.0 / 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 < 8; i++)
|
|
{
|
|
byte fnum;
|
|
for (fnum = 0; fnum < 128; fnum++)
|
|
{
|
|
byte value;
|
|
byte step;
|
|
int offset_depth = i;
|
|
int offset_fnum_bit;
|
|
int bit_tmp;
|
|
for (step = 0; step < 8; step++)
|
|
{
|
|
value = 0;
|
|
for (bit_tmp = 0; bit_tmp < 7; bit_tmp++)
|
|
{
|
|
if ((fnum & (1 << bit_tmp)) != 0)
|
|
{
|
|
offset_fnum_bit = bit_tmp * 8;
|
|
value += lfo_pm_output[offset_fnum_bit + offset_depth, step];
|
|
}
|
|
}
|
|
lfo_pm_table[(fnum * 32 * 8) + (i * 32) + step + 0] = value;
|
|
lfo_pm_table[(fnum * 32 * 8) + (i * 32) + (step ^ 7) + 8] = value;
|
|
lfo_pm_table[(fnum * 32 * 8) + (i * 32) + step + 16] = -value;
|
|
lfo_pm_table[(fnum * 32 * 8) + (i * 32) + (step ^ 7) + 24] = -value;
|
|
}
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
public static void Init_ADPCMATable()
|
|
{
|
|
int step, nib;
|
|
for (step = 0; step < 49; step++)
|
|
{
|
|
for (nib = 0; nib < 16; nib++)
|
|
{
|
|
int value = (2 * (nib & 0x07) + 1) * steps[step] / 8;
|
|
jedi_table[step * 16 + nib] = ((nib & 0x08) != 0) ? -value : value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|