NESHawk: Optimizations and Audio changes
This commit is contained in:
parent
ee45e3114e
commit
469fc4836f
|
@ -74,7 +74,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778
|
4, 7, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public sealed class PulseUnit
|
public sealed class PulseUnit
|
||||||
{
|
{
|
||||||
public PulseUnit(APU apu, int unit) { this.unit = unit; this.apu = apu; }
|
public PulseUnit(APU apu, int unit) { this.unit = unit; this.apu = apu; }
|
||||||
|
@ -968,6 +967,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
ser.Sync("sequencer_irq_flag", ref sequencer_irq_flag);
|
ser.Sync("sequencer_irq_flag", ref sequencer_irq_flag);
|
||||||
ser.Sync("len_clock_active", ref len_clock_active);
|
ser.Sync("len_clock_active", ref len_clock_active);
|
||||||
|
|
||||||
|
ser.Sync("oldmix", ref oldmix);
|
||||||
|
ser.Sync("cart_sound", ref cart_sound);
|
||||||
|
ser.Sync("old_cart_sound", ref old_cart_sound);
|
||||||
|
|
||||||
pulse[0].SyncState(ser);
|
pulse[0].SyncState(ser);
|
||||||
pulse[1].SyncState(ser);
|
pulse[1].SyncState(ser);
|
||||||
triangle.SyncState(ser);
|
triangle.SyncState(ser);
|
||||||
|
@ -1259,127 +1262,118 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
int pending_length_change;
|
int pending_length_change;
|
||||||
|
|
||||||
public void RunOne(bool read)
|
public void RunOneFirst()
|
||||||
{
|
{
|
||||||
if (read)
|
|
||||||
{
|
|
||||||
pulse[0].Run();
|
|
||||||
pulse[1].Run();
|
|
||||||
triangle.Run();
|
|
||||||
noise.Run();
|
|
||||||
dmc.Run();
|
|
||||||
|
|
||||||
pulse[0].len_halt = false;
|
pulse[0].Run();
|
||||||
pulse[1].len_halt = false;
|
pulse[1].Run();
|
||||||
noise.len_halt = false;
|
triangle.Run();
|
||||||
|
noise.Run();
|
||||||
|
dmc.Run();
|
||||||
|
|
||||||
}
|
pulse[0].len_halt = false;
|
||||||
else
|
pulse[1].len_halt = false;
|
||||||
{
|
noise.len_halt = false;
|
||||||
if (pending_length_change>0)
|
|
||||||
{
|
|
||||||
pending_length_change--;
|
|
||||||
if (pending_length_change==0)
|
|
||||||
{
|
|
||||||
dmc.sample_length--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EmitSample();
|
|
||||||
|
|
||||||
// we need to predict if there will be a length clock here, because the sequencer ticks last, but the
|
|
||||||
// timer reload shouldn't happen if length clock and write happen simultaneously
|
|
||||||
// I'm not sure if we can avoid this by simply processing the sequencer first
|
|
||||||
// but at the moment that would break everything, so this is good enough for now
|
|
||||||
if (sequencer_counter == (sequencer_lut[0][1] - 1) ||
|
|
||||||
(sequencer_counter == sequencer_lut[0][3] - 2 && sequencer_mode==0) ||
|
|
||||||
(sequencer_counter == sequencer_lut[1][4] - 2 && sequencer_mode == 1))
|
|
||||||
{
|
|
||||||
len_clock_active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle writes
|
|
||||||
// notes: this set up is a bit convoluded at the moment, mainly because APU behaviour is not entirely understood
|
|
||||||
// in partiuclar, there are several clock pulses affecting the APU, and when new written are latched is not known in detail
|
|
||||||
// the current code simply matches known behaviour
|
|
||||||
if (pending_reg != -1)
|
|
||||||
{
|
|
||||||
if (pending_reg == 0x4015 || pending_reg == 0x4015 || pending_reg == 0x4003 || pending_reg==0x4007)
|
|
||||||
{
|
|
||||||
_WriteReg(pending_reg, pending_val);
|
|
||||||
pending_reg = -1;
|
|
||||||
}
|
|
||||||
else if (dmc.timer%2==0)
|
|
||||||
{
|
|
||||||
_WriteReg(pending_reg, pending_val);
|
|
||||||
pending_reg = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
len_clock_active = false;
|
|
||||||
|
|
||||||
sequencer_tick();
|
|
||||||
sequencer_write_tick(seq_val);
|
|
||||||
doing_tick_quarter = false;
|
|
||||||
|
|
||||||
if (sequencer_irq_assert>0) {
|
|
||||||
sequencer_irq_assert--;
|
|
||||||
if (sequencer_irq_assert==0)
|
|
||||||
{
|
|
||||||
sequencer_irq = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncIRQ();
|
|
||||||
nes._irq_apu = irq_pending;
|
|
||||||
|
|
||||||
// since the units run concurrently, the APU frame sequencer is ran last because
|
|
||||||
// it can change the ouput values of the pulse/triangle channels
|
|
||||||
// we want the changes to affect it on the *next* cycle.
|
|
||||||
|
|
||||||
if (sequencer_irq_flag == false)
|
|
||||||
sequencer_irq = false;
|
|
||||||
|
|
||||||
if (DebugCallbackDivider != 0)
|
|
||||||
{
|
|
||||||
if (DebugCallbackTimer == 0)
|
|
||||||
{
|
|
||||||
if (DebugCallback != null)
|
|
||||||
DebugCallback();
|
|
||||||
DebugCallbackTimer = DebugCallbackDivider;
|
|
||||||
}
|
|
||||||
else DebugCallbackTimer--;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Delta
|
public void RunOneLast()
|
||||||
{
|
{
|
||||||
public uint time;
|
if (pending_length_change > 0)
|
||||||
public int value;
|
|
||||||
public Delta(uint time, int value)
|
|
||||||
{
|
{
|
||||||
this.time = time;
|
pending_length_change--;
|
||||||
this.value = value;
|
if (pending_length_change == 0)
|
||||||
|
{
|
||||||
|
dmc.sample_length--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to predict if there will be a length clock here, because the sequencer ticks last, but the
|
||||||
|
// timer reload shouldn't happen if length clock and write happen simultaneously
|
||||||
|
// I'm not sure if we can avoid this by simply processing the sequencer first
|
||||||
|
// but at the moment that would break everything, so this is good enough for now
|
||||||
|
if (sequencer_counter == (sequencer_lut[0][1] - 1) ||
|
||||||
|
(sequencer_counter == sequencer_lut[0][3] - 2 && sequencer_mode == 0) ||
|
||||||
|
(sequencer_counter == sequencer_lut[1][4] - 2 && sequencer_mode == 1))
|
||||||
|
{
|
||||||
|
len_clock_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle writes
|
||||||
|
// notes: this set up is a bit convoluded at the moment, mainly because APU behaviour is not entirely understood
|
||||||
|
// in partiuclar, there are several clock pulses affecting the APU, and when new written are latched is not known in detail
|
||||||
|
// the current code simply matches known behaviour
|
||||||
|
if (pending_reg != -1)
|
||||||
|
{
|
||||||
|
if (pending_reg == 0x4015 || pending_reg == 0x4015 || pending_reg == 0x4003 || pending_reg == 0x4007)
|
||||||
|
{
|
||||||
|
_WriteReg(pending_reg, pending_val);
|
||||||
|
pending_reg = -1;
|
||||||
|
}
|
||||||
|
else if (dmc.timer % 2 == 0)
|
||||||
|
{
|
||||||
|
_WriteReg(pending_reg, pending_val);
|
||||||
|
pending_reg = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
len_clock_active = false;
|
||||||
|
|
||||||
|
sequencer_tick();
|
||||||
|
sequencer_write_tick(seq_val);
|
||||||
|
doing_tick_quarter = false;
|
||||||
|
|
||||||
|
if (sequencer_irq_assert > 0)
|
||||||
|
{
|
||||||
|
sequencer_irq_assert--;
|
||||||
|
if (sequencer_irq_assert == 0)
|
||||||
|
{
|
||||||
|
sequencer_irq = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncIRQ();
|
||||||
|
nes._irq_apu = irq_pending;
|
||||||
|
|
||||||
|
// since the units run concurrently, the APU frame sequencer is ran last because
|
||||||
|
// it can change the ouput values of the pulse/triangle channels
|
||||||
|
// we want the changes to affect it on the *next* cycle.
|
||||||
|
|
||||||
|
if (sequencer_irq_flag == false)
|
||||||
|
sequencer_irq = false;
|
||||||
|
|
||||||
|
if (DebugCallbackDivider != 0)
|
||||||
|
{
|
||||||
|
if (DebugCallbackTimer == 0)
|
||||||
|
{
|
||||||
|
if (DebugCallback != null)
|
||||||
|
DebugCallback();
|
||||||
|
DebugCallbackTimer = DebugCallbackDivider;
|
||||||
|
}
|
||||||
|
else DebugCallbackTimer--;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Delta> dlist = new List<Delta>();
|
|
||||||
|
|
||||||
/// <summary>only call in board.ClockCPU()</summary>
|
/// <summary>only call in board.ClockCPU()</summary>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
public void ExternalQueue(int value)
|
public void ExternalQueue(int value)
|
||||||
{
|
{
|
||||||
// sampleclock is incremented right before board.ClockCPU()
|
cart_sound = value + old_cart_sound;
|
||||||
dlist.Add(new Delta(sampleclock - 1, value));
|
|
||||||
|
if (cart_sound != old_cart_sound)
|
||||||
|
{
|
||||||
|
recalculate = true;
|
||||||
|
old_cart_sound = cart_sound;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint sampleclock = 0;
|
public uint sampleclock = 0;
|
||||||
|
|
||||||
int oldmix = 0;
|
int oldmix = 0;
|
||||||
|
int cart_sound = 0;
|
||||||
|
int old_cart_sound = 0;
|
||||||
|
|
||||||
void EmitSample()
|
public int EmitSample()
|
||||||
{
|
{
|
||||||
if (recalculate)
|
if (recalculate)
|
||||||
{
|
{
|
||||||
|
@ -1391,16 +1385,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
int s_noise = noise.sample;
|
int s_noise = noise.sample;
|
||||||
int s_dmc = dmc.sample;
|
int s_dmc = dmc.sample;
|
||||||
|
|
||||||
// int s_ext = 0; //gamepak
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (!EnableSquare1) s_pulse0 = 0;
|
|
||||||
if (!EnableSquare2) s_pulse1 = 0;
|
|
||||||
if (!EnableTriangle) s_tri = 0;
|
|
||||||
if (!EnableNoise) s_noise = 0;
|
|
||||||
if (!EnableDMC) s_dmc = 0;
|
|
||||||
*/
|
|
||||||
|
|
||||||
// more properly correct
|
// more properly correct
|
||||||
float pulse_out, tnd_out;
|
float pulse_out, tnd_out;
|
||||||
if (s_pulse0 == 0 && s_pulse1 == 0)
|
if (s_pulse0 == 0 && s_pulse1 == 0)
|
||||||
|
@ -1412,13 +1396,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
float output = pulse_out + tnd_out;
|
float output = pulse_out + tnd_out;
|
||||||
// output = output * 2 - 1;
|
// output = output * 2 - 1;
|
||||||
// this needs to leave enough headroom for straying DC bias due to the DMC unit getting stuck outputs. smb3 is bad about that.
|
// this needs to leave enough headroom for straying DC bias due to the DMC unit getting stuck outputs. smb3 is bad about that.
|
||||||
int mix = (int)(20000 * output * (1 + m_vol/5));
|
int mix = (int)(20000 * output * (1 + m_vol/5)) + cart_sound;
|
||||||
|
|
||||||
dlist.Add(new Delta(sampleclock, mix - oldmix));
|
|
||||||
oldmix = mix;
|
oldmix = mix;
|
||||||
|
|
||||||
|
return mix;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleclock++;
|
return oldmix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ using BizHawk.Emulation.Cores.Components.M6502;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
{
|
{
|
||||||
public partial class NES : IEmulator, ICycleTiming
|
public partial class NES : IEmulator, ISoundProvider, ICycleTiming
|
||||||
{
|
{
|
||||||
//hardware/state
|
//hardware/state
|
||||||
public MOS6502X<CpuLink> cpu;
|
public MOS6502X<CpuLink> cpu;
|
||||||
|
@ -72,92 +72,65 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
return Board;
|
return Board;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Audio
|
||||||
|
|
||||||
|
BlipBuffer blip = new BlipBuffer(4096);
|
||||||
|
const int blipbuffsize = 4096;
|
||||||
|
|
||||||
|
public int old_s = 0;
|
||||||
|
|
||||||
|
public bool CanProvideAsync { get { return false; } }
|
||||||
|
|
||||||
|
public void SetSyncMode(SyncSoundMode mode)
|
||||||
|
{
|
||||||
|
if (mode != SyncSoundMode.Sync)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Only sync mode is supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetSamplesAsync(short[] samples)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException("Async not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyncSoundMode SyncMode
|
||||||
|
{
|
||||||
|
get { return SyncSoundMode.Sync; }
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (magicSoundProvider != null)
|
if (blip != null)
|
||||||
magicSoundProvider.Dispose();
|
{
|
||||||
magicSoundProvider = null;
|
blip.Dispose();
|
||||||
|
blip = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MagicSoundProvider : ISoundProvider, IDisposable
|
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||||
{
|
{
|
||||||
BlipBuffer blip;
|
blip.EndFrame(apu.sampleclock);
|
||||||
NES nes;
|
apu.sampleclock = 0;
|
||||||
|
|
||||||
const int blipbuffsize = 4096;
|
nsamp = blip.SamplesAvailable();
|
||||||
|
samples = new short[nsamp * 2];
|
||||||
|
|
||||||
public MagicSoundProvider(NES nes, uint infreq)
|
blip.ReadSamples(samples, nsamp, true);
|
||||||
{
|
// duplicate to stereo
|
||||||
this.nes = nes;
|
for (int i = 0; i < nsamp * 2; i += 2)
|
||||||
|
samples[i + 1] = samples[i];
|
||||||
|
|
||||||
blip = new BlipBuffer(blipbuffsize);
|
Board.ApplyCustomAudio(samples);
|
||||||
blip.SetRates(infreq, 44100);
|
|
||||||
|
|
||||||
//var actualMetaspu = new Sound.MetaspuSoundProvider(Sound.ESynchMethod.ESynchMethod_V);
|
|
||||||
//1.789773mhz NTSC
|
|
||||||
//resampler = new Sound.Utilities.SpeexResampler(2, infreq, 44100 * APU.DECIMATIONFACTOR, infreq, 44100, actualMetaspu.buffer.enqueue_samples);
|
|
||||||
//output = new Sound.Utilities.DCFilter(actualMetaspu);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanProvideAsync
|
|
||||||
{
|
|
||||||
get { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public SyncSoundMode SyncMode
|
|
||||||
{
|
|
||||||
get { return SyncSoundMode.Sync; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSyncMode(SyncSoundMode mode)
|
|
||||||
{
|
|
||||||
if (mode != SyncSoundMode.Sync)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException("Only sync mode is supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetSamplesAsync(short[] samples)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException("Async not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
|
||||||
{
|
|
||||||
//Console.WriteLine("ASync: {0}", nes.apu.dlist.Count);
|
|
||||||
foreach (var d in nes.apu.dlist)
|
|
||||||
blip.AddDelta(d.time, d.value);
|
|
||||||
nes.apu.dlist.Clear();
|
|
||||||
blip.EndFrame(nes.apu.sampleclock);
|
|
||||||
nes.apu.sampleclock = 0;
|
|
||||||
|
|
||||||
nsamp = blip.SamplesAvailable();
|
|
||||||
samples = new short[nsamp * 2];
|
|
||||||
|
|
||||||
blip.ReadSamples(samples, nsamp, true);
|
|
||||||
// duplicate to stereo
|
|
||||||
for (int i = 0; i < nsamp * 2; i += 2)
|
|
||||||
samples[i + 1] = samples[i];
|
|
||||||
|
|
||||||
nes.Board.ApplyCustomAudio(samples);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DiscardSamples()
|
|
||||||
{
|
|
||||||
nes.apu.dlist.Clear();
|
|
||||||
nes.apu.sampleclock = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (blip != null)
|
|
||||||
{
|
|
||||||
blip.Dispose();
|
|
||||||
blip = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public MagicSoundProvider magicSoundProvider;
|
|
||||||
|
public void DiscardSamples()
|
||||||
|
{
|
||||||
|
blip.Clear();
|
||||||
|
apu.sampleclock = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public void HardReset()
|
public void HardReset()
|
||||||
{
|
{
|
||||||
|
@ -243,8 +216,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
default:
|
default:
|
||||||
throw new Exception("Unknown displaytype!");
|
throw new Exception("Unknown displaytype!");
|
||||||
}
|
}
|
||||||
if (magicSoundProvider == null)
|
|
||||||
magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate);
|
blip.SetRates((uint)cpuclockrate, 44100);
|
||||||
|
|
||||||
BoardSystemHardReset();
|
BoardSystemHardReset();
|
||||||
|
|
||||||
|
@ -563,7 +536,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// dmc dma end
|
// dmc dma end
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
apu.RunOne(true);
|
apu.RunOneFirst();
|
||||||
|
|
||||||
if (cpu.RDY && !IRQ_delay)
|
if (cpu.RDY && !IRQ_delay)
|
||||||
{
|
{
|
||||||
|
@ -576,7 +549,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.ExecuteOne();
|
cpu.ExecuteOne();
|
||||||
apu.RunOne(false);
|
Board.ClockCPU();
|
||||||
|
|
||||||
|
int s = apu.EmitSample();
|
||||||
|
|
||||||
|
if (s != old_s)
|
||||||
|
{
|
||||||
|
blip.AddDelta(apu.sampleclock, s - old_s);
|
||||||
|
old_s = s;
|
||||||
|
}
|
||||||
|
apu.sampleclock++;
|
||||||
|
|
||||||
|
apu.RunOneLast();
|
||||||
|
|
||||||
if (ppu.double_2007_read > 0)
|
if (ppu.double_2007_read > 0)
|
||||||
ppu.double_2007_read--;
|
ppu.double_2007_read--;
|
||||||
|
@ -592,8 +576,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
cpu.RDY = true;
|
cpu.RDY = true;
|
||||||
IRQ_delay = true;
|
IRQ_delay = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Board.ClockCPU();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte ReadReg(int addr)
|
public byte ReadReg(int addr)
|
||||||
|
|
|
@ -61,6 +61,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
ser.Sync("sprdma_countdown", ref sprdma_countdown);
|
ser.Sync("sprdma_countdown", ref sprdma_countdown);
|
||||||
ser.Sync("cpu_deadcounter", ref cpu_deadcounter);
|
ser.Sync("cpu_deadcounter", ref cpu_deadcounter);
|
||||||
|
|
||||||
|
ser.Sync("old_s", ref old_s);
|
||||||
|
|
||||||
//oam related
|
//oam related
|
||||||
ser.Sync("Oam_Dma_Index", ref oam_dma_index);
|
ser.Sync("Oam_Dma_Index", ref oam_dma_index);
|
||||||
ser.Sync("Oam_Dma_Exec", ref oam_dma_exec);
|
ser.Sync("Oam_Dma_Exec", ref oam_dma_exec);
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
Tracer = new TraceBuffer { Header = cpu.TraceHeader };
|
Tracer = new TraceBuffer { Header = cpu.TraceHeader };
|
||||||
ser.Register<ITraceable>(Tracer);
|
ser.Register<ITraceable>(Tracer);
|
||||||
ser.Register<IVideoProvider>(videoProvider);
|
ser.Register<IVideoProvider>(videoProvider);
|
||||||
ser.Register<ISoundProvider>(magicSoundProvider);
|
ser.Register<ISoundProvider>(this);
|
||||||
|
|
||||||
if (Board is BANDAI_FCG_1)
|
if (Board is BANDAI_FCG_1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -298,7 +298,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
ser.Sync("xstart", ref xstart);
|
ser.Sync("xstart", ref xstart);
|
||||||
ser.Sync("rasterpos", ref rasterpos);
|
ser.Sync("rasterpos", ref rasterpos);
|
||||||
ser.Sync("renderspritenow", ref renderspritenow);
|
ser.Sync("renderspritenow", ref renderspritenow);
|
||||||
ser.Sync("renderbgnow", ref renderbgnow);
|
|
||||||
ser.Sync("s", ref s);
|
ser.Sync("s", ref s);
|
||||||
ser.Sync("ppu_aux_index", ref ppu_aux_index);
|
ser.Sync("ppu_aux_index", ref ppu_aux_index);
|
||||||
ser.Sync("junksprite", ref junksprite);
|
ser.Sync("junksprite", ref junksprite);
|
||||||
|
@ -350,10 +349,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
void runppu()
|
void runppu()
|
||||||
{
|
{
|
||||||
//run one ppu cycle at a time so we can interact with the ppu and clockPPU at high granularity
|
//run one ppu cycle at a time so we can interact with the ppu and clockPPU at high granularity
|
||||||
|
if (install_2006 > 0)
|
||||||
|
|
||||||
if (install_2006>0)
|
|
||||||
{
|
{
|
||||||
install_2006--;
|
install_2006--;
|
||||||
if (install_2006==0)
|
if (install_2006==0)
|
||||||
|
@ -386,7 +383,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
ppur.status.cycle++;
|
ppur.status.cycle++;
|
||||||
is_even_cycle = !is_even_cycle;
|
is_even_cycle = !is_even_cycle;
|
||||||
|
|
||||||
if (PPUON && ppur.status.cycle >= 257 && ppur.status.cycle <= 320 && ppur.status.sl <= 240)
|
if (ppur.status.cycle >= 257 && ppur.status.cycle <= 320 && ppur.status.sl <= 240 && PPUON)
|
||||||
{
|
{
|
||||||
reg_2003 = 0;
|
reg_2003 = 0;
|
||||||
}
|
}
|
||||||
|
@ -417,18 +414,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Reg2002_vblank_active_pending)
|
|
||||||
{
|
|
||||||
Reg2002_vblank_active = 1;
|
|
||||||
Reg2002_vblank_active_pending = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Reg2002_vblank_clear_pending)
|
|
||||||
{
|
|
||||||
Reg2002_vblank_active = 0;
|
|
||||||
Reg2002_vblank_clear_pending = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasClockPPU)
|
if (HasClockPPU)
|
||||||
{
|
{
|
||||||
nes.Board.ClockPPU();
|
nes.Board.ClockPPU();
|
||||||
|
|
|
@ -78,13 +78,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
pixelcolor_latch_1 = pixelcolor;
|
pixelcolor_latch_1 = pixelcolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Read_bgdata(int cycle, ref BGDataRecord bgdata)
|
void Read_bgdata(int cycle, int i)
|
||||||
{
|
{
|
||||||
switch (cycle)
|
switch (cycle)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
ppu_addr_temp = ppur.get_ntread();
|
ppu_addr_temp = ppur.get_ntread();
|
||||||
bgdata.nt = ppubus_read(ppu_addr_temp, true, true);
|
bgdata[i].nt = ppubus_read(ppu_addr_temp, true, true);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
break;
|
break;
|
||||||
|
@ -98,20 +98,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
if ((ppur.ht & 2) != 0) at >>= 2;
|
if ((ppur.ht & 2) != 0) at >>= 2;
|
||||||
at &= 0x03;
|
at &= 0x03;
|
||||||
at <<= 2;
|
at <<= 2;
|
||||||
bgdata.at = at;
|
bgdata[i].at = at;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
ppu_addr_temp = ppur.get_ptread(bgdata.nt);
|
ppu_addr_temp = ppur.get_ptread(bgdata[i].nt);
|
||||||
bgdata.pt_0 = ppubus_read(ppu_addr_temp, true, true);
|
bgdata[i].pt_0 = ppubus_read(ppu_addr_temp, true, true);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
ppu_addr_temp |= 8;
|
ppu_addr_temp |= 8;
|
||||||
bgdata.pt_1 = ppubus_read(ppu_addr_temp, true, true);
|
bgdata[i].pt_1 = ppubus_read(ppu_addr_temp, true, true);
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
break;
|
break;
|
||||||
|
@ -134,7 +134,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
int xstart;
|
int xstart;
|
||||||
int rasterpos;
|
int rasterpos;
|
||||||
bool renderspritenow;
|
bool renderspritenow;
|
||||||
bool renderbgnow;
|
|
||||||
int s;
|
int s;
|
||||||
int ppu_aux_index;
|
int ppu_aux_index;
|
||||||
bool junksprite;
|
bool junksprite;
|
||||||
|
@ -149,8 +148,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
ppur.status.cycle = 0;
|
ppur.status.cycle = 0;
|
||||||
|
|
||||||
// These things happen at the start of every frame
|
// These things happen at the start of every frame
|
||||||
//Reg2002_vblank_active = true;
|
|
||||||
//Reg2002_vblank_active_pending = true;
|
|
||||||
ppuphase = PPU_PHASE_VBL;
|
ppuphase = PPU_PHASE_VBL;
|
||||||
bgdata = new BGDataRecord[34];
|
bgdata = new BGDataRecord[34];
|
||||||
}
|
}
|
||||||
|
@ -188,6 +185,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
if (ppur.status.cycle == 341)
|
if (ppur.status.cycle == 341)
|
||||||
{
|
{
|
||||||
|
if (Reg2002_vblank_clear_pending)
|
||||||
|
{
|
||||||
|
Reg2002_vblank_active = 0;
|
||||||
|
Reg2002_vblank_clear_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
ppur.status.cycle = 0;
|
ppur.status.cycle = 0;
|
||||||
ppur.status.sl++;
|
ppur.status.sl++;
|
||||||
if (ppur.status.sl == 241 + preNMIlines + postNMIlines)
|
if (ppur.status.sl == 241 + preNMIlines + postNMIlines)
|
||||||
|
@ -201,100 +204,97 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
public void TickPPU_active()
|
public void TickPPU_active()
|
||||||
{
|
{
|
||||||
if (ppur.status.cycle == 0)
|
|
||||||
{
|
|
||||||
ppur.status.cycle = 0;
|
|
||||||
|
|
||||||
spr_true_count = 0;
|
|
||||||
soam_index = 0;
|
|
||||||
soam_m_index = 0;
|
|
||||||
oam_index_aux = 0;
|
|
||||||
oam_index = 0;
|
|
||||||
is_even_cycle = true;
|
|
||||||
sprite_eval_write = true;
|
|
||||||
sprite_zero_go = sprite_zero_in_range;
|
|
||||||
|
|
||||||
sprite_zero_in_range = false;
|
|
||||||
|
|
||||||
yp = ppur.status.sl - 1;
|
|
||||||
ppuphase = PPU_PHASE_BG;
|
|
||||||
|
|
||||||
// "If PPUADDR is not less then 8 when rendering starts, the first 8 bytes in OAM are written to from
|
|
||||||
// the current location of PPUADDR"
|
|
||||||
if (ppur.status.sl == 0 && PPUON && reg_2003 >= 8 && region == Region.NTSC)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
OAM[i] = OAM[(reg_2003 & 0xF8) + i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NTViewCallback != null && yp == NTViewCallback.Scanline) NTViewCallback.Callback();
|
|
||||||
if (PPUViewCallback != null && yp == PPUViewCallback.Scanline) PPUViewCallback.Callback();
|
|
||||||
|
|
||||||
// set up intial values to use later
|
|
||||||
yp_shift = yp << 8;
|
|
||||||
xt = 0;
|
|
||||||
xp = 0;
|
|
||||||
|
|
||||||
sprite_eval_cycle = 0;
|
|
||||||
|
|
||||||
xstart = xt << 3;
|
|
||||||
target = yp_shift + xstart;
|
|
||||||
rasterpos = xstart;
|
|
||||||
|
|
||||||
spriteHeight = reg_2000.obj_size_16 ? 16 : 8;
|
|
||||||
|
|
||||||
//check all the conditions that can cause things to render in these 8px
|
|
||||||
renderspritenow = show_obj_new && (xt > 0 || reg_2001.show_obj_leftmost);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ppur.status.cycle < 256)
|
if (ppur.status.cycle < 256)
|
||||||
{
|
{
|
||||||
|
if (ppur.status.cycle == 0)
|
||||||
|
{
|
||||||
|
ppur.status.cycle = 0;
|
||||||
|
|
||||||
|
spr_true_count = 0;
|
||||||
|
soam_index = 0;
|
||||||
|
soam_m_index = 0;
|
||||||
|
oam_index_aux = 0;
|
||||||
|
oam_index = 0;
|
||||||
|
is_even_cycle = true;
|
||||||
|
sprite_eval_write = true;
|
||||||
|
sprite_zero_go = sprite_zero_in_range;
|
||||||
|
|
||||||
|
sprite_zero_in_range = false;
|
||||||
|
|
||||||
|
yp = ppur.status.sl - 1;
|
||||||
|
ppuphase = PPU_PHASE_BG;
|
||||||
|
|
||||||
|
// "If PPUADDR is not less then 8 when rendering starts, the first 8 bytes in OAM are written to from
|
||||||
|
// the current location of PPUADDR"
|
||||||
|
if (ppur.status.sl == 0 && PPUON && reg_2003 >= 8 && region == Region.NTSC)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
OAM[i] = OAM[(reg_2003 & 0xF8) + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NTViewCallback != null && yp == NTViewCallback.Scanline) NTViewCallback.Callback();
|
||||||
|
if (PPUViewCallback != null && yp == PPUViewCallback.Scanline) PPUViewCallback.Callback();
|
||||||
|
|
||||||
|
// set up intial values to use later
|
||||||
|
yp_shift = yp << 8;
|
||||||
|
xt = 0;
|
||||||
|
xp = 0;
|
||||||
|
|
||||||
|
sprite_eval_cycle = 0;
|
||||||
|
|
||||||
|
xstart = xt << 3;
|
||||||
|
target = yp_shift + xstart;
|
||||||
|
rasterpos = xstart;
|
||||||
|
|
||||||
|
spriteHeight = reg_2000.obj_size_16 ? 16 : 8;
|
||||||
|
|
||||||
|
//check all the conditions that can cause things to render in these 8px
|
||||||
|
renderspritenow = show_obj_new && (xt > 0 || reg_2001.show_obj_leftmost);
|
||||||
|
}
|
||||||
|
|
||||||
if (ppur.status.sl != 0)
|
if (ppur.status.sl != 0)
|
||||||
{
|
{
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// Sprite Evaluation Start
|
// Sprite Evaluation Start
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
|
|
||||||
if (sprite_eval_cycle <= 63 && !is_even_cycle)
|
if (sprite_eval_cycle < 64)
|
||||||
{
|
{
|
||||||
// the first 64 cycles of each scanline are used to initialize sceondary OAM
|
// the first 64 cycles of each scanline are used to initialize sceondary OAM
|
||||||
// the actual effect setting a flag that always returns 0xFF from a OAM read
|
// the actual effect setting a flag that always returns 0xFF from a OAM read
|
||||||
// this is a bit of a shortcut to save some instructions
|
// this is a bit of a shortcut to save some instructions
|
||||||
// data is read from OAM as normal but never used
|
// data is read from OAM as normal but never used
|
||||||
soam[soam_index] = 0xFF;
|
if (!is_even_cycle)
|
||||||
soam_index++;
|
{
|
||||||
|
soam[soam_index] = 0xFF;
|
||||||
|
soam_index++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sprite_eval_cycle == 64)
|
|
||||||
{
|
|
||||||
soam_index = 0;
|
|
||||||
oam_index = reg_2003;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, scan through OAM and test if sprites are in range
|
// otherwise, scan through OAM and test if sprites are in range
|
||||||
// if they are, they get copied to the secondary OAM
|
// if they are, they get copied to the secondary OAM
|
||||||
if (sprite_eval_cycle >= 64)
|
else
|
||||||
{
|
{
|
||||||
|
if (sprite_eval_cycle == 64)
|
||||||
|
{
|
||||||
|
soam_index = 0;
|
||||||
|
oam_index = reg_2003;
|
||||||
|
}
|
||||||
|
|
||||||
if (oam_index >= 256)
|
if (oam_index >= 256)
|
||||||
{
|
{
|
||||||
oam_index = 0;
|
oam_index = 0;
|
||||||
sprite_eval_write = false;
|
sprite_eval_write = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_even_cycle && oam_index < 256)
|
if (is_even_cycle)
|
||||||
{
|
{
|
||||||
if ((oam_index + soam_m_index) < 256)
|
if ((oam_index + soam_m_index) < 256)
|
||||||
read_value = OAM[oam_index + soam_m_index];
|
read_value = OAM[oam_index + soam_m_index];
|
||||||
else
|
else
|
||||||
read_value = OAM[oam_index + soam_m_index - 256];
|
read_value = OAM[oam_index + soam_m_index - 256];
|
||||||
}
|
}
|
||||||
else if (!sprite_eval_write)
|
|
||||||
{
|
|
||||||
// if we don't write sprites anymore, just scan through the oam
|
|
||||||
read_value = soam[0];
|
|
||||||
oam_index += 4;
|
|
||||||
}
|
|
||||||
else if (sprite_eval_write)
|
else if (sprite_eval_write)
|
||||||
{
|
{
|
||||||
//look for sprites
|
//look for sprites
|
||||||
|
@ -341,7 +341,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
oam_index += 4;
|
oam_index += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (soam_index >= 8)
|
else
|
||||||
{
|
{
|
||||||
if (yp >= read_value && yp < read_value + spriteHeight && PPUON)
|
if (yp >= read_value && yp < read_value + spriteHeight && PPUON)
|
||||||
{
|
{
|
||||||
|
@ -380,30 +380,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
read_value = soam[0]; //writes change to reads
|
read_value = soam[0]; //writes change to reads
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if we don't write sprites anymore, just scan through the oam
|
||||||
|
read_value = soam[0];
|
||||||
|
oam_index += 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// Sprite Evaluation End
|
// Sprite Evaluation End
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
|
|
||||||
|
int pixel = 0, pixelcolor = PALRAM[pixel];
|
||||||
|
|
||||||
//process the current clock's worth of bg data fetching
|
//process the current clock's worth of bg data fetching
|
||||||
//this needs to be split into 8 pieces or else exact sprite 0 hitting wont work
|
//this needs to be split into 8 pieces or else exact sprite 0 hitting wont work
|
||||||
// due to the cpu not running while the sprite renders below
|
// due to the cpu not running while the sprite renders below
|
||||||
if (PPUON) { Read_bgdata(xp, ref bgdata[xt + 2]); }
|
if (PPUON) { Read_bgdata(xp, xt + 2); }
|
||||||
|
|
||||||
renderbgnow = show_bg_new && (xt > 0 || reg_2001.show_bg_leftmost);
|
|
||||||
//bg pos is different from raster pos due to its offsetability.
|
|
||||||
//so adjust for that here
|
|
||||||
int bgpos = rasterpos + ppur.fh;
|
|
||||||
int bgpx = bgpos & 7;
|
|
||||||
int bgtile = bgpos >> 3;
|
|
||||||
|
|
||||||
int pixel = 0, pixelcolor = PALRAM[pixel];
|
|
||||||
|
|
||||||
//according to qeed's doc, use palette 0 or $2006's value if it is & 0x3Fxx
|
//according to qeed's doc, use palette 0 or $2006's value if it is & 0x3Fxx
|
||||||
//at one point I commented this out to fix bottom-left garbage in DW4. but it's needed for full_nes_palette.
|
//at one point I commented this out to fix bottom-left garbage in DW4. but it's needed for full_nes_palette.
|
||||||
//solution is to only run when PPU is actually OFF (left-suppression doesnt count)
|
//solution is to only run when PPU is actually OFF (left-suppression doesnt count)
|
||||||
if (!PPUON)
|
else
|
||||||
{
|
{
|
||||||
// if there's anything wrong with how we're doing this, someone please chime in
|
// if there's anything wrong with how we're doing this, someone please chime in
|
||||||
int addr = ppur.get_2007access();
|
int addr = ppur.get_2007access();
|
||||||
|
@ -412,15 +410,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
pixel = addr & 0x1F;
|
pixel = addr & 0x1F;
|
||||||
}
|
}
|
||||||
pixelcolor = PALRAM[pixel];
|
pixelcolor = PALRAM[pixel];
|
||||||
pixelcolor |= 0x8000; //whats this? i think its a flag to indicate a hidden background to be used by the canvas filling logic later
|
pixelcolor |= 0x8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
//generate the BG data
|
//generate the BG data
|
||||||
if (renderbgnow)
|
if (show_bg_new && (xt > 0 || reg_2001.show_bg_leftmost))
|
||||||
{
|
{
|
||||||
|
int bgtile = (rasterpos + ppur.fh) >> 3;
|
||||||
byte pt_0 = bgdata[bgtile].pt_0;
|
byte pt_0 = bgdata[bgtile].pt_0;
|
||||||
byte pt_1 = bgdata[bgtile].pt_1;
|
byte pt_1 = bgdata[bgtile].pt_1;
|
||||||
int sel = 7 - bgpx;
|
int sel = 7 - (rasterpos + ppur.fh) & 7;
|
||||||
pixel = ((pt_0 >> sel) & 1) | (((pt_1 >> sel) & 1) << 1);
|
pixel = ((pt_0 >> sel) & 1) | (((pt_1 >> sel) & 1) << 1);
|
||||||
if (pixel != 0)
|
if (pixel != 0)
|
||||||
pixel |= bgdata[bgtile].at;
|
pixel |= bgdata[bgtile].at;
|
||||||
|
@ -428,7 +427,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nes.Settings.DispBackground)
|
if (!nes.Settings.DispBackground)
|
||||||
pixelcolor = 0x8000; //whats this? i think its a flag to indicate a hidden background to be used by the canvas filling logic later
|
pixelcolor = 0x8000;
|
||||||
|
|
||||||
//check if the pixel has a sprite in it
|
//check if the pixel has a sprite in it
|
||||||
if (sl_sprites[256 + xt * 8 + xp] != 0 && renderspritenow)
|
if (sl_sprites[256 + xt * 8 + xp] != 0 && renderspritenow)
|
||||||
|
@ -457,13 +456,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
runppu();
|
runppu();
|
||||||
|
|
||||||
if (PPUON && xp == 6)
|
if (xp == 6 && PPUON)
|
||||||
{
|
{
|
||||||
ppu_was_on = true;
|
ppu_was_on = true;
|
||||||
if (ppur.status.cycle == 255) { race_2006 = true; }
|
if (ppur.status.cycle == 255) { race_2006 = true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PPUON && xp == 7)
|
if (xp == 7 && PPUON)
|
||||||
{
|
{
|
||||||
ppur.increment_hsc();
|
ppur.increment_hsc();
|
||||||
|
|
||||||
|
@ -526,18 +525,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if scanline is the pre-render line, we just read BG data
|
// if scanline is the pre-render line, we just read BG data
|
||||||
Read_bgdata(xp, ref bgdata[xt + 2]);
|
Read_bgdata(xp, xt + 2);
|
||||||
|
|
||||||
runppu();
|
runppu();
|
||||||
|
|
||||||
if (PPUON && xp == 6)
|
if (xp == 6 && PPUON)
|
||||||
{
|
{
|
||||||
ppu_was_on = true;
|
ppu_was_on = true;
|
||||||
if (ppur.status.cycle == 255) { race_2006 = true; }
|
if (ppur.status.cycle == 255) { race_2006 = true; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PPUON && xp == 7)
|
if (xp == 7 && PPUON)
|
||||||
{
|
{
|
||||||
ppur.increment_hsc();
|
ppur.increment_hsc();
|
||||||
|
|
||||||
|
@ -623,7 +622,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
s = 0;
|
s = 0;
|
||||||
ppu_aux_index = 0;
|
ppu_aux_index = 0;
|
||||||
|
|
||||||
junksprite = (!PPUON);
|
junksprite = !PPUON;
|
||||||
|
|
||||||
t_oam[s].oam_y = soam[s * 4];
|
t_oam[s].oam_y = soam[s * 4];
|
||||||
t_oam[s].oam_ind = soam[s * 4 + 1];
|
t_oam[s].oam_ind = soam[s * 4 + 1];
|
||||||
|
@ -662,7 +661,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
runppu();
|
runppu();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (PPUON && ppur.status.sl == 0 && ppur.status.cycle == 305)
|
if (ppur.status.sl == 0 && ppur.status.cycle == 305 && PPUON)
|
||||||
{
|
{
|
||||||
ppur.install_latches();
|
ppur.install_latches();
|
||||||
|
|
||||||
|
@ -670,7 +669,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
runppu();
|
runppu();
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (PPUON && (ppur.status.sl != 0) && ppur.status.cycle == 257)
|
else if ((ppur.status.sl != 0) && ppur.status.cycle == 257 && PPUON)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (target <= 61441 && target > 0 && s == 0)
|
if (target <= 61441 && target > 0 && s == 0)
|
||||||
|
@ -739,15 +738,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
// if the PPU is off, we don't put anything on the bus
|
runppu();
|
||||||
if (junksprite)
|
|
||||||
{
|
|
||||||
runppu();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
runppu();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
// if the PPU is off, we don't put anything on the bus
|
// if the PPU is off, we don't put anything on the bus
|
||||||
|
@ -825,7 +816,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
if (s < 8)
|
if (s < 8)
|
||||||
{
|
{
|
||||||
junksprite = (!PPUON);
|
junksprite = !PPUON;
|
||||||
|
|
||||||
t_oam[s].oam_y = soam[s * 4];
|
t_oam[s].oam_y = soam[s * 4];
|
||||||
t_oam[s].oam_ind = soam[s * 4 + 1];
|
t_oam[s].oam_ind = soam[s * 4 + 1];
|
||||||
|
@ -925,26 +916,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ppur.status.cycle == 320)
|
|
||||||
{
|
|
||||||
ppuphase = PPU_PHASE_BG;
|
|
||||||
xt = 0;
|
|
||||||
xp = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ppur.status.cycle < 336)
|
if (ppur.status.cycle < 336)
|
||||||
{
|
{
|
||||||
|
if (ppur.status.cycle == 320)
|
||||||
|
{
|
||||||
|
ppuphase = PPU_PHASE_BG;
|
||||||
|
xt = 0;
|
||||||
|
xp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// if scanline is the pre-render line, we just read BG data
|
// if scanline is the pre-render line, we just read BG data
|
||||||
Read_bgdata(xp, ref bgdata[xt]);
|
Read_bgdata(xp, xt);
|
||||||
|
|
||||||
runppu();
|
runppu();
|
||||||
|
|
||||||
if (PPUON && xp == 6)
|
if (xp == 6 && PPUON)
|
||||||
{
|
{
|
||||||
ppu_was_on = true;
|
ppu_was_on = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PPUON && xp == 7)
|
if (xp == 7 && PPUON)
|
||||||
{
|
{
|
||||||
if (!race_2006)
|
if (!race_2006)
|
||||||
ppur.increment_hsc();
|
ppur.increment_hsc();
|
||||||
|
@ -1009,6 +1000,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
if (ppur.status.cycle == 341)
|
if (ppur.status.cycle == 341)
|
||||||
{
|
{
|
||||||
|
if (Reg2002_vblank_active_pending)
|
||||||
|
{
|
||||||
|
Reg2002_vblank_active = 1;
|
||||||
|
Reg2002_vblank_active_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
ppur.status.cycle = 0;
|
ppur.status.cycle = 0;
|
||||||
ppur.status.sl++;
|
ppur.status.sl++;
|
||||||
if (ppur.status.sl == 241 + preNMIlines)
|
if (ppur.status.sl == 241 + preNMIlines)
|
||||||
|
@ -1038,6 +1035,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||||
|
|
||||||
if (ppur.status.cycle == 241 * 341 - start_up_offset)
|
if (ppur.status.cycle == 241 * 341 - start_up_offset)
|
||||||
{
|
{
|
||||||
|
if (Reg2002_vblank_active_pending)
|
||||||
|
{
|
||||||
|
Reg2002_vblank_active = 1;
|
||||||
|
Reg2002_vblank_active_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
ppudead--;
|
ppudead--;
|
||||||
|
|
||||||
ppu_init_frame();
|
ppu_init_frame();
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SubNESHawk
|
||||||
game, rom, subnesSettings, subnesSyncSettings);
|
game, rom, subnesSettings, subnesSyncSettings);
|
||||||
|
|
||||||
ser.Register<IVideoProvider>(subnes.videoProvider);
|
ser.Register<IVideoProvider>(subnes.videoProvider);
|
||||||
ser.Register<ISoundProvider>(subnes.magicSoundProvider);
|
ser.Register<ISoundProvider>(subnes);
|
||||||
|
|
||||||
_tracer = new TraceBuffer { Header = "6502: PC, machine code, mnemonic, operands, registers (A, X, Y, P, SP), flags (NVTBDIZCR), CPU Cycle, PPU Cycle" };
|
_tracer = new TraceBuffer { Header = "6502: PC, machine code, mnemonic, operands, registers (A, X, Y, P, SP), flags (NVTBDIZCR), CPU Cycle, PPU Cycle" };
|
||||||
ser.Register<ITraceable>(_tracer);
|
ser.Register<ITraceable>(_tracer);
|
||||||
|
|
Loading…
Reference in New Issue