Example #1
0
 public Renderer(PPU ppu, FrameSink destination, long offset)
 {
     fs       = destination;
     PPU      = ppu;
     ppu.Mode = Mode.OAMSearch;
     fetcher  = new PixelFetcher(PPU);
     TimeUntilWhichToPause += offset;
 }
Example #2
0
 public PPU(Action enableVBlankInterrupt, Action enableLCDCStatusInterrupt, FrameSink frameSink)
 {
     OAM  = new OAM();
     VRAM = new VRAM();
     EnableVBlankInterrupt     = enableVBlankInterrupt;
     EnableLCDCStatusInterrupt = enableLCDCStatusInterrupt;
     Writer = frameSink;
 }
Example #3
0
        public Core(byte[] gameROM, byte[]?bootROM, Keypad Keypad, FrameSink frameSink)
        {
            if (gameROM.Length < 0x8000)
            {
                throw new Exception("Cartridge file has to be at least 8kb in size");
            }
            PC = new();

            InterruptRegisters        = new InterruptRegisters();
            Keypad.Input.KeyWentDown += InterruptRegisters.TriggerEvent;

            APU = new APU(32768);
            PPU = new PPU(InterruptRegisters.EnableVBlankInterrupt, InterruptRegisters.EnableLCDSTATInterrupt, frameSink);

            Timers = new Timers(InterruptRegisters.EnableTimerInterrupt);

            var ioRegisters = SetupControlRegisters(Keypad);

            CartHeader Header = new CartHeader(gameROM);

            MBC Card;

            if (Header.HasBattery())
            {
                var mmf = Header.MakeMemoryMappedFile();
                Card = Header.HasClock() ? Header.MakeMBC(gameROM, mmf, () => masterclock) : Header.MakeMBC(gameROM, mmf);
            }
            else
            {
                Card = Header.MakeMBC(gameROM);
            }

            //Writing out the RTC too often would be very heavy. This writes it out once per frame.
            //
            if (Header.Type == CartType.MBC3_TIMER_RAM_BATTERY)
            {
                var SaveRTC = ((MBC3)Card).SaveRTC();

                void h(object?x, EventArgs y) => SaveRTC();

                frameSink.FramePushed += h;
            }

            if (Card is MBC5WithRumble rumble)
            {
                rumble.RumbleStateChange += Keypad.ToggleRumble;
            }

            Memory = new MMU(
                bootROM,
                Card,
                PPU.VRAM,
                PPU.OAM,
                ioRegisters,
                (x => InterruptRegisters.InterruptControlRegister = x,
                 () => InterruptRegisters.InterruptControlRegister),
                PC
                );

            CPU = new CPU(Memory, InterruptRegisters, PC);
            ioRegisters[0x0f] = InterruptRegisters.HookUp();

            ioRegisters[0x50] = Memory.HookUpMemory();

            //We have to replicate the state of the system post boot without running the bootrom
            if (bootROM == null)
            {
                //registers
                CPU.SetStateWithoutBootrom();

                //timers
                Timers.SetStateWithoutBootrom();


                //sound
                APU.SetStateWithoutBootrom();

                //graphics TODO: we can't really set up the graphics environment correctly
                //because we will have to also initialize the internal renderer state correctly
                PPU.SetStateWithoutBootrom();


                InterruptRegisters.SetStateWithoutBootrom();
            }
        }