[NES] work on sound, improve square waves muchly, add 6502 IRQ signal, make add some infrastructure to keep from buffering samples while core is silenced.

This commit is contained in:
zeromus 2011-03-19 09:12:56 +00:00
parent 0a354c2748
commit c7f0c457ad
16 changed files with 205 additions and 71 deletions

View File

@ -22,6 +22,10 @@ namespace BizHawk.Emulation.CPUs.M6502
TriggerException(ExceptionType.NMI);
NMI = false;
}
if (IRQ && !FlagI)
{
TriggerException(ExceptionType.IRQ);
}
if(debug) Console.WriteLine(State());
@ -1201,9 +1205,7 @@ FlagT = true;// this seems wrong
PendingCycles -= 7; TotalExecutedCycles += 7;
break;
default:
//throw new Exception(String.Format("Unhandled opcode: {0:X2}", opcode));
//Console.WriteLine(String.Format("Unhandled opcode: {0:X2}", opcode));
break;
throw new Exception(String.Format("Unhandled opcode: {0:X2}", opcode));
}
}
}

View File

@ -64,10 +64,11 @@ namespace BizHawk.Emulation.CPUs.M6502
private const ushort NMIVector = 0xFFFA;
private const ushort ResetVector = 0xFFFC;
private const ushort BRKVector = 0xFFFE;
private const ushort IRQVector = 0xFFFE;
enum ExceptionType
{
BRK, NMI
BRK, NMI, IRQ
}
void TriggerException(ExceptionType type)
@ -80,10 +81,19 @@ namespace BizHawk.Emulation.CPUs.M6502
WriteMemory((ushort)(S-- + 0x100), P);
P = oldP;
FlagI = true;
if (type == ExceptionType.NMI)
PC = ReadWord(NMIVector);
else
PC = ReadWord(BRKVector);
switch (type)
{
case ExceptionType.NMI:
PC = ReadWord(NMIVector);
break;
case ExceptionType.IRQ:
PC = ReadWord(IRQVector);
break;
case ExceptionType.BRK:
PC = ReadWord(BRKVector);
break;
default: throw new Exception();
}
PendingCycles -= 7;
}
@ -96,8 +106,7 @@ namespace BizHawk.Emulation.CPUs.M6502
public ushort PC;
public byte S;
// TODO IRQ, NMI functions
public bool Interrupt;
public bool IRQ;
public bool NMI;
public void SaveStateText(TextWriter writer)
@ -110,7 +119,7 @@ namespace BizHawk.Emulation.CPUs.M6502
writer.WriteLine("PC {0:X4}", PC);
writer.WriteLine("S {0:X2}", S);
writer.WriteLine("NMI {0}", NMI);
writer.WriteLine("Interrupt {0}", Interrupt);
writer.WriteLine("IRQ {0}", IRQ);
writer.WriteLine("TotalExecutedCycles {0}", TotalExecutedCycles);
writer.WriteLine("PendingCycles {0}", PendingCycles);
writer.WriteLine("[/MOS6502]\n");
@ -137,8 +146,8 @@ namespace BizHawk.Emulation.CPUs.M6502
S = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "NMI")
NMI = bool.Parse(args[1]);
else if (args[0] == "Interrupt")
Interrupt = bool.Parse(args[1]);
else if (args[0] == "IRQ")
IRQ = bool.Parse(args[1]);
else if (args[0] == "TotalExecutedCycles")
TotalExecutedCycles = int.Parse(args[1]);
else if (args[0] == "PendingCycles")

View File

@ -47,13 +47,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
//reg0
int duty_cnt, env_loop, env_constant, env_cnt_value;
//reg1
int sweep_en, sweep_period, negate, shiftcount;
int sweep_en, sweep_divider_cnt, sweep_negate, sweep_shiftcount;
bool sweep_reload;
//reg2/3
int len_cnt;
int timer_raw_reload_value, timer_reload_value;
//from other apu regs
public int lenctr_en;
//misc..
int lenctr_en;
public bool IsLenCntNonZero() { return len_cnt > 0; }
public void WriteReg(int addr, byte val)
{
@ -67,30 +70,42 @@ namespace BizHawk.Emulation.Consoles.Nintendo
duty_cnt = (val >> 6) & 3;
break;
case 1:
shiftcount = val & 7;
negate = (val >> 3) & 1;
sweep_period = (val >> 4) & 7;
sweep_shiftcount = val & 7;
sweep_negate = (val >> 3) & 1;
sweep_divider_cnt = (val >> 4) & 7;
sweep_en = (val >> 7) & 1;
sweep_reload = true;
break;
case 2:
timer_reload_value = (timer_reload_value & ~0xFF) | val;
calc_sweep_unit();
timer_raw_reload_value = timer_reload_value * 2 + 2;
//if (unit == 1) Console.WriteLine("{0} timer_reload_value: {1}", unit, timer_reload_value);
break;
case 3:
len_cnt = LENGTH_TABLE[(val >> 3) & 0x1F];
timer_reload_value = (timer_reload_value & 0xFF) | ((val & 0x07) << 8);
timer_raw_reload_value = timer_reload_value * 2;
duty_step = 0;
timer_raw_reload_value = timer_reload_value * 2 + 2;
//duty_step = 0; //?just a guess?
timer_counter = timer_raw_reload_value;
calc_sweep_unit();
env_start_flag = 1;
//allow the lenctr_en to kill the len_cnt
set_lenctr_en(lenctr_en);
//serves as a useful note-on diagnostic
//Console.WriteLine("{0} timer_reload_value: {1}", unit, timer_reload_value);
//if(unit==1) Console.WriteLine("{0} timer_reload_value: {1}", unit, timer_reload_value);
break;
}
}
int swp_val_result;
public void set_lenctr_en(int value)
{
lenctr_en = value;
//if the length counter is not enabled, then we must disable the length system in this way
if (lenctr_en == 0) len_cnt = 0;
}
int swp_divider_counter;
bool swp_silence;
int duty_step;
int timer_counter;
@ -98,27 +113,51 @@ namespace BizHawk.Emulation.Consoles.Nintendo
int env_start_flag, env_divider, env_counter, env_output;
void calc_sweep_unit()
{
//1's complement for chan 0, 2's complement if chan 1
if (negate == 1) //check to see if negate is on
swp_val_result = ~swp_val_result + unit;
//add with the shifter chan
swp_val_result += timer_reload_value;
if ((timer_reload_value < 8) ||
((swp_val_result > 0x7FF) && (negate==0)))
swp_silence = true; //silence
else
swp_silence = false; //don't silence
}
public void clock_length_and_sweep()
{
//(as well as length counter)
if(sweep_en==1)
timer_raw_reload_value = swp_val_result & 0x7FF;
//this should be optimized to update only when `timer_reload_value` changes
int sweep_shifter = timer_reload_value >> sweep_shiftcount;
if (sweep_negate == 1)
sweep_shifter = ~sweep_shifter + unit;
sweep_shifter += timer_reload_value;
//this sweep logic is always enabled:
swp_silence = (timer_reload_value < 8 || (sweep_shifter > 0x7FF && sweep_negate == 0));
//does enable only block the pitch bend? does the clocking proceed?
if (sweep_en == 1)
{
//clock divider
if (swp_divider_counter != 0) swp_divider_counter--;
if (swp_divider_counter == 0)
{
swp_divider_counter = sweep_divider_cnt + 1;
//divider was clocked: process sweep pitch bend
if (sweep_shiftcount != 0 && !swp_silence)
{
timer_reload_value = sweep_shifter;
timer_raw_reload_value = timer_reload_value * 2 + 2;
}
//TODO - does this change the user's reload value or the latched reload value?
}
//handle divider reload, after clocking happens
if (sweep_reload)
{
swp_divider_counter = sweep_divider_cnt + 1;
sweep_reload = false;
}
}
//env_loopdoubles as "halt length counter"
if (env_loop == 0 && len_cnt > 0)
len_cnt--;
}
public void clock_env()
@ -131,7 +170,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
else
{
env_divider--;
if(env_divider != 0) env_divider--;
if (env_divider == 0)
{
env_divider = (env_cnt_value + 1);
@ -162,7 +201,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
duty_step = (duty_step + 1) & 7;
//reload timer
timer_counter = timer_raw_reload_value + 2;
timer_counter = timer_raw_reload_value;
}
if (PULSE_DUTY[duty_cnt, duty_step] == 1) //we are outputting something
{
@ -171,8 +210,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
if (swp_silence)
sample = 0;
//if (len_cnt==0) //length counter is 0
// sample = 0; //silenced
if (len_cnt==0) //length counter is 0
sample = 0; //silenced
}
else
sample = 0; //duty cycle is 0, silenced.
@ -269,7 +308,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
PulseUnit[] pulse = { new PulseUnit(0), new PulseUnit(1) };
TriangleUnit triangle = new TriangleUnit();
int sequencer_counter, sequencer_step, sequencer_mode, sequencer_irq_inhibit, sequencer_irq_flag;
int sequencer_counter, sequencer_step, sequencer_mode, sequencer_irq_inhibit;
void sequencer_reset()
{
sequencer_counter = 0;
@ -308,9 +347,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
if (sequencer_irq_inhibit == 0)
{
sequencer_irq_flag = 1;
//nes.cpu.Interrupt = true;
//Console.WriteLine("APU trigger IRQ (cpu needs implementation)");
nes.irq_apu = true;
}
sequencer_step = 0;
}
@ -349,13 +386,14 @@ namespace BizHawk.Emulation.Consoles.Nintendo
triangle.WriteReg(addr - 0x4008, val);
break;
case 0x4015:
pulse[0].lenctr_en = (val & 1);
pulse[1].lenctr_en = ((val>>1) & 1);
pulse[0].set_lenctr_en(val & 1);
pulse[1].set_lenctr_en((val >> 1) & 1);
break;
case 0x4017:
sequencer_mode = (val>>7)&1;
if(((val>>6)&1)==1)
sequencer_irq_inhibit = 0;
sequencer_irq_inhibit = (val >> 6) & 1;
if (sequencer_irq_inhibit == 1)
nes.irq_apu = false;
sequencer_reset();
break;
}
@ -365,8 +403,22 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
switch (addr)
{
case 0x4015:
{
//notice a missing bit here. should properly emulate with empty bus
//if an interrupt flag was set at the same moment of the read, it will read back as 1 but it will not be cleared.
int dmc_irq_flag = 0; //todo
int dmc_nonzero = 0; //todo
int noise_nonzero = 0; //todo
int tri_nonzero = 0; //todo
int pulse1_nonzero = pulse[1].IsLenCntNonZero() ? 1 : 0;
int pulse0_nonzero = pulse[0].IsLenCntNonZero() ? 1 : 0;
int ret = (dmc_irq_flag << 7) | ((nes.irq_apu?1:0) << 6) | (dmc_nonzero << 4) | (noise_nonzero << 3) | (tri_nonzero<<2) | (pulse1_nonzero<<1) | (pulse0_nonzero);
nes.irq_apu = false;
return (byte)ret;
}
default:
return 0x00;
return 0xFF;
}
}
@ -376,6 +428,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo
RunOne();
}
public void DiscardSamples()
{
metaspu.buffer.clear();
}
void RunOne()
{
pulse[0].Run();

View File

@ -23,6 +23,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo
CartInfo cart; //the current cart prototype. should be moved into the board, perhaps
INESBoard board; //the board hardware that is currently driving things
bool _irq_apu;
public bool irq_apu { get { return _irq_apu; } set { _irq_apu = value; sync_irq(); } }
void sync_irq()
{
cpu.IRQ = _irq_apu;
}
//user configuration
int[,] palette = new int[64,3];
int[] palette_compiled = new int[64];

View File

@ -46,6 +46,7 @@ namespace BizHawk
public int BufferHeight { get { return 192; } }
public int BackgroundColor { get { return 0; } }
public void GetSamples(short[] samples) { }
public void DiscardSamples() { }
private IList<MemoryDomain> memoryDomains;
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }

View File

@ -3,5 +3,6 @@
public interface ISoundProvider
{
void GetSamples(short[] samples);
void DiscardSamples();
}
}

View File

@ -127,6 +127,7 @@ namespace BizHawk.Emulation.Sound
}
}
public void DiscardSamples() { /*TBD*/ }
public void GetSamples(short[] samples)
{
int elapsedCycles = frameStopTime - frameStartTime;

View File

@ -416,6 +416,7 @@ namespace BizHawk.Emulation.Sound
#endregion
public void DiscardSamples() { /* todo */ }
public void GetSamples(short[] samples)
{
int elapsedCycles = frameStopTime - frameStartTime;

View File

@ -23,6 +23,12 @@ namespace BizHawk.Emulation.Sound
private const int TargetExtraSamples = 882;
private const int MaxExcessSamples = 4096;
public void DiscardSamples()
{
if(BaseSoundProvider != null)
BaseSoundProvider.DiscardSamples();
}
public void GetSamples(short[] samples)
{
int samplesToGenerate = SamplesInOneFrame;

View File

@ -19,12 +19,17 @@ namespace BizHawk.Emulation.Sound
{
buffer.output_samples(samples, samples.Length / 2);
}
public void DiscardSamples()
{
buffer.clear();
}
}
public interface ISynchronizingAudioBuffer
{
void enqueue_samples(short[] buf, int samples_provided);
void enqueue_sample(short left, short right);
void clear();
//returns the number of samples actually supplied, which may not match the number requested
int output_samples(short[] buf, int samples_requested);
@ -69,6 +74,11 @@ namespace BizHawk.Emulation.Sound
//adjustobuf(200,1000)
bool mixqueue_go = false;
public void clear()
{
adjustobuf.clear();
}
public void enqueue_sample(short left, short right)
{
adjustobuf.enqueue(left, right);
@ -122,12 +132,7 @@ namespace BizHawk.Emulation.Sound
{
minLatency = _minLatency;
maxLatency = _maxLatency;
rollingTotalSize = 0;
targetLatency = (maxLatency + minLatency)/2;
rate = 1.0f;
cursor = 0.0f;
curr[0] = curr[1] = 0;
kAverageSize = 80000;
clear();
}
float rate, cursor;
@ -137,6 +142,19 @@ namespace BizHawk.Emulation.Sound
public int size = 0;
short[] curr = new short[2];
public void clear()
{
buffer.Clear();
statsHistory.Clear();
rollingTotalSize = 0;
targetLatency = (maxLatency + minLatency) / 2;
rate = 1.0f;
cursor = 0.0f;
curr[0] = curr[1] = 0;
kAverageSize = 80000;
size = 0;
}
public void enqueue(short left, short right)
{
buffer.Enqueue(left);
@ -244,6 +262,11 @@ namespace BizHawk.Emulation.Sound
return new ssamp((short)lrv,(short)rrv);
}
public void clear()
{
sampleQueue.Clear();
}
static void emit_sample(short[] outbuf, ref int cursor, ssamp sample)
{
outbuf[cursor++] = sample.l;

View File

@ -24,6 +24,12 @@ namespace BizHawk.Emulation.Sound
SoundProviders.Remove(source);
}
public void DiscardSamples()
{
foreach (var soundSource in SoundProviders)
soundSource.DiscardSamples();
}
public void GetSamples(short[] samples)
{
foreach (var soundSource in SoundProviders)

View File

@ -38,6 +38,7 @@ namespace BizHawk.Emulation.Sound
OPLL_writeReg(opll, register, value);
}
public void DiscardSamples() { }
public void GetSamples(short[] samples)
{
for (int i=0; i<samples.Length;)

View File

@ -17,6 +17,7 @@
{
}
public void DiscardSamples() { }
public void GetSamples(short[] samples)
{
// TODO

View File

@ -125,12 +125,19 @@ namespace BizHawk.MultiClient
cmdRom = arg;
}
if (cmdRom != null) //Commandline should always override auto-load
if (cmdRom != null)
{
//Commandline should always override auto-load
LoadRom(cmdRom);
if (Global.Game == null)
{
MessageBox.Show("Failed to load rom specified on commandline");
}
}
else if (Global.Config.AutoLoadMostRecentRom && !Global.Config.RecentRoms.IsEmpty())
LoadRomFromRecent(Global.Config.RecentRoms.GetRecentFileByPosition(0));
if (cmdLoadState != null)
if (cmdLoadState != null && Global.Game != null)
LoadState("QuickSave" + cmdLoadState);
if (Global.Config.AutoLoadRamWatch)

View File

@ -14,6 +14,7 @@ namespace BizHawk.MultiClient
private byte[] SoundBuffer;
private const int BufferSize = 4410 * 2 * 2; // 1/10th of a second, 2 bytes per sample, 2 channels;
//private int SoundBufferPosition; //TODO: use this
bool needDiscard;
private BufferedAsync semisync = new BufferedAsync();
@ -48,6 +49,8 @@ namespace BizHawk.MultiClient
if(IsPlaying)
return;
needDiscard = true;
DSoundBuffer.Write(SoundBuffer, 0, LockFlags.EntireBuffer);
DSoundBuffer.CurrentPlayPosition = 0;
@ -112,7 +115,10 @@ namespace BizHawk.MultiClient
public void UpdateSound(ISoundProvider soundProvider)
{
if (Global.Config.SoundEnabled == false || disposed)
return;
{
soundProvider.DiscardSamples();
return;
}
int samplesNeeded = SNDDXGetAudioSpace()*2;
if (samplesNeeded == 0)
@ -121,11 +127,12 @@ namespace BizHawk.MultiClient
short[] samples = new short[samplesNeeded];
//Console.WriteLine(samplesNeeded/2);
if (soundProvider != null && Muted == false)
{
semisync.BaseSoundProvider = soundProvider;
semisync.GetSamples(samples);
}
if (soundProvider != null && Muted == false)
{
semisync.BaseSoundProvider = soundProvider;
semisync.GetSamples(samples);
}
else soundProvider.DiscardSamples();
int cursor = soundoffset;
for (int i = 0; i < samples.Length; i++)

View File

@ -364,6 +364,10 @@ namespace M6502
w.WriteLine(" TriggerException(ExceptionType.NMI);");
w.WriteLine(" NMI = false;");
w.WriteLine(" }");
w.WriteLine(" if (IRQ && !FlagI)");
w.WriteLine(" {");
w.WriteLine(" TriggerException(ExceptionType.IRQ);");
w.WriteLine(" }");
w.WriteLine("");
w.WriteLine(" if(debug) Console.WriteLine(State());");