public MBC3(CartHeader header, byte[] gameROM, MemoryMappedFile file, Func <long>?getClock = null) { this.gameROM = gameROM; RAMBanks = file.CreateViewAccessor(0, header.RAM_Size); //0x800 is the only alternative bank size if (header.RAM_Size == 0) { RAMBankSize = 0; } //0x800 is the only alternative bank size if (header.RAM_Size == 0x800) { RAMBankSize = 0x800; } if (getClock is not null) { ClockStorage = file.CreateViewAccessor(header.RAM_Size, 16); hasClock = true; var InitialOffsetFromSave = ClockStorage.ReadInt64(0); var timeOfLastSave = ClockStorage.ReadInt64(8); var DotNetTicksElapsed = DateTime.Now.Ticks - timeOfLastSave; var GameboyTicksElapsed = (long)(DotNetTicksElapsed * (TicksPerSecond / 10000000.0)); InitialOffsetFromSave += GameboyTicksElapsed; GetRTC = () => getClock() + InitialOffsetFromSave; } }
public MBC1(CartHeader header, byte[] gameROM) { this.gameROM = gameROM; ROMBankCount = this.gameROM.Length / 0x4000; RAMBankCount = Math.Max(1, header.RAM_Size / RAMBankSize); RAMBankSize = Math.Min(header.RAM_Size, 0x2000); RAMBanks = new byte[Math.Max(0x2000, header.RAM_Size)]; }
public MBC1WithBatteryBackedRAM(CartHeader header, byte[] gameROM, MemoryMappedFile file) { this.gameROM = gameROM; ROMBankCount = this.gameROM.Length / 0x4000; RAMBankCount = Math.Max(1, header.RAM_Size / RAMBankSize); RAMBankSize = Math.Min(header.RAM_Size, 0x2000); RAMBanks = file.CreateViewAccessor(0, header.RAM_Size); }
public MBC5(CartHeader header, byte[] gameROM) { this.gameROM = gameROM; ROMBankCount = this.gameROM.Length / 0x4000; RAMBankCount = Math.Max(1, header.RAM_Size / RAMBankSize); RAMBanks = null; //0x800 is the only alternative bank size if (header.RAM_Size == 0) { RAMBankSize = 0; } //0x800 is the only alternative bank size if (header.RAM_Size == 0x800) { RAMBankSize = 0x800; } }
public MBC5(CartHeader header, byte[] gameROM, System.IO.MemoryMappedFiles.MemoryMappedFile file) { this.gameROM = gameROM; ROMBankCount = this.gameROM.Length / 0x4000; RAMBankCount = Math.Max(1, header.RAM_Size / RAMBankSize); RAMBanks = file.CreateViewAccessor(0, header.RAM_Size); //0x800 is the only alternative bank size if (header.RAM_Size == 0) { RAMBankSize = 0; } //0x800 is the only alternative bank size if (header.RAM_Size == 0x800) { RAMBankSize = 0x800; } }
public MBC5WithRumble(CartHeader cartHeader, byte[] gameROM, MemoryMappedFile memoryMappedFile) : base(cartHeader, gameROM, memoryMappedFile) { }
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(); } }