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
|
||||
};
|
||||
|
||||
|
||||
public sealed class PulseUnit
|
||||
{
|
||||
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("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[1].SyncState(ser);
|
||||
triangle.SyncState(ser);
|
||||
|
@ -1259,127 +1262,118 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
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[1].len_halt = false;
|
||||
noise.len_halt = false;
|
||||
pulse[0].Run();
|
||||
pulse[1].Run();
|
||||
triangle.Run();
|
||||
noise.Run();
|
||||
dmc.Run();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
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--;
|
||||
|
||||
}
|
||||
}
|
||||
pulse[0].len_halt = false;
|
||||
pulse[1].len_halt = false;
|
||||
noise.len_halt = false;
|
||||
}
|
||||
|
||||
public struct Delta
|
||||
public void RunOneLast()
|
||||
{
|
||||
public uint time;
|
||||
public int value;
|
||||
public Delta(uint time, int value)
|
||||
if (pending_length_change > 0)
|
||||
{
|
||||
this.time = time;
|
||||
this.value = value;
|
||||
pending_length_change--;
|
||||
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>
|
||||
/// <param name="value"></param>
|
||||
public void ExternalQueue(int value)
|
||||
{
|
||||
// sampleclock is incremented right before board.ClockCPU()
|
||||
dlist.Add(new Delta(sampleclock - 1, value));
|
||||
cart_sound = value + old_cart_sound;
|
||||
|
||||
if (cart_sound != old_cart_sound)
|
||||
{
|
||||
recalculate = true;
|
||||
old_cart_sound = cart_sound;
|
||||
}
|
||||
}
|
||||
|
||||
public uint sampleclock = 0;
|
||||
|
||||
int oldmix = 0;
|
||||
int cart_sound = 0;
|
||||
int old_cart_sound = 0;
|
||||
|
||||
void EmitSample()
|
||||
public int EmitSample()
|
||||
{
|
||||
if (recalculate)
|
||||
{
|
||||
|
@ -1391,16 +1385,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
int s_noise = noise.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
|
||||
float pulse_out, tnd_out;
|
||||
if (s_pulse0 == 0 && s_pulse1 == 0)
|
||||
|
@ -1412,13 +1396,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
float output = pulse_out + tnd_out;
|
||||
// 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.
|
||||
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;
|
||||
|
||||
return mix;
|
||||
}
|
||||
|
||||
sampleclock++;
|
||||
return oldmix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ using BizHawk.Emulation.Cores.Components.M6502;
|
|||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
||||
{
|
||||
public partial class NES : IEmulator, ICycleTiming
|
||||
public partial class NES : IEmulator, ISoundProvider, ICycleTiming
|
||||
{
|
||||
//hardware/state
|
||||
public MOS6502X<CpuLink> cpu;
|
||||
|
@ -72,92 +72,65 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
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()
|
||||
{
|
||||
if (magicSoundProvider != null)
|
||||
magicSoundProvider.Dispose();
|
||||
magicSoundProvider = null;
|
||||
if (blip != null)
|
||||
{
|
||||
blip.Dispose();
|
||||
blip = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class MagicSoundProvider : ISoundProvider, IDisposable
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
BlipBuffer blip;
|
||||
NES nes;
|
||||
blip.EndFrame(apu.sampleclock);
|
||||
apu.sampleclock = 0;
|
||||
|
||||
const int blipbuffsize = 4096;
|
||||
nsamp = blip.SamplesAvailable();
|
||||
samples = new short[nsamp * 2];
|
||||
|
||||
public MagicSoundProvider(NES nes, uint infreq)
|
||||
{
|
||||
this.nes = nes;
|
||||
blip.ReadSamples(samples, nsamp, true);
|
||||
// duplicate to stereo
|
||||
for (int i = 0; i < nsamp * 2; i += 2)
|
||||
samples[i + 1] = samples[i];
|
||||
|
||||
blip = new BlipBuffer(blipbuffsize);
|
||||
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;
|
||||
}
|
||||
}
|
||||
Board.ApplyCustomAudio(samples);
|
||||
}
|
||||
public MagicSoundProvider magicSoundProvider;
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
blip.Clear();
|
||||
apu.sampleclock = 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
|
@ -243,8 +216,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
default:
|
||||
throw new Exception("Unknown displaytype!");
|
||||
}
|
||||
if (magicSoundProvider == null)
|
||||
magicSoundProvider = new MagicSoundProvider(this, (uint)cpuclockrate);
|
||||
|
||||
blip.SetRates((uint)cpuclockrate, 44100);
|
||||
|
||||
BoardSystemHardReset();
|
||||
|
||||
|
@ -563,7 +536,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
/////////////////////////////
|
||||
// dmc dma end
|
||||
/////////////////////////////
|
||||
apu.RunOne(true);
|
||||
apu.RunOneFirst();
|
||||
|
||||
if (cpu.RDY && !IRQ_delay)
|
||||
{
|
||||
|
@ -576,7 +549,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
|
||||
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)
|
||||
ppu.double_2007_read--;
|
||||
|
@ -592,8 +576,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
cpu.RDY = true;
|
||||
IRQ_delay = true;
|
||||
}
|
||||
|
||||
Board.ClockCPU();
|
||||
}
|
||||
|
||||
public byte ReadReg(int addr)
|
||||
|
|
|
@ -61,6 +61,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
ser.Sync("sprdma_countdown", ref sprdma_countdown);
|
||||
ser.Sync("cpu_deadcounter", ref cpu_deadcounter);
|
||||
|
||||
ser.Sync("old_s", ref old_s);
|
||||
|
||||
//oam related
|
||||
ser.Sync("Oam_Dma_Index", ref oam_dma_index);
|
||||
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 };
|
||||
ser.Register<ITraceable>(Tracer);
|
||||
ser.Register<IVideoProvider>(videoProvider);
|
||||
ser.Register<ISoundProvider>(magicSoundProvider);
|
||||
ser.Register<ISoundProvider>(this);
|
||||
|
||||
if (Board is BANDAI_FCG_1)
|
||||
{
|
||||
|
|
|
@ -298,7 +298,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
ser.Sync("xstart", ref xstart);
|
||||
ser.Sync("rasterpos", ref rasterpos);
|
||||
ser.Sync("renderspritenow", ref renderspritenow);
|
||||
ser.Sync("renderbgnow", ref renderbgnow);
|
||||
ser.Sync("s", ref s);
|
||||
ser.Sync("ppu_aux_index", ref ppu_aux_index);
|
||||
ser.Sync("junksprite", ref junksprite);
|
||||
|
@ -350,10 +349,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
void runppu()
|
||||
{
|
||||
//run one ppu cycle at a time so we can interact with the ppu and clockPPU at high granularity
|
||||
|
||||
|
||||
if (install_2006>0)
|
||||
//run one ppu cycle at a time so we can interact with the ppu and clockPPU at high granularity
|
||||
if (install_2006 > 0)
|
||||
{
|
||||
install_2006--;
|
||||
if (install_2006==0)
|
||||
|
@ -386,7 +383,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
ppur.status.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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
nes.Board.ClockPPU();
|
||||
|
|
|
@ -78,13 +78,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
pixelcolor_latch_1 = pixelcolor;
|
||||
}
|
||||
|
||||
void Read_bgdata(int cycle, ref BGDataRecord bgdata)
|
||||
void Read_bgdata(int cycle, int i)
|
||||
{
|
||||
switch (cycle)
|
||||
{
|
||||
case 0:
|
||||
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;
|
||||
case 1:
|
||||
break;
|
||||
|
@ -98,20 +98,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
if ((ppur.ht & 2) != 0) at >>= 2;
|
||||
at &= 0x03;
|
||||
at <<= 2;
|
||||
bgdata.at = at;
|
||||
bgdata[i].at = at;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
break;
|
||||
case 4:
|
||||
ppu_addr_temp = ppur.get_ptread(bgdata.nt);
|
||||
bgdata.pt_0 = ppubus_read(ppu_addr_temp, true, true);
|
||||
ppu_addr_temp = ppur.get_ptread(bgdata[i].nt);
|
||||
bgdata[i].pt_0 = ppubus_read(ppu_addr_temp, true, true);
|
||||
break;
|
||||
case 5:
|
||||
break;
|
||||
case 6:
|
||||
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;
|
||||
case 7:
|
||||
break;
|
||||
|
@ -134,7 +134,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
int xstart;
|
||||
int rasterpos;
|
||||
bool renderspritenow;
|
||||
bool renderbgnow;
|
||||
int s;
|
||||
int ppu_aux_index;
|
||||
bool junksprite;
|
||||
|
@ -149,8 +148,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
ppur.status.cycle = 0;
|
||||
|
||||
// These things happen at the start of every frame
|
||||
//Reg2002_vblank_active = true;
|
||||
//Reg2002_vblank_active_pending = true;
|
||||
ppuphase = PPU_PHASE_VBL;
|
||||
bgdata = new BGDataRecord[34];
|
||||
}
|
||||
|
@ -188,6 +185,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
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.sl++;
|
||||
if (ppur.status.sl == 241 + preNMIlines + postNMIlines)
|
||||
|
@ -201,100 +204,97 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
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 == 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)
|
||||
{
|
||||
/////////////////////////////////////////////
|
||||
// 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 actual effect setting a flag that always returns 0xFF from a OAM read
|
||||
// this is a bit of a shortcut to save some instructions
|
||||
// data is read from OAM as normal but never used
|
||||
soam[soam_index] = 0xFF;
|
||||
soam_index++;
|
||||
if (!is_even_cycle)
|
||||
{
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
oam_index = 0;
|
||||
sprite_eval_write = false;
|
||||
}
|
||||
|
||||
if (is_even_cycle && oam_index < 256)
|
||||
if (is_even_cycle)
|
||||
{
|
||||
if ((oam_index + soam_m_index) < 256)
|
||||
read_value = OAM[oam_index + soam_m_index];
|
||||
else
|
||||
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)
|
||||
{
|
||||
//look for sprites
|
||||
|
@ -341,7 +341,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
oam_index += 4;
|
||||
}
|
||||
}
|
||||
else if (soam_index >= 8)
|
||||
else
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we don't write sprites anymore, just scan through the oam
|
||||
read_value = soam[0];
|
||||
oam_index += 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Sprite Evaluation End
|
||||
/////////////////////////////////////////////
|
||||
|
||||
int pixel = 0, pixelcolor = PALRAM[pixel];
|
||||
|
||||
//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
|
||||
// due to the cpu not running while the sprite renders below
|
||||
if (PPUON) { Read_bgdata(xp, ref bgdata[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];
|
||||
|
||||
if (PPUON) { Read_bgdata(xp, xt + 2); }
|
||||
//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.
|
||||
//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
|
||||
int addr = ppur.get_2007access();
|
||||
|
@ -412,15 +410,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
pixel = addr & 0x1F;
|
||||
}
|
||||
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
|
||||
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_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);
|
||||
if (pixel != 0)
|
||||
pixel |= bgdata[bgtile].at;
|
||||
|
@ -428,7 +427,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
|
||||
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
|
||||
if (sl_sprites[256 + xt * 8 + xp] != 0 && renderspritenow)
|
||||
|
@ -457,13 +456,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
runppu();
|
||||
|
||||
if (PPUON && xp == 6)
|
||||
if (xp == 6 && PPUON)
|
||||
{
|
||||
ppu_was_on = true;
|
||||
if (ppur.status.cycle == 255) { race_2006 = true; }
|
||||
}
|
||||
|
||||
if (PPUON && xp == 7)
|
||||
if (xp == 7 && PPUON)
|
||||
{
|
||||
ppur.increment_hsc();
|
||||
|
||||
|
@ -526,18 +525,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
else
|
||||
{
|
||||
// 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();
|
||||
|
||||
if (PPUON && xp == 6)
|
||||
if (xp == 6 && PPUON)
|
||||
{
|
||||
ppu_was_on = true;
|
||||
if (ppur.status.cycle == 255) { race_2006 = true; }
|
||||
|
||||
}
|
||||
|
||||
if (PPUON && xp == 7)
|
||||
if (xp == 7 && PPUON)
|
||||
{
|
||||
ppur.increment_hsc();
|
||||
|
||||
|
@ -623,7 +622,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
s = 0;
|
||||
ppu_aux_index = 0;
|
||||
|
||||
junksprite = (!PPUON);
|
||||
junksprite = !PPUON;
|
||||
|
||||
t_oam[s].oam_y = soam[s * 4];
|
||||
t_oam[s].oam_ind = soam[s * 4 + 1];
|
||||
|
@ -662,7 +661,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
runppu();
|
||||
break;
|
||||
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();
|
||||
|
||||
|
@ -670,7 +669,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
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)
|
||||
|
@ -739,15 +738,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
break;
|
||||
case 5:
|
||||
// if the PPU is off, we don't put anything on the bus
|
||||
if (junksprite)
|
||||
{
|
||||
runppu();
|
||||
}
|
||||
else
|
||||
{
|
||||
runppu();
|
||||
}
|
||||
runppu();
|
||||
break;
|
||||
case 6:
|
||||
// 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)
|
||||
{
|
||||
junksprite = (!PPUON);
|
||||
junksprite = !PPUON;
|
||||
|
||||
t_oam[s].oam_y = soam[s * 4];
|
||||
t_oam[s].oam_ind = soam[s * 4 + 1];
|
||||
|
@ -925,26 +916,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
}
|
||||
else
|
||||
{
|
||||
if (ppur.status.cycle == 320)
|
||||
{
|
||||
ppuphase = PPU_PHASE_BG;
|
||||
xt = 0;
|
||||
xp = 0;
|
||||
}
|
||||
|
||||
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
|
||||
Read_bgdata(xp, ref bgdata[xt]);
|
||||
Read_bgdata(xp, xt);
|
||||
|
||||
runppu();
|
||||
|
||||
if (PPUON && xp == 6)
|
||||
if (xp == 6 && PPUON)
|
||||
{
|
||||
ppu_was_on = true;
|
||||
}
|
||||
|
||||
if (PPUON && xp == 7)
|
||||
if (xp == 7 && PPUON)
|
||||
{
|
||||
if (!race_2006)
|
||||
ppur.increment_hsc();
|
||||
|
@ -1009,6 +1000,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|||
|
||||
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.sl++;
|
||||
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 (Reg2002_vblank_active_pending)
|
||||
{
|
||||
Reg2002_vblank_active = 1;
|
||||
Reg2002_vblank_active_pending = false;
|
||||
}
|
||||
|
||||
ppudead--;
|
||||
|
||||
ppu_init_frame();
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SubNESHawk
|
|||
game, rom, subnesSettings, subnesSyncSettings);
|
||||
|
||||
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" };
|
||||
ser.Register<ITraceable>(_tracer);
|
||||
|
|
Loading…
Reference in New Issue