Example #1
0
        private static Emulator Boot()
        {
            var ppuRegs   = new PPURegisters();
            var memoryBus = new MemoryBus(ppuRegs)
            {
                IsBootRomMapped = useBootRom
            };
            //memoryBus.Attach(new GameLinkConsole());
            var ppu = new PPU(ppuRegs, memoryBus);

            ppu.Boot();

            var cpu = new CPU(new Registers(), memoryBus);

            if (!useBootRom)
            {
                cpu.BootWithoutBootRom();
            }

            var rom = Cartridge.LoadFrom(romPath);

            memoryBus.Load(rom);

            var joypad = new Joypad(memoryBus.JoypadRegister);

            return(new Emulator(cpu, ppu, joypad));
        }
Example #2
0
        public NESRawMemoryMap(PPURegisters ppuRegisters, InputHandler inputHandler) : base()
        {
            this.ppuRegisters = ppuRegisters;
            this.inputHandler = inputHandler;

            this.ppuRegisterLookup = new Dictionary <int, Register8Bit>()
            {
                { PPURegisterAddresses.PPUCTRL, ppuRegisters.PPUCTRL },
                { PPURegisterAddresses.PPUMASK, ppuRegisters.PPUMASK },
                { PPURegisterAddresses.PPUSTATUS, ppuRegisters.PPUSTATUS },
                { PPURegisterAddresses.OAMADDR, ppuRegisters.OAMADDR },
                { PPURegisterAddresses.OAMDATA, ppuRegisters.OAMDATA },
                { PPURegisterAddresses.PPUADDR, ppuRegisters.PPUADDR },
                { PPURegisterAddresses.PPUDATA, ppuRegisters.PPUDATA },
                { PPURegisterAddresses.OAMDMA, ppuRegisters.OAMDMA }
            };

            this.apuRegisterLookup = new Dictionary <int, Register8Bit>()
            {
                { APURegisterAddresses.Pulse1TimerLow, null },
                { APURegisterAddresses.Pulse1TimerHigh, null },
                { APURegisterAddresses.Pulse2TimerLow, null },
                { APURegisterAddresses.Pulse2TimerHigh, null },
                { APURegisterAddresses.DirectLoad, null },
                { APURegisterAddresses.ChannelStatus, null },
                { APURegisterAddresses.TriangleTimerLow, null },
                { APURegisterAddresses.TriangleTimerHigh, null },
            };

            this.inputHandler.ControllerChanged += this.OnControllerChanged;
        }
Example #3
0
 public SpriteHandler(
     IPPUMemoryMap ppuMemoryMap,
     PPURegisters registers,
     PaletteHandler paletteHandler)
 {
     this.registers      = registers;
     this.ppuMemoryMap   = ppuMemoryMap;
     this.paletteHandler = paletteHandler;
 }
Example #4
0
        private static void Start(string romPath)
        {
            var ppuRegs   = new PPURegisters();
            var memoryBus = new MemoryBus(ppuRegs);

            ppu = new PPU(ppuRegs, memoryBus);
            cpu = new CPU(new Registers(), memoryBus);
            cpu.BootWithoutBootRom();

            var rom = Cartridge.LoadFrom(romPath);

            memoryBus.Load(rom);
        }
Example #5
0
        public void Generate_Expected_Background_Map_Pixels_From_Known_VRAM_Dump_Using_Unsigned_Tile_Numbers()
        {
            var vram   = Memory.FromFile(Path.Combine("PPU", "Input", "tetris_title_screen.vram.dump"));
            var regs   = new PPURegisters(lcdc: 0xD3, bgPalette: 0xE4);
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram
            };
            var ppu = new PPU(regs, memBus);

            ppu.TileSet.UpdateFrom(vram);

            var actualPixels   = ppu.RenderBackgroundMap();
            var expectedPixels = ImageHelper.LoadImageAsPaletteIndexedByteArray(Path.Combine("PPU", "Expected", "tetris_title_screen_expected_bgmap.png"));

            AssertPixelsMatch(expectedPixels, actualPixels, width: 256);
        }
Example #6
0
        public void Generate_Blank_Background_Map_Pixels_When_Background_Map_Drawing_Is_Disabled()
        {
            var vram   = Memory.FromFile(Path.Combine("PPU", "Input", "pokemon_reds_room.vram.dump"));
            var regs   = new PPURegisters(lcdc: 0xE0);  //LCDC bit 0 = 0 => background is disabled
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram
            };
            var ppu = new PPU(regs, memBus);

            Assert.IsFalse(ppu.Registers.LCDControl.BackgroundDisplayEnabled);

            var actualPixels   = ppu.RenderBackgroundMap();
            var expectedPixels = new byte[TileMap.WidthInPixels * TileMap.HeightInPixels];

            AssertPixelsMatch(expectedPixels, actualPixels, width: 256);
        }
Example #7
0
        public void Generate_Blank_Sprite_Pixels_When_Sprite_Drawing_Is_Disabled()
        {
            var vram   = Memory.FromFile(Path.Combine("PPU", "Input", "pokemon_reds_room.vram.dump"));
            var oam    = Memory.FromFile(Path.Combine("PPU", "Input", "pokemon_reds_room.oam.dump"));
            var regs   = new PPURegisters(lcdc: 0xE3, spritePalette0: 0xE4, spritePalette1: 0xE4);
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram, ObjectAttributeMemory = oam
            };
            var ppu = new PPU(regs, memBus);

            ppu.Registers.LCDControl.SpriteDisplayEnabled = false;
            var actualPixels   = ppu.RenderSprites();
            var expectedPixels = new byte[PPU.ScreenWidthInPixels * PPU.ScreenHeightInPixels];

            AssertPixelsMatch(expectedPixels, actualPixels, width: PPU.ScreenWidthInPixels);
        }
Example #8
0
        public void Generate_Blank_Window_Pixels_When_Window_Drawing_Is_Disabled()
        {
            var vram   = Memory.FromFile(Path.Combine("PPU", "Input", "links_awakening_you_are_on_koholint_island.vram.dump"));
            var regs   = new PPURegisters(lcdc: 0xD7);  //LCDC bit 5 = 0 => window is disabled
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram
            };
            var ppu = new PPU(regs, memBus);

            Assert.IsFalse(ppu.Registers.LCDControl.WindowDisplayEnabled);

            var actualPixels   = ppu.RenderWindow();
            var expectedPixels = new byte[TileMap.WidthInPixels * TileMap.HeightInPixels];

            AssertPixelsMatch(expectedPixels, actualPixels, width: 256);
        }
Example #9
0
        public void Generate_Expected_Window_Pixels_From_Known_VRAM_Dump_Using_Signed_Tile_Numbers()
        {
            var vram   = Memory.FromFile(Path.Combine("PPU", "Input", "links_awakening_you_are_on_koholint_island.vram.dump"));
            var regs   = new PPURegisters(lcdc: 0xE7, bgPalette: 0xE4);
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram
            };
            var ppu = new PPU(regs, memBus);

            ppu.TileSet.UpdateFrom(vram);

            var actualPixels   = ppu.RenderWindow();
            var expectedPixels = ImageHelper.LoadImageAsPaletteIndexedByteArray(Path.Combine("PPU", "Expected", "links_awakening_you_are_on_koholint_island_expected_window.png"));

            AssertPixelsMatch(expectedPixels, actualPixels, width: 256);
        }
Example #10
0
        public void Generate_Expected_Tileset_Pixels_From_Known_VRAM_Dump()
        {
            var vram   = Memory.FromFile(Path.Combine("PPU", "Input", "tetris.tileset.dump"));
            var regs   = new PPURegisters();
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram
            };
            var ppu = new PPU(regs, memBus);

            ppu.TileSet.UpdateFrom(vram);

            var actualPixels   = ppu.RenderTileSet();
            var expectedPixels = ImageHelper.LoadImageAsPaletteIndexedByteArray(Path.Combine("PPU", "Expected", "tetris.tileset.png"));

            AssertPixelsMatch(expectedPixels, actualPixels, width: TileSet.WidthInPixels);
        }
Example #11
0
        public BackgroundHandler(
            IPPUMemoryMap ppuMemoryMap,
            PPURegisters registers,
            PPUInternalRegisters internalRegisters,
            ScrollHandler scrollHandler,
            PaletteHandler paletteHandler)
        {
            this.registers         = registers;
            this.ppuMemoryMap      = ppuMemoryMap;
            this.scrollHandler     = scrollHandler;
            this.paletteHandler    = paletteHandler;
            this.internalRegisters = internalRegisters;

            foreach (var patternTable in this.ppuMemoryMap.GetMemorySegments <PatternTable>())
            {
                if (patternTable.Range.Min == 0x0000)
                {
                    this.patternTable0 = patternTable;
                }
                else if (patternTable.Range.Min == 0x1000)
                {
                    this.patternTable1 = patternTable;
                }
            }

            foreach (var nameTable in this.ppuMemoryMap.GetMemorySegments <Nametable>())
            {
                if (nameTable.Range.Min == 0x2000)
                {
                    this.nameTable0 = nameTable;
                }
                else if (nameTable.Range.Min == 0x2400)
                {
                    this.nameTable1 = nameTable;
                }
                else if (nameTable.Range.Min == 0x2800)
                {
                    this.nameTable2 = nameTable;
                }
                else if (nameTable.Range.Min == 0x2C00)
                {
                    this.nameTable3 = nameTable;
                }
            }
        }
Example #12
0
        public void Generate_Expected_Sprite_Pixels_From_Known_VRAM_And_OAM_Dumps()
        {
            var vram   = Memory.FromFile(Path.Combine("PPU", "Input", "tetris_title_screen.vram.dump"));
            var oam    = Memory.FromFile(Path.Combine("PPU", "Input", "tetris_title_screen.oam.dump"));
            var regs   = new PPURegisters(lcdc: 0xD3, spritePalette0: 0xFF, spritePalette1: 0xFF);
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram, ObjectAttributeMemory = oam
            };
            var ppu = new PPU(regs, memBus);

            ppu.TileSet.UpdateFrom(vram);
            ppu.Sprites.UpdateFrom(oam);

            var actualPixels   = ppu.RenderSprites();
            var expectedPixels = ImageHelper.LoadImageAsPaletteIndexedByteArray(Path.Combine("PPU", "Expected", "tetris_title_screen_expected_sprites.png"));

            AssertPixelsMatch(expectedPixels, actualPixels, width: PPU.ScreenWidthInPixels);
        }
Example #13
0
        public void Generate_Expected_Screen_Pixels_From_Known_Memory_Dump_When_Sprite_Drawing_Is_Disabled_But_Background_Drawing_Is_Enabled()
        {
            var vram = Memory.FromFile(Path.Combine("PPU", "Input", "pokemon_reds_room.vram.dump"));
            var oam  = Memory.FromFile(Path.Combine("PPU", "Input", "pokemon_reds_room.oam.dump"));
            //magic numbers in PPU registers collected from bgb at the point the above memory dumps were captured
            var regs   = new PPURegisters(lcdc: 0xE3, scrollY: 0xD0, windowX: 0x07, windowY: 0x90, bgPalette: 0xE4, spritePalette0: 0xD0, spritePalette1: 0xE0);
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram, ObjectAttributeMemory = oam
            };
            var ppu = new PPU(regs, memBus);

            ppu.TileSet.UpdateFrom(vram);

            ppu.Registers.LCDControl.SpriteDisplayEnabled = false;
            var actualPixels   = ppu.ForceRenderScreen();
            var expectedPixels = ImageHelper.LoadImageAsPaletteIndexedByteArray(Path.Combine("PPU", "Expected", "pokemon_reds_room_expected_screen_wo_sprites.png"));

            AssertPixelsMatch(expectedPixels, actualPixels, width: PPU.ScreenWidthInPixels);
        }
Example #14
0
        public void Generate_Expected_Screen_Pixels_From_Known_VRAM_And_OAM_Dumps_With_Background_And_8_x_16_Sprites_And_Window()
        {
            var vram = Memory.FromFile(Path.Combine("PPU", "Input", "links_awakening_you_are_on_koholint_island.vram.dump"));
            var oam  = Memory.FromFile(Path.Combine("PPU", "Input", "links_awakening_you_are_on_koholint_island.oam.dump"));
            //magic numbers in PPU registers collected from bgb at the point the above memory dumps were captured
            var regs   = new PPURegisters(lcdc: 0xE7, windowX: 0x06, windowY: 0x80, bgPalette: 0xE4, spritePalette0: 0x1C, spritePalette1: 0xE4);
            var memBus = new MemoryBus(regs)
            {
                VideoMemory = vram, ObjectAttributeMemory = oam
            };
            var ppu = new PPU(regs, memBus);

            ppu.TileSet.UpdateFrom(vram);
            ppu.Sprites.UpdateFrom(oam);

            var actualPixels   = ppu.ForceRenderScreen();
            var expectedPixels = ImageHelper.LoadImageAsPaletteIndexedByteArray(Path.Combine("PPU", "Expected", "links_awakening_you_are_on_koholint_island_expected_screen.png"));

            AssertPixelsMatch(expectedPixels, actualPixels, width: PPU.ScreenWidthInPixels);
        }
Example #15
0
        public NESMemoryMapSlim(PPURegisters ppuRegisters, InputHandler inputHandler) : base()
        {
            this.ppuRegisters = ppuRegisters;
            this.inputHandler = inputHandler;

            this.sram  = new SRAM(MemoryRanges.SRAM, "SRAM");
            this.stack = new CPU.MOS6502.Stack(MemoryRanges.Stack, "Stack");

            this.prgROMLowerBank = new ExternalFacade(this, MemoryRanges.ProgramRomLowerBank, Constants.ProgramRomLowerBank);
            this.prgROMUpperBank = new ExternalFacade(this, MemoryRanges.ProgramRomUpperBank, Constants.ProgramRomUpperBank);

            this.ppuRegisterLookup = new Dictionary <int, Register8Bit>()
            {
                { PPURegisterAddresses.PPUCTRL, ppuRegisters.PPUCTRL },
                { PPURegisterAddresses.PPUMASK, ppuRegisters.PPUMASK },
                { PPURegisterAddresses.PPUSTATUS, ppuRegisters.PPUSTATUS },
                { PPURegisterAddresses.OAMADDR, ppuRegisters.OAMADDR },
                { PPURegisterAddresses.OAMDATA, ppuRegisters.OAMDATA },
                { PPURegisterAddresses.PPUADDR, ppuRegisters.PPUADDR },
                { PPURegisterAddresses.PPUDATA, ppuRegisters.PPUDATA },
                { PPURegisterAddresses.OAMDMA, ppuRegisters.OAMDMA }
            };

            this.apuRegisterLookup = new Dictionary <int, Register8Bit>()
            {
                { APURegisterAddresses.Pulse1TimerLow, null },
                { APURegisterAddresses.Pulse1TimerHigh, null },
                { APURegisterAddresses.Pulse2TimerLow, null },
                { APURegisterAddresses.Pulse2TimerHigh, null },
                { APURegisterAddresses.DirectLoad, null },
                { APURegisterAddresses.ChannelStatus, null },
                { APURegisterAddresses.TriangleTimerLow, null },
                { APURegisterAddresses.TriangleTimerHigh, null },
            };

            this.inputHandler.ControllerChanged += this.OnControllerChanged;

            this.ScopeMemoryRanges();
        }
Example #16
0
 public ScrollHandler(PPURegisters registers, PPUInternalRegisters internalRegisters)
 {
     this.registers         = registers;
     this.internalRegisters = internalRegisters;
 }