예제 #1
0
        protected void RunEmulator(IEmulator emulator, Func <bool> tickFunc)
        {
            IProcessorCore      cpu = (IProcessorCore)emulator.Components.First(c => c.Name == "Ricoh 2A03 CPU");
            IComponentWithClock ppu = (IComponentWithClock)emulator.Components.First(c => c.Name == "Ricoh 2C02 PPU");
            IComponentWithClock apu = (IComponentWithClock)emulator.Components.First(c => c.Name == "NES APU");

            while (true)
            {
                if (tickFunc != null && !tickFunc())
                {
                    break;
                }

                int cycles = cpu.Step();
                if (cycles < 0)
                {
                    Assert.Fail("CPU step failed!");
                    break;
                }

                for (int i = 0; i < cycles; i++)
                {
                    // PPU ticks 3 times during each CPU cycle
                    ppu.Tick();
                    ppu.Tick();
                    ppu.Tick();
                }

                for (int i = 0; i < cycles; i++)
                {
                    apu.Tick();
                }
            }
        }
예제 #2
0
        internal NesApu(IMemoryBus cpuBus, IProcessorCore cpu)
        {
            this.cpuIrq = cpu.GetInterruptByName("IRQ");

            this.pulse1   = new PulseChannel(true);
            this.pulse2   = new PulseChannel(false);
            this.triangle = new TriangleChannel();
            this.noise    = new NoiseChannel();
            this.dmc      = new DeltaChannel(cpuBus, cpuIrq);

            this.isApuCycle = false;
            this.buffer     = new byte[NesApu.BUFFER_SIZE];
            this.ptr        = Marshal.AllocHGlobal(4);
        }
예제 #3
0
        void IEmulator.Run()
        {
            int cycles;
            int cycleCount = 0;

            Stopwatch sw = new Stopwatch();

            sw.Start();

            this.run = true;

            IProcessorCore      core     = (IProcessorCore)this.cpu;
            IComponentWithClock ppuClock = (IComponentWithClock)this.ppu;
            IComponentWithClock apuClock = (IComponentWithClock)this.cpu.APU;

            while (this.run)
            {
                cycles = core.Step();
                if (cycles < 0)
                {
                    break;
                }

                for (int i = 0; i < cycles; i++)
                {
                    // PPU ticks 3 times during each CPU cycle
                    ppuClock.Tick();
                    ppuClock.Tick();
                    ppuClock.Tick();
                }

                for (int i = 0; i < cycles; i++)
                {
                    apuClock.Tick();
                }

                cycleCount += cycles;
                if (sw.ElapsedMilliseconds >= 1000)
                {
                    Trace.WriteLine(String.Format("Emulated clock speed: {0} MHz", cycleCount / (1000000f)));
                    cycleCount = 0;
                    sw.Restart();
                }
            }

            stopEvent.Set();
        }
예제 #4
0
        public void RunCpuTest()
        {
            IEmulator emulator = this.GetInstance();

            emulator.LoadFile(@"TestRoms\nestest\nestest.nes");

            List <string> expectedOutput = new List <string>(File.ReadAllLines(@"TestResources\CpuTests\nestest.expected"));

            IProcessorCore          cpu = (IProcessorCore)emulator.Components.First(c => c.Name == "Ricoh 2A03 CPU");
            IComponentWithRegisters ppu = (IComponentWithRegisters)emulator.Components.First(c => c.Name == "Ricoh 2C02 PPU");

            IMemoryBus cpuBus = (IMemoryBus)emulator.Components.First(c => c.Name == "CPU Bus");

            // Jump to automated test entry point
            cpu.GetRegisterByName("PC").Value = 0xC000;

            // Set initial state
            cpu.GetRegisterByName("S").Value        = 0xFD;
            ppu.GetRegisterByName("cycle").Value    = 0;
            ppu.GetRegisterByName("scanline").Value = 241;

            IEnumerator <string> outputLineEnumerator = expectedOutput.GetEnumerator();

            outputLineEnumerator.MoveNext();

            base.RunEmulator(emulator, () =>
            {
                string state = this.DumpEmulatorState(cpu, ppu, cpuBus);
                string expectedOutputLine = outputLineEnumerator.Current;

                if (!expectedOutputLine.StartsWith("#") &&
                    !String.Equals(state, expectedOutputLine, StringComparison.Ordinal))
                {
                    Debug.WriteLine("**** {0}", (object)state);
                    Debug.WriteLine("Exp: {0}", (object)expectedOutputLine);
                    Assert.Fail("Output mismatch!");
                    return(false);
                }

                Debug.WriteLine(state);

                return(outputLineEnumerator.MoveNext());
            });
        }
예제 #5
0
        // Dump current CPU/PPU state in Nintendulator-ish format
        private string DumpEmulatorState(IProcessorCore cpu, IComponentWithRegisters ppu, IMemoryBus cpuBus)
        {
            UInt16 PC = (UInt16)cpu.GetRegisterByName("PC").Value;
            byte   A  = (byte)cpu.GetRegisterByName("A").Value;
            byte   X  = (byte)cpu.GetRegisterByName("X").Value;
            byte   Y  = (byte)cpu.GetRegisterByName("Y").Value;
            string P  = cpu.GetRegisterByName("P").FormattedValue;
            byte   S  = (byte)cpu.GetRegisterByName("S").Value;

            int cycle    = ppu.GetRegisterByName("cycle").Value;
            int scanline = ppu.GetRegisterByName("scanline").Value;

            StringBuilder state = new StringBuilder();

            state.AppendFormat("{0:X4}  ", PC);

            IEnumerable <byte> instructionBytes = cpu.GetInstructionBytes(PC);

            if (!instructionBytes.Any())
            {
                state.AppendFormat("UNKNOWN: {0:X2}", cpuBus.Read(PC));
                return(state.ToString());
            }

            IInstruction instruction = cpu.DecodeInstruction(instructionBytes);

            state.AppendFormat("{0,-8} {1,4} {2} {3}",
                               String.Join(" ", instructionBytes.Select(b => String.Format("{0:X2}", b))),
                               instruction.Mnemonic,
                               instruction.Operands,
                               instruction.OperandsDetail);

            state.Append(' ', 48 - state.Length);

            state.AppendFormat("A:{0:X2} X:{1:X2} Y:{2:X2} P:{3} SP:{4:X2} CYC:{5,3} SL:{6}",
                               A, X, Y, P, S, cycle, scanline);

            return(state.ToString());
        }
예제 #6
0
        internal Ricoh2C02(IProcessorCore cpu, IMemoryBus cpuBus, IMemoryBus ppuBus)
        {
            this.ppuBus = ppuBus;

            this.vbi      = cpu.GetInterruptByName("NMI");
            this.vbiTimer = new Stopwatch();

            this.paletteMemory = new byte[0x20];
            this.primaryOam    = new byte[0x100];
            this.secondaryOam  = new byte[0x20];

            // PPU registers on CPU bus
            cpuBus.RegisterMappedDevice(this, 0x2000, 0x2007);

            // Palette data
            ppuBus.RegisterMappedDevice(this, 0x3F00, 0x3F1F);

            // Set up power-on state
            this.SetPpuCtrl(0x00);
            this.SetPpuMask(0x00);
            this.SetOamAddr(0x00);

            this.spriteZeroHit  = false;
            this.spriteOverflow = false;
            this.isVBlank       = false;
            this.oddFrame       = false;
            this.scanline       = -1;
            this.cycle          = 0;

            this.registers = new ReadOnlyCollection <IRegister>(
                new IRegister[] {
                new RegisterWrapper("scanline", "PPU Scanline", 16, () => this.scanline, s => this.scanline = s),
                new RegisterWrapper("cycle", "PPU Cycle", 16, () => this.cycle, c => this.cycle             = c)
            });

            //this.vbiTimer.Start();
        }
예제 #7
0
 public static IProcessorInterrupt GetInterruptByName(this IProcessorCore cpu, string interruptName)
 {
     return(cpu.Interrupts.FirstOrDefault(i => String.Equals(i.Name, interruptName, StringComparison.Ordinal)));
 }