BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs

1072 lines
32 KiB
C#
Raw Normal View History

2017-11-19 14:30:18 +00:00
using System;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Common.NumberExtensions;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
2017-08-29 13:18:28 +00:00
{
// Audio Emulation
2017-11-19 14:30:18 +00:00
public class Audio : ISoundProvider
{
public GBHawk Core { get; set; }
public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0};
public static int[] DIVISOR = new int[] { 8, 16, 32, 48, 64, 80, 96, 112 };
public const int NR10 = 0;
public const int NR11 = 1;
public const int NR12 = 2;
public const int NR13 = 3;
public const int NR14 = 4;
public const int NR21 = 5;
public const int NR22 = 6;
public const int NR23 = 7;
public const int NR24 = 8;
public const int NR30 = 9;
public const int NR31 = 10;
public const int NR32 = 11;
public const int NR33 = 12;
public const int NR34 = 13;
public const int NR41 = 14;
public const int NR42 = 15;
public const int NR43 = 16;
public const int NR44 = 17;
public const int NR50 = 18;
public const int NR51 = 19;
public const int NR52 = 20;
public static int[] unused_bits = new int[] { 0x80, 0x3F, 0x00, 0xFF, 0xBF,
0x3F, 0x00, 0xFF, 0xBF,
0x7F, 0xFF, 0x9F, 0xFF, 0xBF,
0xFF, 0x00, 0x00, 0xBF,
0x00, 0x00, 0x70};
public byte[] Audio_Regs = new byte[21];
2017-11-18 16:43:58 +00:00
public byte[] Wave_RAM = new byte[16];
2017-08-29 13:18:28 +00:00
2017-11-14 16:59:32 +00:00
2017-11-15 17:17:58 +00:00
// Audio Variables
// derived
2017-11-19 14:30:18 +00:00
public bool WAVE_DAC_pow;
public bool NOISE_wdth_md;
public bool SQ1_negate;
public bool SQ1_trigger, SQ2_trigger, WAVE_trigger, NOISE_trigger;
public bool SQ1_len_en, SQ2_len_en, WAVE_len_en, NOISE_len_en;
public bool SQ1_env_add, SQ2_env_add, NOISE_env_add;
public byte WAVE_vol_code;
public byte NOISE_clk_shft;
public byte NOISE_div_code;
public byte SQ1_shift;
2017-11-18 16:43:58 +00:00
public byte SQ1_duty, SQ2_duty;
public byte SQ1_st_vol, SQ2_st_vol, NOISE_st_vol;
2017-11-19 14:30:18 +00:00
public byte SQ1_per, SQ2_per, NOISE_per;
public byte SQ1_swp_prd;
public int SQ1_frq, SQ2_frq, WAVE_frq;
2017-11-18 16:43:58 +00:00
public ushort SQ1_length, SQ2_length, WAVE_length, NOISE_length;
2017-11-15 17:17:58 +00:00
// state
2017-11-19 14:30:18 +00:00
public bool WAVE_can_get;
public bool SQ1_calc_done;
2017-11-18 16:43:58 +00:00
public bool SQ1_swp_enable;
public bool SQ1_vol_done, SQ2_vol_done, NOISE_vol_done;
2017-11-19 14:30:18 +00:00
public bool SQ1_enable, SQ2_enable, WAVE_enable, NOISE_enable;
public byte SQ1_vol_state, SQ2_vol_state, NOISE_vol_state;
public byte SQ1_duty_cntr, SQ2_duty_cntr;
public byte WAVE_wave_cntr;
public int SQ1_frq_shadow;
2017-11-18 16:43:58 +00:00
public int SQ1_intl_cntr, SQ2_intl_cntr, WAVE_intl_cntr, NOISE_intl_cntr;
2017-11-19 14:30:18 +00:00
public int SQ1_vol_per, SQ2_vol_per, NOISE_vol_per;
public int SQ1_intl_swp_cnt;
public int NOISE_LFSR;
2017-11-18 16:43:58 +00:00
public ushort SQ1_len_cntr, SQ2_len_cntr, WAVE_len_cntr, NOISE_len_cntr;
2017-11-15 17:17:58 +00:00
// computed
2017-11-18 16:43:58 +00:00
public int SQ1_output, SQ2_output, WAVE_output, NOISE_output;
2017-11-15 17:17:58 +00:00
// Contol Variables
2017-11-19 14:30:18 +00:00
public bool AUD_CTRL_vin_L_en;
public bool AUD_CTRL_vin_R_en;
public bool AUD_CTRL_sq1_L_en;
public bool AUD_CTRL_sq2_L_en;
public bool AUD_CTRL_wave_L_en;
public bool AUD_CTRL_noise_L_en;
public bool AUD_CTRL_sq1_R_en;
public bool AUD_CTRL_sq2_R_en;
public bool AUD_CTRL_wave_R_en;
public bool AUD_CTRL_noise_R_en;
public bool AUD_CTRL_power;
public byte AUD_CTRL_vol_L;
public byte AUD_CTRL_vol_R;
public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick;
public int master_audio_clock;
public byte ReadReg(int addr)
{
byte ret = 0;
switch (addr)
2017-11-18 16:43:58 +00:00
{
case 0xFF10: ret = (byte)(Audio_Regs[NR10] | unused_bits[NR10]); break; // NR10 (sweep)
case 0xFF11: ret = (byte)(Audio_Regs[NR11] | unused_bits[NR11]); break; // NR11 (sound length / wave pattern duty %)
case 0xFF12: ret = (byte)(Audio_Regs[NR12] | unused_bits[NR12]); break; // NR12 (envelope)
case 0xFF13: ret = (byte)(Audio_Regs[NR13] | unused_bits[NR13]); break; // NR13 (freq low)
case 0xFF14: ret = (byte)(Audio_Regs[NR14] | unused_bits[NR14]); break; // NR14 (freq hi)
case 0xFF16: ret = (byte)(Audio_Regs[NR21] | unused_bits[NR21]); break; // NR21 (sound length / wave pattern duty %)
case 0xFF17: ret = (byte)(Audio_Regs[NR22] | unused_bits[NR22]); break; // NR22 (envelope)
case 0xFF18: ret = (byte)(Audio_Regs[NR23] | unused_bits[NR23]); break; // NR23 (freq low)
case 0xFF19: ret = (byte)(Audio_Regs[NR24] | unused_bits[NR24]); break; // NR24 (freq hi)
case 0xFF1A: ret = (byte)(Audio_Regs[NR30] | unused_bits[NR30]); break; // NR30 (on/off)
case 0xFF1B: ret = (byte)(Audio_Regs[NR31] | unused_bits[NR31]); break; // NR31 (length)
case 0xFF1C: ret = (byte)(Audio_Regs[NR32] | unused_bits[NR32]); break; // NR32 (level output)
case 0xFF1D: ret = (byte)(Audio_Regs[NR33] | unused_bits[NR33]); break; // NR33 (freq low)
case 0xFF1E: ret = (byte)(Audio_Regs[NR34] | unused_bits[NR34]); break; // NR34 (freq hi)
case 0xFF20: ret = (byte)(Audio_Regs[NR41] | unused_bits[NR41]); break; // NR41 (length)
case 0xFF21: ret = (byte)(Audio_Regs[NR42] | unused_bits[NR42]); break; // NR42 (envelope)
case 0xFF22: ret = (byte)(Audio_Regs[NR43] | unused_bits[NR43]); break; // NR43 (shift)
case 0xFF23: ret = (byte)(Audio_Regs[NR44] | unused_bits[NR44]); break; // NR44 (trigger)
case 0xFF24: ret = (byte)(Audio_Regs[NR50] | unused_bits[NR50]); break; // NR50 (ctrl)
case 0xFF25: ret = (byte)(Audio_Regs[NR51] | unused_bits[NR51]); break; // NR51 (ctrl)
case 0xFF26: ret = (byte)(Read_NR52() | unused_bits[NR52]); break; // NR52 (ctrl)
2017-08-29 13:18:28 +00:00
// wave ram table
2017-11-19 14:30:18 +00:00
case 0xFF30:
case 0xFF31:
case 0xFF32:
case 0xFF33:
case 0xFF34:
case 0xFF35:
case 0xFF36:
case 0xFF37:
case 0xFF38:
case 0xFF39:
case 0xFF3A:
case 0xFF3B:
case 0xFF3C:
case 0xFF3D:
case 0xFF3E:
case 0xFF3F:
if (WAVE_enable)
{
if (WAVE_can_get) { ret = Wave_RAM[WAVE_wave_cntr >> 1]; }
else { ret = 0xFF; }
}
else { ret = Wave_RAM[addr & 0x0F]; }
break;
}
return ret;
}
public void WriteReg(int addr, byte value)
2017-08-29 13:18:28 +00:00
{
2017-11-14 16:59:32 +00:00
// while power is on, everything is writable
2017-11-19 14:30:18 +00:00
if (AUD_CTRL_power)
{
switch (addr)
{
2017-11-14 16:59:32 +00:00
case 0xFF10: // NR10 (sweep)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR10] = value;
SQ1_swp_prd = (byte)((value & 0x70) >> 4);
SQ1_negate = (value & 8) > 0;
SQ1_shift = (byte)(value & 7);
if (!SQ1_negate && SQ1_calc_done) { SQ1_enable = false; }
break;
2017-11-14 16:59:32 +00:00
case 0xFF11: // NR11 (sound length / wave pattern duty %)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR11] = value;
SQ1_duty = (byte)((value & 0xC0) >> 6);
SQ1_length = (ushort)(64 - (value & 0x3F));
SQ1_len_cntr = SQ1_length;
break;
2017-11-14 16:59:32 +00:00
case 0xFF12: // NR12 (envelope)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR12] = value;
SQ1_st_vol = (byte)((value & 0xF0) >> 4);
SQ1_env_add = (value & 8) > 0;
SQ1_per = (byte)(value & 7);
if (SQ1_per == 0) { SQ1_per = 8; }
if ((value & 0xF8) == 0) { SQ1_enable = SQ1_swp_enable = false; }
break;
2017-11-14 16:59:32 +00:00
case 0xFF13: // NR13 (freq low)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR13] = value;
SQ1_frq &= 0x700;
SQ1_frq |= value;
break;
2017-11-14 16:59:32 +00:00
case 0xFF14: // NR14 (freq hi)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR14] = value;
SQ1_trigger = (value & 0x80) > 0;
SQ1_frq &= 0xFF;
SQ1_frq |= (ushort)((value & 7) << 8);
if (((sequencer_len & 1) > 0))
{
if (!SQ1_len_en && ((value & 0x40) > 0) && (SQ1_len_cntr > 0))
{
SQ1_len_cntr--;
if ((SQ1_len_cntr == 0) && !SQ1_trigger) { SQ1_enable = SQ1_swp_enable = false; }
}
}
if (SQ1_trigger)
{
SQ1_enable = true;
SQ1_vol_done = false;
if (SQ1_len_cntr == 0)
{
SQ1_len_cntr = 64;
if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ1_len_cntr--; }
}
SQ1_vol_state = SQ1_st_vol;
SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8;
SQ1_frq_shadow = SQ1_frq;
SQ1_intl_cntr = (2048 - SQ1_frq_shadow) * 4;
SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8;
SQ1_calc_done = false;
2017-11-18 16:43:58 +00:00
if ((SQ1_shift > 0) || (SQ1_swp_prd > 0))
2017-11-16 01:44:47 +00:00
{
2017-11-18 16:43:58 +00:00
SQ1_swp_enable = true;
2017-11-19 14:30:18 +00:00
}
2017-11-18 16:43:58 +00:00
else
{
SQ1_swp_enable = false;
2017-11-19 14:30:18 +00:00
}
2017-11-18 16:43:58 +00:00
if (SQ1_shift > 0)
2017-11-16 01:44:47 +00:00
{
2017-11-18 16:43:58 +00:00
int shadow_frq = SQ1_frq_shadow;
shadow_frq = shadow_frq >> SQ1_shift;
if (SQ1_negate) { shadow_frq = -shadow_frq; }
shadow_frq += SQ1_frq_shadow;
// disable channel if overflow
if ((uint)shadow_frq > 2047)
{
SQ1_enable = SQ1_swp_enable = false;
}
// set negate mode flag that disables channel is negate clerar
if (SQ1_negate) { SQ1_calc_done = true; }
2017-11-16 01:44:47 +00:00
}
2017-11-19 14:30:18 +00:00
if ((SQ1_vol_state == 0) && !SQ1_env_add) { SQ1_enable = SQ1_swp_enable = false; }
2017-11-18 16:43:58 +00:00
}
2017-11-16 01:44:47 +00:00
2017-11-19 14:30:18 +00:00
SQ1_len_en = (value & 0x40) > 0;
break;
2017-11-14 16:59:32 +00:00
case 0xFF16: // NR21 (sound length / wave pattern duty %)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR21] = value;
SQ2_duty = (byte)((value & 0xC0) >> 6);
SQ2_length = (ushort)(64 - (value & 0x3F));
SQ2_len_cntr = SQ2_length;
break;
2017-11-14 16:59:32 +00:00
case 0xFF17: // NR22 (envelope)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR22] = value;
SQ2_st_vol = (byte)((value & 0xF0) >> 4);
SQ2_env_add = (value & 8) > 0;
2017-11-15 17:17:58 +00:00
SQ2_per = (byte)(value & 7);
//if (SQ2_per == 0) { SQ2_per = 8; }
2017-11-19 14:30:18 +00:00
if ((value & 0xF8) == 0) { SQ2_enable = false; }
break;
2017-11-14 16:59:32 +00:00
case 0xFF18: // NR23 (freq low)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR23] = value;
SQ2_frq &= 0x700;
SQ2_frq |= value;
break;
2017-11-14 16:59:32 +00:00
case 0xFF19: // NR24 (freq hi)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR24] = value;
SQ2_trigger = (value & 0x80) > 0;
SQ2_frq &= 0xFF;
SQ2_frq |= (ushort)((value & 7) << 8);
if ((sequencer_len & 1) > 0)
{
if (!SQ2_len_en && ((value & 0x40) > 0) && (SQ2_len_cntr > 0))
{
SQ2_len_cntr--;
if ((SQ2_len_cntr == 0) && !SQ2_trigger) { SQ2_enable = false; }
}
}
if (SQ2_trigger)
{
SQ2_enable = true;
SQ2_vol_done = false;
if (SQ2_len_cntr == 0)
{
SQ2_len_cntr = 64;
if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { SQ2_len_cntr--; }
}
SQ2_intl_cntr = (2048 - SQ2_frq) * 4;
SQ2_vol_state = SQ2_st_vol;
SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8;
if ((SQ2_vol_state == 0) && !SQ2_env_add) { SQ2_enable = false; }
}
SQ2_len_en = (value & 0x40) > 0;
break;
2017-11-14 16:59:32 +00:00
case 0xFF1A: // NR30 (on/off)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR30] = value;
WAVE_DAC_pow = (value & 0x80) > 0;
if (!WAVE_DAC_pow) { WAVE_enable = false; }
break;
2017-11-14 16:59:32 +00:00
case 0xFF1B: // NR31 (length)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR31] = value;
WAVE_length = (ushort)(256 - value);
WAVE_len_cntr = WAVE_length;
break;
2017-11-14 16:59:32 +00:00
case 0xFF1C: // NR32 (level output)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR32] = value;
WAVE_vol_code = (byte)((value & 0x60) >> 5);
break;
2017-11-14 16:59:32 +00:00
case 0xFF1D: // NR33 (freq low)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR33] = value;
WAVE_frq &= 0x700;
WAVE_frq |= value;
break;
2017-11-14 16:59:32 +00:00
case 0xFF1E: // NR34 (freq hi)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR34] = value;
WAVE_trigger = (value & 0x80) > 0;
WAVE_frq &= 0xFF;
WAVE_frq |= (ushort)((value & 7) << 8);
if ((sequencer_len & 1) > 0)
{
if (!WAVE_len_en && ((value & 0x40) > 0) && (WAVE_len_cntr > 0))
{
WAVE_len_cntr--;
if ((WAVE_len_cntr == 0) && !WAVE_trigger) { WAVE_enable = false; }
}
}
if (WAVE_trigger)
{
// some corruption occurs if triggering while reading
if (WAVE_enable && WAVE_intl_cntr == 2)
{
// we want to use the previous wave cntr value since it was just incremented
int t_wave_cntr = (WAVE_wave_cntr + 1) & 31;
if ((t_wave_cntr >> 1) < 4)
{
Wave_RAM[0] = Wave_RAM[t_wave_cntr >> 1];
}
else
{
Wave_RAM[0] = Wave_RAM[(t_wave_cntr >> 3) * 4];
Wave_RAM[1] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 1];
Wave_RAM[2] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 2];
Wave_RAM[3] = Wave_RAM[(t_wave_cntr >> 3) * 4 + 3];
}
}
WAVE_enable = true;
if (WAVE_len_cntr == 0)
{
WAVE_len_cntr = 256;
if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { WAVE_len_cntr--; }
}
WAVE_intl_cntr = (2048 - WAVE_frq) * 2 + 6; // trigger delay for wave channel
WAVE_wave_cntr = 0;
if (!WAVE_DAC_pow) { WAVE_enable = false; }
}
WAVE_len_en = (value & 0x40) > 0;
break;
2017-11-14 16:59:32 +00:00
case 0xFF20: // NR41 (length)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR41] = value;
NOISE_length = (ushort)(64 - (value & 0x3F));
NOISE_len_cntr = NOISE_length;
break;
2017-11-14 16:59:32 +00:00
case 0xFF21: // NR42 (envelope)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR42] = value;
NOISE_st_vol = (byte)((value & 0xF0) >> 4);
NOISE_env_add = (value & 8) > 0;
2017-11-15 17:17:58 +00:00
NOISE_per = (byte)(value & 7);
//if (NOISE_per == 0) { NOISE_per = 8; }
2017-11-19 14:30:18 +00:00
if ((value & 0xF8) == 0) { NOISE_enable = false; }
break;
2017-11-14 16:59:32 +00:00
case 0xFF22: // NR43 (shift)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR43] = value;
NOISE_clk_shft = (byte)((value & 0xF0) >> 4);
NOISE_wdth_md = (value & 8) > 0;
NOISE_div_code = (byte)(value & 7);
break;
2017-11-14 16:59:32 +00:00
case 0xFF23: // NR44 (trigger)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR44] = value;
NOISE_trigger = (value & 0x80) > 0;
if ((sequencer_len & 1) > 0)
{
if (!NOISE_len_en && ((value & 0x40) > 0) && (NOISE_len_cntr > 0))
{
NOISE_len_cntr--;
if ((NOISE_len_cntr == 0) && !NOISE_trigger) { NOISE_enable = false; }
}
}
if (NOISE_trigger)
{
NOISE_enable = true;
NOISE_vol_done = false;
if (NOISE_len_cntr == 0)
{
NOISE_len_cntr = 64;
if (((value & 0x40) > 0) && ((sequencer_len & 1) > 0)) { NOISE_len_cntr--; }
}
NOISE_intl_cntr = (DIVISOR[NOISE_div_code] << NOISE_clk_shft);
NOISE_vol_state = NOISE_st_vol;
NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8;
NOISE_LFSR = 0x7FFF;
if ((NOISE_vol_state == 0) && !NOISE_env_add) { NOISE_enable = false; }
2017-11-16 01:44:47 +00:00
}
2017-11-14 22:52:35 +00:00
2017-11-19 14:30:18 +00:00
NOISE_len_en = (value & 0x40) > 0;
break;
2017-11-14 16:59:32 +00:00
case 0xFF24: // NR50 (ctrl)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR50] = value;
AUD_CTRL_vin_L_en = (value & 0x80) > 0;
AUD_CTRL_vol_L = (byte)((value & 0x70) >> 4);
AUD_CTRL_vin_R_en = (value & 8) > 0;
AUD_CTRL_vol_R = (byte)(value & 7);
break;
2017-11-14 16:59:32 +00:00
case 0xFF25: // NR51 (ctrl)
2017-11-19 14:30:18 +00:00
Audio_Regs[NR51] = value;
AUD_CTRL_noise_L_en = (value & 0x80) > 0;
AUD_CTRL_wave_L_en = (value & 0x40) > 0;
AUD_CTRL_sq2_L_en = (value & 0x20) > 0;
AUD_CTRL_sq1_L_en = (value & 0x10) > 0;
AUD_CTRL_noise_R_en = (value & 8) > 0;
AUD_CTRL_wave_R_en = (value & 4) > 0;
AUD_CTRL_sq2_R_en = (value & 2) > 0;
AUD_CTRL_sq1_R_en = (value & 1) > 0;
break;
case 0xFF26: // NR52 (ctrl)
AUD_CTRL_power = (value & 0x80) > 0;
if (!AUD_CTRL_power) { power_off(); }
2017-11-14 16:59:32 +00:00
break;
// wave ram table
2017-11-19 14:30:18 +00:00
case 0xFF30:
case 0xFF31:
case 0xFF32:
case 0xFF33:
case 0xFF34:
case 0xFF35:
case 0xFF36:
case 0xFF37:
case 0xFF38:
case 0xFF39:
case 0xFF3A:
case 0xFF3B:
case 0xFF3C:
case 0xFF3D:
case 0xFF3E:
case 0xFF3F:
if (WAVE_enable)
{
if (WAVE_can_get) { Wave_RAM[WAVE_wave_cntr >> 1] = value; }
}
else { Wave_RAM[addr & 0xF] = value; }
break;
}
2017-11-14 16:59:32 +00:00
}
// when power is off, only length counters and waveRAM are effected by writes
2017-11-19 14:30:18 +00:00
else
{
switch (addr)
{
2017-11-14 16:59:32 +00:00
case 0xFF11: // NR11 (sound length / wave pattern duty %)
2017-11-19 14:30:18 +00:00
SQ1_length = (ushort)(64 - (value & 0x3F));
SQ1_len_cntr = SQ1_length;
break;
2017-11-14 16:59:32 +00:00
case 0xFF16: // NR21 (sound length / wave pattern duty %)
2017-11-19 14:30:18 +00:00
SQ2_length = (ushort)(64 - (value & 0x3F));
SQ2_len_cntr = SQ2_length;
break;
2017-11-14 16:59:32 +00:00
case 0xFF1B: // NR31 (length)
2017-11-19 14:30:18 +00:00
WAVE_length = (ushort)(256 - value);
WAVE_len_cntr = WAVE_length;
break;
2017-11-14 16:59:32 +00:00
case 0xFF20: // NR41 (length)
2017-11-19 14:30:18 +00:00
NOISE_length = (ushort)(64 - (value & 0x3F));
NOISE_len_cntr = NOISE_length;
break;
case 0xFF26: // NR52 (ctrl)
AUD_CTRL_power = (value & 0x80) > 0;
if (AUD_CTRL_power)
{
sequencer_vol = 0;
sequencer_len = 0;
sequencer_swp = 0;
2017-11-19 14:30:18 +00:00
}
2017-11-14 16:59:32 +00:00
break;
// wave ram table
2017-11-19 14:30:18 +00:00
case 0xFF30:
case 0xFF31:
case 0xFF32:
case 0xFF33:
case 0xFF34:
case 0xFF35:
case 0xFF36:
case 0xFF37:
case 0xFF38:
case 0xFF39:
case 0xFF3A:
case 0xFF3B:
case 0xFF3C:
case 0xFF3D:
case 0xFF3E:
case 0xFF3F:
Wave_RAM[addr & 0x0F] = value;
break;
}
}
}
public void tick()
2017-08-29 13:18:28 +00:00
{
2017-11-14 19:43:58 +00:00
// calculate square1's output
2017-11-19 14:30:18 +00:00
if (SQ1_enable)
{
SQ1_intl_cntr--;
if (SQ1_intl_cntr == 0)
{
SQ1_intl_cntr = (2048 - SQ1_frq_shadow) * 4;
SQ1_duty_cntr++;
SQ1_duty_cntr &= 7;
SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr];
SQ1_output *= SQ1_vol_state;
}
2017-11-14 19:43:58 +00:00
}
// calculate square2's output
2017-11-19 14:30:18 +00:00
if (SQ2_enable)
{
SQ2_intl_cntr--;
if (SQ2_intl_cntr == 0)
{
SQ2_intl_cntr = (2048 - SQ2_frq) * 4;
SQ2_duty_cntr++;
SQ2_duty_cntr &= 7;
SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr];
SQ2_output *= SQ2_vol_state;
}
2017-11-14 19:43:58 +00:00
}
// calculate wave output
2017-11-19 14:30:18 +00:00
WAVE_can_get = false;
if (WAVE_enable)
{
WAVE_intl_cntr--;
if (WAVE_intl_cntr == 0)
{
WAVE_intl_cntr = (2048 - WAVE_frq) * 2;
WAVE_wave_cntr++;
WAVE_wave_cntr &= 0x1F;
WAVE_can_get = true;
byte sample = Wave_RAM[WAVE_wave_cntr >> 1];
if ((WAVE_wave_cntr & 1) == 0)
{
sample = (byte)(sample >> 4);
}
if (WAVE_vol_code == 0)
{
sample = (byte)((sample & 0xF) >> 4);
}
else if (WAVE_vol_code == 1)
{
sample = (byte)(sample & 0xF);
}
else if (WAVE_vol_code == 2)
{
sample = (byte)((sample & 0xF) >> 1);
}
else
{
sample = (byte)((sample & 0xF) >> 2);
}
WAVE_output = sample;
if (!WAVE_DAC_pow) { WAVE_output = 0; }
}
2017-11-14 22:52:35 +00:00
}
2017-11-14 19:43:58 +00:00
// calculate noise output
2017-11-19 14:30:18 +00:00
if (NOISE_enable)
{
NOISE_intl_cntr--;
if (NOISE_intl_cntr == 0)
{
NOISE_intl_cntr = (DIVISOR[NOISE_div_code] << NOISE_clk_shft);
int bit_lfsr = (NOISE_LFSR & 1) ^ ((NOISE_LFSR & 2) >> 1);
NOISE_LFSR = (NOISE_LFSR >> 1) & 0x3FFF;
NOISE_LFSR |= (bit_lfsr << 14);
if (NOISE_wdth_md)
{
NOISE_LFSR = NOISE_LFSR & 0x7FBF;
NOISE_LFSR |= (bit_lfsr << 6);
}
NOISE_output = NOISE_LFSR & 1;
NOISE_output *= NOISE_vol_state;
}
2017-11-14 22:52:35 +00:00
}
// add up components to each channel
2017-11-19 14:30:18 +00:00
int L_final = 0;
int R_final = 0;
if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; }
if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; }
if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; }
if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; }
if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; }
if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; }
if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; }
if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; }
L_final *= (AUD_CTRL_vol_L + 1);
2017-11-15 17:17:58 +00:00
R_final *= (AUD_CTRL_vol_R + 1);
2017-11-14 22:52:35 +00:00
// send out an actual sample every 94 cycles
2017-11-19 14:30:18 +00:00
master_audio_clock++;
if (master_audio_clock == 94)
{
master_audio_clock = 0;
if (AudioClocks < 1500)
{
2017-11-15 17:17:58 +00:00
AudioSamples[AudioClocks] = (short)(L_final * 4);
2017-11-19 14:30:18 +00:00
AudioClocks++;
AudioSamples[AudioClocks] = (short)(R_final * 4);
AudioClocks++;
}
2017-11-14 22:52:35 +00:00
}
2017-11-14 19:43:58 +00:00
// frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over)
2017-11-19 14:30:18 +00:00
sequencer_tick++;
if (sequencer_tick == 8192)
{
sequencer_tick = 0;
sequencer_vol++; sequencer_vol &= 0x7;
sequencer_len++; sequencer_len &= 0x7;
2017-11-14 19:43:58 +00:00
sequencer_swp++; sequencer_swp &= 0x7;
// clock the lengths
2017-11-19 14:30:18 +00:00
if ((sequencer_len & 1) > 0)
{
if (SQ1_len_en && SQ1_len_cntr > 0)
{
SQ1_len_cntr--;
if (SQ1_len_cntr == 0) { SQ1_enable = SQ1_swp_enable = false; }
}
if (SQ2_len_en && SQ2_len_cntr > 0)
{
SQ2_len_cntr--;
if (SQ2_len_cntr == 0) { SQ2_enable = false; }
}
if (WAVE_len_en && WAVE_len_cntr > 0)
{
WAVE_len_cntr--;
if (WAVE_len_cntr == 0) { WAVE_enable = false; }
}
if (NOISE_len_en && NOISE_len_cntr > 0)
{
NOISE_len_cntr--;
if (NOISE_len_cntr == 0) { NOISE_enable = false; }
}
2017-11-14 19:43:58 +00:00
}
// clock the sweep
2017-11-19 14:30:18 +00:00
if ((sequencer_swp == 3) || (sequencer_swp == 7))
{
SQ1_intl_swp_cnt--;
if ((SQ1_intl_swp_cnt == 0) && SQ1_swp_enable)
{
SQ1_intl_swp_cnt = SQ1_swp_prd > 0 ? SQ1_swp_prd : 8;
if ((SQ1_swp_prd > 0))
{
int shadow_frq = SQ1_frq_shadow;
shadow_frq = shadow_frq >> SQ1_shift;
if (SQ1_negate) { shadow_frq = -shadow_frq; }
2017-11-15 17:17:58 +00:00
shadow_frq += SQ1_frq_shadow;
2017-11-14 19:43:58 +00:00
// set negate mode flag that disables channel is negate clerar
if (SQ1_negate) { SQ1_calc_done = true; }
2017-11-15 17:17:58 +00:00
// disable channel if overflow
2017-11-19 14:30:18 +00:00
if ((uint)shadow_frq > 2047)
{
SQ1_enable = SQ1_swp_enable = false;
}
else
2017-11-15 17:17:58 +00:00
{
2017-11-18 16:43:58 +00:00
if (SQ1_shift > 0)
2017-11-15 17:17:58 +00:00
{
2017-11-18 16:43:58 +00:00
shadow_frq &= 0x7FF;
SQ1_frq = shadow_frq;
SQ1_frq_shadow = shadow_frq;
// note that we also write back the frequency to the actual register
Audio_Regs[NR13] = (byte)(SQ1_frq & 0xFF);
Audio_Regs[NR14] &= 0xF8;
Audio_Regs[NR14] |= (byte)((SQ1_frq >> 8) & 7);
// after writing, we repeat the process and do another overflow check
shadow_frq = SQ1_frq_shadow;
shadow_frq = shadow_frq >> SQ1_shift;
if (SQ1_negate) { shadow_frq = -shadow_frq; }
shadow_frq += SQ1_frq_shadow;
if ((uint)shadow_frq > 2047)
{
SQ1_enable = SQ1_swp_enable = false;
2017-11-19 14:30:18 +00:00
}
}
}
}
}
2017-11-14 19:43:58 +00:00
}
// clock the volume envelope
2017-11-19 14:30:18 +00:00
if (sequencer_vol == 0)
{
if (SQ1_per > 0)
{
SQ1_vol_per--;
if (SQ1_vol_per == 0)
{
SQ1_vol_per = (SQ1_per > 0) ? SQ1_per : 8;
if (!SQ1_vol_done)
{
if (SQ1_env_add)
{
if (SQ1_vol_state < 15) { SQ1_vol_state++; }
else { SQ1_vol_done = true; }
}
else
{
if (SQ1_vol_state >= 1) { SQ1_vol_state--; }
else { SQ1_vol_done = true; }
}
2017-11-19 14:30:18 +00:00
}
}
}
if (SQ2_per > 0)
{
SQ2_vol_per--;
if (SQ2_vol_per == 0)
{
SQ2_vol_per = (SQ2_per > 0) ? SQ2_per : 8;
if (!SQ2_vol_done)
{
if (SQ2_env_add)
{
if (SQ2_vol_state < 15) { SQ2_vol_state++; }
else { SQ2_vol_done = true; }
}
else
{
if (SQ2_vol_state >= 1) { SQ2_vol_state--; }
else { SQ2_vol_done = true; }
}
2017-11-19 14:30:18 +00:00
}
}
}
if (NOISE_per > 0)
{
NOISE_vol_per--;
if (NOISE_vol_per == 0)
{
NOISE_vol_per = (NOISE_per > 0) ? NOISE_per : 8;
if (!NOISE_vol_done)
{
if (NOISE_env_add)
{
if (NOISE_vol_state < 15) { NOISE_vol_state++; }
else { NOISE_vol_done = true; }
}
else
{
if (NOISE_vol_state >= 1) { NOISE_vol_state--; }
else { NOISE_vol_done = true; }
}
2017-11-19 14:30:18 +00:00
}
}
}
}
}
}
public void power_off()
{
for (int i = 0; i < 21; i++)
{
Audio_Regs[i] = 0;
}
// reset derived values
2017-11-19 14:30:18 +00:00
sync_channels();
// reset state variables
SQ1_enable = SQ1_swp_enable = false;
SQ2_enable = false;
WAVE_enable = false;
NOISE_enable = false;
SQ1_len_en = false;
SQ2_len_en = false;
WAVE_len_en = false;
NOISE_len_en = false;
sequencer_len = 0;
sequencer_vol = 0;
sequencer_swp = 0;
master_audio_clock = 0;
}
public void Reset()
{
Wave_RAM = new byte[] { 0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C,
0x60, 0x59, 0x59, 0xB0, 0x34, 0xB8, 0x2E, 0xDA };
Audio_Regs = new byte[21];
AudioClocks = 0;
master_audio_clock = 0;
sequencer_len = 0;
sequencer_swp = 0;
sequencer_vol = 0;
sequencer_tick = 0;
}
public void SyncState(Serializer ser)
{
ser.Sync("Audio_Regs", ref Audio_Regs, false);
2017-08-29 13:18:28 +00:00
ser.Sync("Wave_Ram", ref Wave_RAM, false);
2017-11-15 17:17:58 +00:00
// save state variables
2017-11-19 14:30:18 +00:00
ser.Sync("WAVE_can_get", ref WAVE_can_get);
ser.Sync("SQ1_vol_done", ref SQ1_vol_done);
ser.Sync("SQ2_vol_done", ref SQ2_vol_done);
ser.Sync("NOISE_vol_done", ref NOISE_vol_done);
ser.Sync("SQ1_calc_done", ref SQ1_calc_done);
2017-11-18 16:43:58 +00:00
ser.Sync("SQ1_swp_enable", ref SQ1_swp_enable);
2017-11-19 14:30:18 +00:00
ser.Sync("SQ1_length_counter", ref SQ1_len_cntr);
ser.Sync("SQ2_length_counter", ref SQ2_len_cntr);
ser.Sync("WAVE_length_counter", ref WAVE_len_cntr);
ser.Sync("NOISE_length_counter", ref NOISE_len_cntr);
ser.Sync("SQ1_enable", ref SQ1_enable);
ser.Sync("SQ2_enable", ref SQ2_enable);
ser.Sync("WAVE_enable", ref WAVE_enable);
ser.Sync("NOISE_enable", ref NOISE_enable);
ser.Sync("SQ1_vol_state", ref SQ1_vol_state);
ser.Sync("SQ2_vol_state", ref SQ2_vol_state);
ser.Sync("NOISE_vol_state", ref NOISE_vol_state);
ser.Sync("SQ1_duty_cntr", ref SQ1_duty_cntr);
ser.Sync("SQ2_duty_cntr", ref SQ2_duty_cntr);
ser.Sync("WAVE_wave_cntr", ref WAVE_wave_cntr);
ser.Sync("SQ1_frq_shadow", ref SQ1_frq_shadow);
ser.Sync("SQ1_intl_cntr", ref SQ1_intl_cntr);
ser.Sync("SQ2_intl_cntr", ref SQ2_intl_cntr);
ser.Sync("WAVE_intl_cntr", ref WAVE_intl_cntr);
ser.Sync("NOISE_intl_cntr", ref NOISE_intl_cntr);
ser.Sync("SQ1_vol_per", ref SQ1_vol_per);
ser.Sync("SQ2_vol_per", ref SQ2_vol_per);
ser.Sync("NOISE_vol_per", ref NOISE_vol_per);
ser.Sync("SQ1_intl_swp_cnt", ref SQ1_intl_swp_cnt);
ser.Sync("NOISE_LFSR", ref NOISE_LFSR);
ser.Sync("SQ1_len_cntr", ref SQ1_len_cntr);
ser.Sync("SQ2_len_cntr", ref SQ2_len_cntr);
ser.Sync("WAVE_len_cntr", ref WAVE_len_cntr);
ser.Sync("NOISE_len_cntr", ref NOISE_len_cntr);
ser.Sync("sequencer_len", ref sequencer_len);
ser.Sync("sequencer_vol", ref sequencer_vol);
ser.Sync("sequencer_swp", ref sequencer_swp);
ser.Sync("sequencer_tick", ref sequencer_tick);
2017-11-14 22:52:35 +00:00
ser.Sync("master_audio_clock", ref master_audio_clock);
2017-11-14 16:59:32 +00:00
// get derived state
2017-11-19 14:30:18 +00:00
if (ser.IsReader)
{
sync_channels();
}
}
public void sync_channels()
{
SQ1_swp_prd = (byte)((Audio_Regs[NR10] & 0x70) >> 4);
SQ1_negate = (Audio_Regs[NR10] & 8) > 0;
SQ1_shift = (byte)(Audio_Regs[NR10] & 7);
SQ1_duty = (byte)((Audio_Regs[NR11] & 0xC0) >> 6);
SQ1_length = (ushort)(64 - Audio_Regs[NR11] & 0x3F);
SQ1_st_vol = (byte)((Audio_Regs[NR12] & 0xF0) >> 4);
SQ1_env_add = (Audio_Regs[NR12] & 8) > 0;
SQ1_per = (byte)(Audio_Regs[NR12] & 7);
SQ1_frq &= 0x700;
SQ1_frq |= Audio_Regs[NR13];
SQ1_trigger = (Audio_Regs[NR14] & 0x80) > 0;
SQ1_len_en = (Audio_Regs[NR14] & 0x40) > 0;
SQ1_frq &= 0xFF;
2017-11-15 17:17:58 +00:00
SQ1_frq |= (ushort)((Audio_Regs[NR14] & 7) << 8);
2017-11-14 16:59:32 +00:00
2017-11-19 14:30:18 +00:00
SQ2_duty = (byte)((Audio_Regs[NR21] & 0xC0) >> 6);
SQ2_length = (ushort)(64 - Audio_Regs[NR21] & 0x3F);
SQ2_st_vol = (byte)((Audio_Regs[NR22] & 0xF0) >> 4);
SQ2_env_add = (Audio_Regs[NR22] & 8) > 0;
SQ2_per = (byte)(Audio_Regs[NR22] & 7);
SQ2_frq &= 0x700;
SQ2_frq |= Audio_Regs[NR23];
SQ2_trigger = (Audio_Regs[NR24] & 0x80) > 0;
SQ2_len_en = (Audio_Regs[NR24] & 0x40) > 0;
SQ2_frq &= 0xFF;
SQ2_frq |= (ushort)((Audio_Regs[NR24] & 7) << 8);
WAVE_DAC_pow = (Audio_Regs[NR30] & 0x80) > 0;
WAVE_length = (ushort)(256 - Audio_Regs[NR31]);
WAVE_vol_code = (byte)((Audio_Regs[NR32] & 0x60) >> 5);
WAVE_frq &= 0x700;
WAVE_frq |= Audio_Regs[NR33];
WAVE_trigger = (Audio_Regs[NR34] & 0x80) > 0;
WAVE_len_en = (Audio_Regs[NR34] & 0x40) > 0;
WAVE_frq &= 0xFF;
WAVE_frq |= (ushort)((Audio_Regs[NR34] & 7) << 8);
NOISE_length = (ushort)(64 - Audio_Regs[NR41] & 0x3F);
NOISE_st_vol = (byte)((Audio_Regs[NR42] & 0xF0) >> 4);
NOISE_env_add = (Audio_Regs[NR42] & 8) > 0;
NOISE_per = (byte)(Audio_Regs[NR42] & 7);
NOISE_clk_shft = (byte)((Audio_Regs[NR43] & 0xF0) >> 4);
NOISE_wdth_md = (Audio_Regs[NR43] & 8) > 0;
NOISE_div_code = (byte)(Audio_Regs[NR43] & 7);
WAVE_trigger = (Audio_Regs[NR44] & 0x80) > 0;
WAVE_len_en = (Audio_Regs[NR44] & 0x40) > 0;
AUD_CTRL_vin_L_en = (Audio_Regs[NR50] & 0x80) > 0;
AUD_CTRL_vol_L = (byte)((Audio_Regs[NR50] & 0x70) >> 4);
AUD_CTRL_vin_R_en = (Audio_Regs[NR50] & 8) > 0;
AUD_CTRL_vol_R = (byte)(Audio_Regs[NR50] & 7);
AUD_CTRL_noise_L_en = (Audio_Regs[NR51] & 0x80) > 0;
AUD_CTRL_wave_L_en = (Audio_Regs[NR51] & 0x40) > 0;
AUD_CTRL_sq2_L_en = (Audio_Regs[NR51] & 0x20) > 0;
AUD_CTRL_sq1_L_en = (Audio_Regs[NR51] & 0x10) > 0;
AUD_CTRL_noise_R_en = (Audio_Regs[NR51] & 8) > 0;
AUD_CTRL_wave_R_en = (Audio_Regs[NR51] & 4) > 0;
AUD_CTRL_sq2_R_en = (Audio_Regs[NR51] & 2) > 0;
AUD_CTRL_sq1_R_en = (Audio_Regs[NR51] & 1) > 0;
AUD_CTRL_power = (Audio_Regs[NR51] & 0x80) > 0;
2017-08-29 13:18:28 +00:00
}
2017-11-18 16:43:58 +00:00
public byte Read_NR52()
2017-08-29 13:18:28 +00:00
{
2017-11-18 16:43:58 +00:00
return (byte)(
((AUD_CTRL_power ? 1 : 0) << 7) |
((SQ1_enable ? 1 : 0)) |
((SQ2_enable ? 1 : 0) << 1) |
((WAVE_enable ? 1 : 0) << 2) |
((NOISE_enable ? 1 : 0) << 3));
2017-08-29 13:18:28 +00:00
}
2017-11-19 14:30:18 +00:00
#region audio
public bool CanProvideAsync => false;
public int AudioClocks;
public short[] AudioSamples = new short[1500];
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported_");
}
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
nsamp = AudioClocks / 2;
short[] temp_samp = new short[AudioClocks];
for (int i = 0; i < AudioClocks; i++)
{
temp_samp[i] = AudioSamples[i];
}
samples = temp_samp;
AudioClocks = 0;
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
AudioClocks = 0;
}
private void GetSamples(short[] samples)
{
2017-08-29 13:18:28 +00:00
}
2017-11-19 14:30:18 +00:00
#endregion
}
2017-08-29 13:18:28 +00:00
}