using System.IO; namespace MAME.Core { public unsafe class ICS2115 { public static byte V_ON = 1, V_DONE = 2; public static voice_struct[] voice2; public static timer_struct[] timer; public static byte[] icsrom; public static short[] ulaw = new short[256]; public static byte osc_select; public static byte reg_select; public static byte irq_enabled, irq_pending; public static bool irq_on; public struct timer_struct { public byte scale, preset; public EmuTimer.emu_timer timer; public long period; } public static void ics2115_w(int offset, byte data) { switch (offset) { case 1: reg_select = data; break; case 2: reg_write(data, false); break; case 3: reg_write(data, true); break; default: break; } } public static void timer_cb_0() { irq_pending |= 1 << 0; recalc_irq(); } public static void timer_cb_1() { irq_pending |= 1 << 1; recalc_irq(); } public struct voice_struct { public ushort fc, addrh, addrl, strth, endh, volacc; public byte strtl, endl, saddr, pan, conf, ctl; public byte vstart, vend, vctl; public byte state; } public static void recalc_irq() { int i; bool irq = false; if ((irq_enabled & irq_pending) != 0) irq = true; for (i = 0; irq == false && i < 32; i++) if ((voice2[i].state & V_DONE) != 0) irq = true; if (irq != irq_on) { irq_on = irq; PGM.sound_irq(irq ? (int)LineState.ASSERT_LINE : (int)LineState.CLEAR_LINE); } } public static void ics2115_update(int offset, int length) { int osc, i; bool irq_invalid = false; for (i = 0; i < length; i++) { Sound.ics2115stream.streamoutput_Ptrs[0][offset + i] = 0; Sound.ics2115stream.streamoutput_Ptrs[1][offset + i] = 0; } for (osc = 0; osc < 32; osc++) { if ((voice2[osc].state & V_ON) != 0) { uint adr = (uint)((voice2[osc].addrh << 16) | voice2[osc].addrl); uint end = (uint)((voice2[osc].endh << 16) | (voice2[osc].endl << 8)); uint loop = (uint)((voice2[osc].strth << 16) | (voice2[osc].strtl << 8)); uint badr = (uint)((voice2[osc].saddr << 20) & 0xffffff); uint delta = (uint)(voice2[osc].fc << 2); byte conf = voice2[osc].conf; int vol = voice2[osc].volacc; vol = (((vol & 0xff0) | 0x1000) << (vol >> 12)) >> 12; for (i = 0; i < length; i++) { int v; if ((badr | adr >> 12) >= icsrom.Length) { v = 0; } else { v = icsrom[badr | (adr >> 12)]; } if ((conf & 1) != 0) v = ulaw[v]; else v = ((sbyte)v) << 6; v = (v * vol) >> (16 + 5); Sound.ics2115stream.streamoutput_Ptrs[0][offset + i] += v; Sound.ics2115stream.streamoutput_Ptrs[1][offset + i] += v; adr += delta; if (adr >= end) { adr -= (end - loop); voice2[osc].state &= (byte)(~V_ON); voice2[osc].state |= V_DONE; irq_invalid = true; break; } } voice2[osc].addrh = (ushort)(adr >> 16); voice2[osc].addrl = (ushort)(adr); } } if (irq_invalid) { recalc_irq(); } } public static void keyon() { voice2[osc_select].state |= V_ON; } public static void recalc_timer(int i) { long period = (long)(1000000000 * timer[i].scale * timer[i].preset / 33868800); if (period != 0) period = (long)(1000000000 / 62.8206); if (timer[i].period != period) { timer[i].period = period; if (period != 0) EmuTimer.timer_adjust_periodic(timer[i].timer, Attotime.ATTOTIME_IN_NSEC(period), Attotime.ATTOTIME_IN_NSEC(period)); else EmuTimer.timer_adjust_periodic(timer[i].timer, Attotime.ATTOTIME_NEVER, Attotime.ATTOTIME_NEVER); } } static void reg_write(byte data, bool msb) { switch (reg_select) { case 0x00: // [osc] Oscillator Configuration if (msb) { voice2[osc_select].conf = data; } break; case 0x01: // [osc] Wavesample frequency // freq = fc*33075/1024 in 32 voices mode, fc*44100/1024 in 24 voices mode if (msb) voice2[osc_select].fc = (ushort)((voice2[osc_select].fc & 0xff) | (data << 8)); else voice2[osc_select].fc = (ushort)((voice2[osc_select].fc & 0xff00) | data); break; case 0x02: // [osc] Wavesample loop start address 19-4 if (msb) voice2[osc_select].strth = (ushort)((voice2[osc_select].strth & 0xff) | (data << 8)); else voice2[osc_select].strth = (ushort)((voice2[osc_select].strth & 0xff00) | data); break; case 0x03: // [osc] Wavesample loop start address 3-0.3-0 if (msb) { voice2[osc_select].strtl = data; } break; case 0x04: // [osc] Wavesample loop end address 19-4 if (msb) voice2[osc_select].endh = (ushort)((voice2[osc_select].endh & 0xff) | (data << 8)); else voice2[osc_select].endh = (ushort)((voice2[osc_select].endh & 0xff00) | data); break; case 0x05: // [osc] Wavesample loop end address 3-0.3-0 if (msb) { voice2[osc_select].endl = data; } break; case 0x07: // [osc] Volume Start if (msb) { voice2[osc_select].vstart = data; } break; case 0x08: // [osc] Volume End if (msb) { voice2[osc_select].vend = data; } break; case 0x09: // [osc] Volume accumulator if (msb) voice2[osc_select].volacc = (ushort)((voice2[osc_select].volacc & 0xff) | (data << 8)); else voice2[osc_select].volacc = (ushort)((voice2[osc_select].volacc & 0xff00) | data); break; case 0x0a: // [osc] Wavesample address 19-4 if (msb) voice2[osc_select].addrh = (ushort)((voice2[osc_select].addrh & 0xff) | (data << 8)); else voice2[osc_select].addrh = (ushort)((voice2[osc_select].addrh & 0xff00) | data); break; case 0x0b: // [osc] Wavesample address 3-0.8-0 if (msb) voice2[osc_select].addrl = (ushort)((voice2[osc_select].addrl & 0xff) | (data << 8)); else voice2[osc_select].addrl = (ushort)((voice2[osc_select].addrl & 0xff00) | data); break; case 0x0c: // [osc] Pan if (msb) { voice2[osc_select].pan = data; } break; case 0x0d: // [osc] Volume Enveloppe Control if (msb) { voice2[osc_select].vctl = data; } break; case 0x10: // [osc] Oscillator Control if (msb) { voice2[osc_select].ctl = data; if (data == 0) keyon(); } break; case 0x11: // [osc] Wavesample static address 27-20 if (msb) { voice2[osc_select].saddr = data; } break; case 0x40: // Timer 1 Preset if (!msb) { timer[0].preset = data; recalc_timer(0); } break; case 0x41: // Timer 2 Preset if (!msb) { timer[1].preset = data; recalc_timer(1); } break; case 0x42: // Timer 1 Prescaler if (!msb) { timer[0].scale = data; recalc_timer(0); } break; case 0x43: // Timer 2 Prescaler if (!msb) { timer[1].scale = data; recalc_timer(1); } break; case 0x4a: // IRQ Enable if (!msb) { irq_enabled = data; recalc_irq(); } break; case 0x4f: // Oscillator Address being Programmed if (!msb) { osc_select = (byte)(data & 31); } break; default: break; } } public static ushort reg_read() { switch (reg_select) { case 0x0d: return 0x100; case 0x0f: { int osc; byte res = 0xff; for (osc = 0; osc < 32; osc++) if ((voice2[osc].state & V_DONE) != 0) { voice2[osc].state &= (byte)(~V_DONE); recalc_irq(); res = (byte)(0x40 | osc); break; } return (ushort)(res << 8); } case 0x40: irq_pending &= unchecked((byte)(~(1 << 0))); recalc_irq(); return timer[0].preset; case 0x41: irq_pending &= unchecked((byte)(~(1 << 1))); recalc_irq(); return timer[1].preset; case 0x43: return (ushort)(irq_pending & 3); case 0x4a: return irq_pending; case 0x4b: return 0x80; case 0x4c: return 0x01; default: return 0; } } public static void ics2115_start() { int i; voice2 = new voice_struct[32]; timer = new timer_struct[2]; timer[0].timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.ICS2115_timer_cb_0, false); timer[1].timer = EmuTimer.timer_alloc_common(EmuTimer.TIME_ACT.ICS2115_timer_cb_1, false); ulaw = new short[256]; for (i = 0; i < 256; i++) { byte c = (byte)(~i); int v; v = ((c & 15) << 1) + 33; v <<= ((c & 0x70) >> 4); if ((c & 0x80) != 0) v = 33 - v; else v = v - 33; ulaw[i] = (short)v; } } public static byte ics2115_r(int offset) { byte ret = 0; switch (offset) { case 0: if (irq_on) { int i; ret |= 0x80; if ((irq_enabled & irq_pending & 3) != 0) ret |= 1; for (i = 0; i < 32; i++) if ((voice2[i].state & V_DONE) != 0) { ret |= 2; break; } } break; case 1: ret = reg_select; break; case 2: ret = (byte)(reg_read()); break; case 3: ret = (byte)(reg_read() >> 8); break; default: break; } return ret; } public static void ics2115_reset() { int i; irq_enabled = 0; irq_pending = 0; for (i = 0; i < 32; i++) { voice2[i].fc = 0; voice2[i].addrh = 0; voice2[i].addrl = 0; voice2[i].strth = 0; voice2[i].endh = 0; voice2[i].volacc = 0; voice2[i].strtl = 0; voice2[i].endl = 0; voice2[i].saddr = 0; voice2[i].pan = 0; voice2[i].conf = 0; voice2[i].ctl = 0; voice2[i].vstart = 0; voice2[i].vend = 0; voice2[i].vctl = 0; voice2[i].state = 0; } for (i = 0; i < 2; i++) { EmuTimer.timer_adjust_periodic(timer[i].timer, Attotime.ATTOTIME_NEVER, Attotime.ATTOTIME_NEVER); timer[i].period = 0; } recalc_irq(); } public static void SaveStateBinary(BinaryWriter writer) { int i; for (i = 0; i < 32; i++) { writer.Write(voice2[i].fc); writer.Write(voice2[i].addrh); writer.Write(voice2[i].addrl); writer.Write(voice2[i].strth); writer.Write(voice2[i].endh); writer.Write(voice2[i].volacc); writer.Write(voice2[i].strtl); writer.Write(voice2[i].endl); writer.Write(voice2[i].saddr); writer.Write(voice2[i].pan); writer.Write(voice2[i].conf); writer.Write(voice2[i].ctl); writer.Write(voice2[i].vstart); writer.Write(voice2[i].vend); writer.Write(voice2[i].vctl); writer.Write(voice2[i].state); } for (i = 0; i < 2; i++) { writer.Write(timer[i].scale); writer.Write(timer[i].preset); writer.Write(timer[i].period); } writer.Write(osc_select); writer.Write(reg_select); writer.Write(irq_enabled); writer.Write(irq_pending); writer.Write(irq_on); } public static void LoadStateBinary(BinaryReader reader) { int i; for (i = 0; i < 32; i++) { voice2[i].fc = reader.ReadUInt16(); voice2[i].addrh = reader.ReadUInt16(); voice2[i].addrl = reader.ReadUInt16(); voice2[i].strth = reader.ReadUInt16(); voice2[i].endh = reader.ReadUInt16(); voice2[i].volacc = reader.ReadUInt16(); voice2[i].strtl = reader.ReadByte(); voice2[i].endl = reader.ReadByte(); voice2[i].saddr = reader.ReadByte(); voice2[i].pan = reader.ReadByte(); voice2[i].conf = reader.ReadByte(); voice2[i].ctl = reader.ReadByte(); voice2[i].vstart = reader.ReadByte(); voice2[i].vend = reader.ReadByte(); voice2[i].vctl = reader.ReadByte(); voice2[i].state = reader.ReadByte(); } for (i = 0; i < 2; i++) { timer[i].scale = reader.ReadByte(); timer[i].preset = reader.ReadByte(); timer[i].period = reader.ReadInt64(); } osc_select = reader.ReadByte(); reg_select = reader.ReadByte(); irq_enabled = reader.ReadByte(); irq_pending = reader.ReadByte(); irq_on = reader.ReadBoolean(); } } }