public int Parse(byte[] rom) { // Combine every two bytes into a word. int index = 0; while (index + 1 < rom.Length) { Data[(index / 2)] = (ushort)((rom[index++] << 8) | rom[index++]); } // look up hash in gamedb to see what mapper to use string s_mapper = null; var gi = Database.CheckDatabase(SHA1Checksum.ComputePrefixedHex(rom)); if (gi != null && !gi.GetOptions().TryGetValue("board", out s_mapper)) { throw new Exception("INTV gamedb entries must have a board identifier!"); } if (gi == null && (rom.Length % 1024) != 0) { throw new Exception("Game is of unusual size but no header entry present and hash not in game db."); } _mapper = 0; int.TryParse(s_mapper, out _mapper); return(rom.Length); }
public C64(CoreLoadParameters <C64Settings, C64SyncSettings> lp) { PutSyncSettings((C64SyncSettings)lp.SyncSettings ?? new C64SyncSettings()); PutSettings((C64Settings)lp.Settings ?? new C64Settings()); var ser = new BasicServiceProvider(this); ServiceProvider = ser; CoreComm = lp.Comm; _roms = lp.Roms.Select(r => r.RomData).ToList(); _currentDisk = 0; RomSanityCheck(); Init(SyncSettings.VicType, Settings.BorderType, SyncSettings.SidType, SyncSettings.TapeDriveType, SyncSettings.DiskDriveType); _cyclesPerFrame = _board.Vic.CyclesPerFrame; _memoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); InitMedia(_roms[_currentDisk]); HardReset(); switch (SyncSettings.VicType) { case VicType.Ntsc: case VicType.Drean: case VicType.NtscOld: Region = DisplayType.NTSC; break; case VicType.Pal: Region = DisplayType.PAL; break; } if (_board.Sid != null) { _soundProvider = new DCFilter(_board.Sid, 512); ser.Register <ISoundProvider>(_soundProvider); } ser.Register <IVideoProvider>(_board.Vic); ser.Register <IDriveLight>(this); _tracer = new TraceBuffer(_board.Cpu.TraceHeader); ser.Register <ITraceable>(_tracer); ser.Register <IStatable>(new StateSerializer(SyncState)); if (_board.CartPort.IsConnected) { var first = _roms[0]; // There are no multi-cart cart games, so just hardcode first RomDetails = $"{lp.Game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(first)}\r\n{MD5Checksum.ComputePrefixedHex(first)}\r\nMapper Impl \"{_board.CartPort.CartridgeType}\""; } SetupMemoryDomains(); }
private void RebootCore() { // Regenerate mapper here to make sure its state is entirely clean _mapper = CreateMapper(this, _game.GetOptions()["m"], Rom.Length); _lagCount = 0; Cpu = new MOS6502X <CpuLink>(new CpuLink(this)); if (_game["PAL"]) { _pal = true; } else if (_game["NTSC"]) { _pal = false; } else { _pal = DetectPal(_game, Rom); } // dcfilter coefficent is from real observed hardware behavior: a latched "1" will fully decay by ~170 or so tia sound cycles _tia = new TIA(this, _pal, Settings.SECAMColors); _dcfilter = new DCFilter(_tia, 256); _m6532 = new M6532(this); HardReset(); RomDetails = $"{_game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(Rom)}\r\n{MD5Checksum.ComputePrefixedHex(Rom)}\r\nMapper Impl \"{_mapper.GetType()}\""; // Some games (ex. 3D tic tac toe), turn off the screen for extended periods, so we need to allow for this here. if (_game.GetOptions().TryGetValue("SP_FRAME", out var spFrameStr) && spFrameStr == "true") { SP_FRAME = true; } // Some games wait for reset to be unpressed before turning the screen back on, hack unset it if needed if (_game.GetOptions().TryGetValue("SP_RESET", out var spResetStr) && spResetStr == "true") { SP_RESET = true; } // Ditto select (ex. Karate) if (_game.GetOptions().TryGetValue("SP_SELECT", out var spSelectStr) && spSelectStr == "true") { SP_SELECT = true; } }
public Atari2600(GameInfo game, byte[] rom, Atari2600.A2600Settings settings, Atari2600.A2600SyncSettings syncSettings) { var ser = new BasicServiceProvider(this); ServiceProvider = ser; _ram = new byte[128]; Settings = (A2600Settings)settings ?? new A2600Settings(); SyncSettings = (A2600SyncSettings)syncSettings ?? new A2600SyncSettings(); _controllerDeck = new Atari2600ControllerDeck(SyncSettings.Port1, SyncSettings.Port2); _leftDifficultySwitchPressed = SyncSettings.LeftDifficulty; _rightDifficultySwitchPressed = SyncSettings.RightDifficulty; Rom = rom; _game = game; if (!game.GetOptions().ContainsKey("m")) { game.AddOption("m", DetectMapper(rom)); } var romHashSHA1 = SHA1Checksum.ComputePrefixedHex(Rom); if (romHashSHA1 is RomChecksums.CongoBongo or RomChecksums.Tapper or RomChecksums.KangarooNotInGameDB) { game.RemoveOption("m"); game.AddOption("m", "F8_sega"); } Console.WriteLine("Game uses mapper " + game.GetOptions()["m"]); Console.WriteLine(romHashSHA1); RebootCore(); SetupMemoryDomains(); Tracer = new TraceBuffer(Cpu.TraceHeader); ser.Register <IDisassemblable>(Cpu); ser.Register <ITraceable>(Tracer); ser.Register <IVideoProvider>(_tia); ser.Register <ISoundProvider>(_dcfilter); ser.Register <IStatable>(new StateSerializer(SyncState)); }
public Gameboy(CoreComm comm, GameInfo game, byte[] file, GambatteSettings settings, GambatteSyncSettings syncSettings, bool deterministic) { var ser = new BasicServiceProvider(this); ser.Register <IDisassemblable>(_disassembler); ServiceProvider = ser; const string TRACE_HEADER = "LR35902: PC, opcode, registers (A, F, B, C, D, E, H, L, LY, SP, CY)"; Tracer = new TraceBuffer(TRACE_HEADER); ser.Register <ITraceable>(Tracer); InitMemoryCallbacks(); ThrowExceptionForBadRom(file); BoardName = MapperName(file); DeterministicEmulation = deterministic; GambatteState = LibGambatte.gambatte_create(); if (GambatteState == IntPtr.Zero) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???"); } Console.WriteLine(game.System); try { _syncSettings = syncSettings ?? new GambatteSyncSettings(); LibGambatte.LoadFlags flags = LibGambatte.LoadFlags.READONLY_SAV; switch (_syncSettings.ConsoleMode) { case GambatteSyncSettings.ConsoleModeType.GB: break; case GambatteSyncSettings.ConsoleModeType.GBC: flags |= LibGambatte.LoadFlags.CGB_MODE; break; case GambatteSyncSettings.ConsoleModeType.GBA: flags |= LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG; break; default: if (game.System == VSystemID.Raw.GBC) { flags |= LibGambatte.LoadFlags.CGB_MODE; } break; } if (game.System == VSystemID.Raw.SGB) { flags &= ~(LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG); flags |= LibGambatte.LoadFlags.SGB_MODE; IsSgb = true; } if (_syncSettings.MulticartCompat) { flags |= LibGambatte.LoadFlags.MULTICART_COMPAT; } IsCgb = (flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE; if (_syncSettings.EnableBIOS) { FirmwareID fwid = new( IsCgb ? "GBC" : "GB", IsSgb ? "SGB2" : _syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA ? "AGB" : "World"); var bios = comm.CoreFileProvider.GetFirmwareOrThrow(fwid, "BIOS Not Found, Cannot Load. Change SyncSettings to run without BIOS."); // https://github.com/TASEmulators/BizHawk/issues/2832 tho if (LibGambatte.gambatte_loadbiosbuf(GambatteState, bios, (uint)bios.Length) != 0) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbiosbuf)}() returned non-zero (bios error)"); } } else { if (DeterministicEmulation) // throw a warning if a movie is being recorded with the bios disabled { comm.ShowMessage("Detected disabled BIOS during movie recording. It is recommended to use a BIOS for movie recording. Change Sync Settings to run with a BIOS."); } flags |= LibGambatte.LoadFlags.NO_BIOS; } if (LibGambatte.gambatte_loadbuf(GambatteState, file, (uint)file.Length, flags) != 0) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbuf)}() returned non-zero (is this not a gb or gbc rom?)"); } if (IsSgb) { ResetStallTicks = 128 * (2 << 14); } else if (_syncSettings.EnableBIOS && (_syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA)) { ResetStallTicks = 485808; // GBA takes 971616 cycles to switch to CGB mode; CGB CPU is inactive during this time. } else { ResetStallTicks = 0; } // set real default colors (before anyone mucks with them at all) PutSettings((GambatteSettings)settings ?? new GambatteSettings()); InitSound(); Frame = 0; LagCount = 0; IsLagFrame = false; InputCallback = new LibGambatte.InputGetter(ControllerCallback); LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback); InitMemoryDomains(); RomDetails = $"{game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(file)}\r\n{MD5Checksum.ComputePrefixedHex(file)}\r\n"; byte[] buff = new byte[32]; LibGambatte.gambatte_romtitle(GambatteState, buff); string romname = System.Text.Encoding.ASCII.GetString(buff); Console.WriteLine("Core reported rom name: {0}", romname); if (!_syncSettings.EnableBIOS && IsCgb && IsCGBDMGMode()) // without a bios, we need to set the palette for cgbdmg ourselves { int[] cgbDmgColors = new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 }; if (file[0x14B] == 0x01 || (file[0x14B] == 0x33 && file[0x144] == '0' && file[0x145] == '1')) // Nintendo licencees get special palettes { cgbDmgColors = ColorsFromTitleHash(file); } ChangeDMGColors(cgbDmgColors); } if (!DeterministicEmulation && _syncSettings.RealTimeRTC) { LibGambatte.gambatte_settimemode(GambatteState, false); } if (DeterministicEmulation) { int[] rtcRegs = new int[11]; rtcRegs[(int)LibGambatte.RtcRegIndices.Dh] = 0; if (_syncSettings.InternalRTCOverflow) { rtcRegs[(int)LibGambatte.RtcRegIndices.Dh] |= 0x80; } if (_syncSettings.InternalRTCHalt) { rtcRegs[(int)LibGambatte.RtcRegIndices.Dh] |= 0x40; } rtcRegs[(int)LibGambatte.RtcRegIndices.Dh] |= _syncSettings.InternalRTCDays >> 8; rtcRegs[(int)LibGambatte.RtcRegIndices.Dl] = _syncSettings.InternalRTCDays & 0xFF; rtcRegs[(int)LibGambatte.RtcRegIndices.H] = (_syncSettings.InternalRTCHours < 0) ? (_syncSettings.InternalRTCHours + 0x20) : _syncSettings.InternalRTCHours; rtcRegs[(int)LibGambatte.RtcRegIndices.M] = (_syncSettings.InternalRTCMinutes < 0) ? (_syncSettings.InternalRTCMinutes + 0x40) : _syncSettings.InternalRTCMinutes; rtcRegs[(int)LibGambatte.RtcRegIndices.S] = (_syncSettings.InternalRTCSeconds < 0) ? (_syncSettings.InternalRTCSeconds + 0x40) : _syncSettings.InternalRTCSeconds; rtcRegs[(int)LibGambatte.RtcRegIndices.C] = _syncSettings.InternalRTCCycles; rtcRegs[(int)LibGambatte.RtcRegIndices.Dh_L] = 0; if (_syncSettings.LatchedRTCOverflow) { rtcRegs[(int)LibGambatte.RtcRegIndices.Dh_L] |= 0x80; } if (_syncSettings.LatchedRTCHalt) { rtcRegs[(int)LibGambatte.RtcRegIndices.Dh_L] |= 0x40; } rtcRegs[(int)LibGambatte.RtcRegIndices.Dh_L] |= _syncSettings.LatchedRTCDays >> 8; rtcRegs[(int)LibGambatte.RtcRegIndices.Dl_L] = _syncSettings.LatchedRTCDays & 0xFF; rtcRegs[(int)LibGambatte.RtcRegIndices.H_L] = _syncSettings.LatchedRTCHours; rtcRegs[(int)LibGambatte.RtcRegIndices.M_L] = _syncSettings.LatchedRTCMinutes; rtcRegs[(int)LibGambatte.RtcRegIndices.S_L] = _syncSettings.LatchedRTCSeconds; LibGambatte.gambatte_setrtcregs(GambatteState, rtcRegs); } LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset); LibGambatte.gambatte_setcartbuspulluptime(GambatteState, (uint)_syncSettings.CartBusPullUpTime); _cdCallback = new LibGambatte.CDCallback(CDCallbackProc); ControllerDefinition = CreateControllerDefinition(IsSgb, _syncSettings.FrameLength is GambatteSyncSettings.FrameLengthType.UserDefinedFrames); NewSaveCoreSetBuff(); } catch { Dispose(); throw; } }
public Sameboy(CoreComm comm, GameInfo game, byte[] file, SameboySettings settings, SameboySyncSettings syncSettings, bool deterministic) { _serviceProvider = new BasicServiceProvider(this); _settings = settings ?? new SameboySettings(); _syncSettings = syncSettings ?? new SameboySyncSettings(); var model = _syncSettings.ConsoleMode; if (model is SameboySyncSettings.GBModel.Auto) { model = game.System == VSystemID.Raw.GBC ? SameboySyncSettings.GBModel.GB_MODEL_CGB_E : SameboySyncSettings.GBModel.GB_MODEL_DMG_B; } IsCgb = model >= SameboySyncSettings.GBModel.GB_MODEL_CGB_0; byte[] bios; if (_syncSettings.EnableBIOS) { FirmwareID fwid = new( IsCgb ? "GBC" : "GB", _syncSettings.ConsoleMode is SameboySyncSettings.GBModel.GB_MODEL_AGB ? "AGB" : "World"); bios = comm.CoreFileProvider.GetFirmwareOrThrow(fwid, "BIOS Not Found, Cannot Load. Change SyncSettings to run without BIOS."); } else { bios = Util.DecompressGzipFile(new MemoryStream(IsCgb ? _syncSettings.ConsoleMode is SameboySyncSettings.GBModel.GB_MODEL_AGB ? Resources.SameboyAgbBoot.Value : Resources.SameboyCgbBoot.Value : Resources.SameboyDmgBoot.Value)); } DeterministicEmulation = false; bool realtime = true; if (!_syncSettings.UseRealTime || deterministic) { realtime = false; DeterministicEmulation = true; } SameboyState = LibSameboy.sameboy_create(file, file.Length, bios, bios.Length, model, realtime); InitMemoryDomains(); InitMemoryCallbacks(); _samplecb = QueueSample; LibSameboy.sameboy_setsamplecallback(SameboyState, _samplecb); _inputcb = InputCallback; LibSameboy.sameboy_setinputcallback(SameboyState, _inputcb); _tracecb = MakeTrace; LibSameboy.sameboy_settracecallback(SameboyState, null); LibSameboy.sameboy_setscanlinecallback(SameboyState, null, 0); LibSameboy.sameboy_setprintercallback(SameboyState, null); const string TRACE_HEADER = "SM83: PC, opcode, registers (A, F, B, C, D, E, H, L, SP, LY, CY)"; Tracer = new TraceBuffer(TRACE_HEADER); _serviceProvider.Register(Tracer); _disassembler = new Gameboy.GBDisassembler(); _serviceProvider.Register <IDisassemblable>(_disassembler); PutSettings(_settings); _stateBuf = new byte[LibSameboy.sameboy_statelen(SameboyState)]; RomDetails = $"{game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(file)}\r\n{MD5Checksum.ComputePrefixedHex(file)}\r\n"; BoardName = MapperName(file); _hasAcc = BoardName is "MBC7 ROM+ACCEL+EEPROM"; ControllerDefinition = Gameboy.Gameboy.CreateControllerDefinition(sgb: false, sub: false, tilt: _hasAcc); LibSameboy.sameboy_setrtcdivisoroffset(SameboyState, _syncSettings.RTCDivisorOffset); CycleCount = 0; }
/// <exception cref="Exception"><paramref name="file"/> does not exist</exception> public RomGame(HawkFile file, string patch) { if (!file.Exists) { throw new Exception("The file needs to exist, yo."); } Extension = file.Extension.ToUpperInvariant(); var stream = file.GetStream(); int fileLength = (int)stream.Length; // read the entire contents of the file into memory. // unfortunate in the case of large files, but that's what we've got to work with for now. // if we're offset exactly 512 bytes from a 1024-byte boundary, // assume we have a header of that size. Otherwise, assume it's just all rom. // Other 'recognized' header sizes may need to be added. int headerOffset = fileLength % BankSize; if (headerOffset.In(0, 128, 512) == false) { Console.WriteLine("ROM was not a multiple of 1024 bytes, and not a recognized header size: {0}. Assume it's purely ROM data.", headerOffset); headerOffset = 0; } else if (headerOffset > 0) { Console.WriteLine("Assuming header of {0} bytes.", headerOffset); } // read the entire file into FileData. FileData = new byte[fileLength]; stream.Position = 0; stream.Read(FileData, 0, fileLength); string SHA1_check = SHA1Checksum.ComputePrefixedHex(FileData); // if there was no header offset, RomData is equivalent to FileData // (except in cases where the original interleaved file data is necessary.. in that case we'll have problems.. // but this whole architecture is not going to withstand every peculiarity and be fast as well. if (headerOffset == 0) { RomData = FileData; } else if (file.Extension == ".dsk" || file.Extension == ".tap" || file.Extension == ".tzx" || file.Extension == ".pzx" || file.Extension == ".csw" || file.Extension == ".wav" || file.Extension == ".cdt") { // these are not roms. unfortunately if treated as such there are certain edge-cases // where a header offset is detected. This should mitigate this issue until a cleaner solution is found // (-Asnivor) RomData = FileData; } else if (SHA1_check == Flappy_Bird_INTV || SHA1_check == Minehunter_INTV) { // several INTV games have sizes that are multiples of 512 bytes Console.WriteLine("False positive detected in Header Check, using entire file."); RomData = FileData; } else { // if there was a header offset, read the whole file into FileData and then copy it into RomData (this is unfortunate, in case RomData isn't needed) int romLength = fileLength - headerOffset; RomData = new byte[romLength]; Buffer.BlockCopy(FileData, headerOffset, RomData, 0, romLength); } if (file.Extension == ".smd") { RomData = DeInterleaveSMD(RomData); } if (file.Extension == ".z64" || file.Extension == ".n64" || file.Extension == ".v64") { // Use a simple magic number to detect N64 rom format, then byteswap the ROM to ensure a consistent endianness/order RomData = RomData[0] switch { 0x37 => EndiannessUtils.ByteSwap16(RomData), // V64 format (byte swapped) 0x40 => EndiannessUtils.ByteSwap32(RomData), // N64 format (word swapped) _ => RomData // Z64 format (no swap), or something unexpected; in either case do nothing }; } // note: this will be taking several hashes, of a potentially large amount of data.. yikes! GameInfo = Database.GetGameInfo(RomData, file.Name); if (GameInfo.NotInDatabase && headerOffset == 128 && file.Extension == ".a78") { // if the game is not in the DB, add the header back in so the core can use it // for now only .A78 games, but probably should be for other systems as well RomData = FileData; } CheckForPatchOptions(); if (patch != null) { using var patchFile = new HawkFile(patch); patchFile.BindFirstOf(".ips"); if (patchFile.IsBound) { RomData = IPS.Patch(RomData, patchFile.GetStream()); } } }
public O2Hawk(CoreComm comm, GameInfo game, byte[] rom, O2Settings settings, O2SyncSettings syncSettings) { var ser = new BasicServiceProvider(this); cpu = new I8048 { ReadMemory = ReadMemory, WriteMemory = WriteMemory, PeekMemory = PeekMemory, DummyReadMemory = ReadMemory, ReadPort = ReadPort, WritePort = WritePort, OnExecFetch = ExecFetch, }; _settings = settings ?? new O2Settings(); _syncSettings = syncSettings ?? new O2SyncSettings(); is_G7400 = _syncSettings.G7400_Enable; _controllerDeck = new O2HawkControllerDeck("O2 Controller", "O2 Controller", is_G7400); _bios = comm.CoreFileProvider.GetFirmwareOrThrow(new("O2", is_G7400 ? "BIOS-G7400" : "BIOS-O2"), "BIOS Not Found, Cannot Load"); Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); Console.WriteLine(MD5Checksum.ComputePrefixedHex(rom)); Console.WriteLine(SHA1Checksum.ComputePrefixedHex(rom)); _rom = rom; if (game["XROM"]) { is_XROM = true; } Setup_Mapper(); _frameHz = 60; ser.Register <IVideoProvider>(this); ServiceProvider = ser; _tracer = new TraceBuffer(cpu.TraceHeader); ser.Register(_tracer); ser.Register <IStatable>(new StateSerializer(SyncState)); SetupMemoryDomains(); cpu.SetCallbacks(ReadMemory, PeekMemory, PeekMemory, WriteMemory); // set up differences between PAL and NTSC systems if ((game.Region == "US" || game.Region == "EU-US" || game.Region == null) && !is_G7400) { is_pal = false; pic_height = 240; _frameHz = 60; ppu = new NTSC_PPU(); } else { is_pal = true; pic_height = 240; _frameHz = 50; ppu = new PAL_PPU(); } ppu.Core = this; ppu.set_region(is_pal, is_G7400); ser.Register <ISoundProvider>(ppu); _vidbuffer = new int[372 * pic_height]; frame_buffer = new int[320 * pic_height]; HardReset(); }
public Gameboy(CoreComm comm, GameInfo game, byte[] file, GambatteSettings settings, GambatteSyncSettings syncSettings, bool deterministic) { var ser = new BasicServiceProvider(this); ser.Register <IDisassemblable>(_disassembler); ServiceProvider = ser; const string TRACE_HEADER = "LR35902: PC, opcode, registers (A, F, B, C, D, E, H, L, LY, SP, CY)"; Tracer = new TraceBuffer(TRACE_HEADER); ser.Register <ITraceable>(Tracer); InitMemoryCallbacks(); DeterministicEmulation = deterministic; GambatteState = LibGambatte.gambatte_create(); if (GambatteState == IntPtr.Zero) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_create)}() returned null???"); } Console.WriteLine(game.System); try { _syncSettings = syncSettings ?? new GambatteSyncSettings(); LibGambatte.LoadFlags flags = LibGambatte.LoadFlags.READONLY_SAV; switch (_syncSettings.ConsoleMode) { case GambatteSyncSettings.ConsoleModeType.GB: break; case GambatteSyncSettings.ConsoleModeType.GBC: flags |= LibGambatte.LoadFlags.CGB_MODE; break; case GambatteSyncSettings.ConsoleModeType.GBA: flags |= LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG; break; default: if (game.System == VSystemID.Raw.GBC) { flags |= LibGambatte.LoadFlags.CGB_MODE; } break; } if (game.System == VSystemID.Raw.SGB) { flags &= ~(LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG); flags |= LibGambatte.LoadFlags.SGB_MODE; IsSgb = true; } IsCgb = (flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE; if (_syncSettings.EnableBIOS) { FirmwareID fwid = new( IsCgb ? "GBC" : "GB", IsSgb ? "SGB2" : _syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA ? "AGB" : "World"); var bios = comm.CoreFileProvider.GetFirmwareOrThrow(fwid, "BIOS Not Found, Cannot Load. Change SyncSettings to run without BIOS."); // https://github.com/TASEmulators/BizHawk/issues/2832 tho if (LibGambatte.gambatte_loadbiosbuf(GambatteState, bios, (uint)bios.Length) != 0) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbiosbuf)}() returned non-zero (bios error)"); } } else { if (DeterministicEmulation) // throw a warning if a movie is being recorded with the bios disabled { comm.ShowMessage("Detected disabled BIOS during movie recording. It is recommended to use a BIOS for movie recording. Change Sync Settings to run with a BIOS."); } flags |= LibGambatte.LoadFlags.NO_BIOS; } if (LibGambatte.gambatte_loadbuf(GambatteState, file, (uint)file.Length, flags) != 0) { throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbuf)}() returned non-zero (is this not a gb or gbc rom?)"); } if (IsSgb) { ResetStallTicks = 128 * (2 << 14); } else if (_syncSettings.EnableBIOS && (_syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA)) { ResetStallTicks = 485808; // GBA takes 971616 cycles to switch to CGB mode; CGB CPU is inactive during this time. } else { ResetStallTicks = 0; } // set real default colors (before anyone mucks with them at all) PutSettings(settings ?? new GambatteSettings()); InitSound(); Frame = 0; LagCount = 0; IsLagFrame = false; InputCallback = new LibGambatte.InputGetter(ControllerCallback); LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback, IntPtr.Zero); InitMemoryDomains(); byte[] mbcBuf = new byte[32]; uint rambanks = 0; uint rombanks = 0; uint crc = 0; uint headerchecksumok = 0; LibGambatte.gambatte_pakinfo(GambatteState, mbcBuf, ref rambanks, ref rombanks, ref crc, ref headerchecksumok); byte[] romNameBuf = new byte[32]; LibGambatte.gambatte_romtitle(GambatteState, romNameBuf); string romname = Encoding.ASCII.GetString(romNameBuf); RomDetails = $"{game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(file)}\r\n{MD5Checksum.ComputePrefixedHex(file)}\r\n\r\n"; BoardName = Encoding.ASCII.GetString(mbcBuf); RomDetails += $"Core reported Header Name: {romname}\r\n"; RomDetails += $"Core reported RAM Banks: {rambanks}\r\n"; RomDetails += $"Core reported ROM Banks: {rombanks}\r\n"; RomDetails += $"Core reported CRC32: {crc:X8}\r\n"; RomDetails += $"Core reported Header Checksum Status: {(headerchecksumok != 0 ? "OK" : "BAD")}\r\n"; if (_syncSettings.EnableBIOS && headerchecksumok == 0) { comm.ShowMessage("Core reports the header checksum is bad. This ROM will not boot with the official BIOS.\n" + "Disable BIOS in sync settings to boot this game"); } if (!_syncSettings.EnableBIOS && IsCgb && IsCGBDMGMode()) // without a bios, we need to set the palette for cgbdmg ourselves { int[] cgbDmgColors = new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 }; if (file[0x14B] == 0x01 || (file[0x14B] == 0x33 && file[0x144] == '0' && file[0x145] == '1')) // Nintendo licencees get special palettes { cgbDmgColors = ColorsFromTitleHash(file); } ChangeDMGColors(cgbDmgColors); } if (!DeterministicEmulation && _syncSettings.RealTimeRTC) { LibGambatte.gambatte_settimemode(GambatteState, false); } if (DeterministicEmulation) { ulong dividers = _syncSettings.InitialTime * (0x400000UL + (ulong)_syncSettings.RTCDivisorOffset) / 2UL; LibGambatte.gambatte_settime(GambatteState, dividers); } LibGambatte.gambatte_setrtcdivisoroffset(GambatteState, _syncSettings.RTCDivisorOffset); LibGambatte.gambatte_setcartbuspulluptime(GambatteState, (uint)_syncSettings.CartBusPullUpTime); _cdCallback = new LibGambatte.CDCallback(CDCallbackProc); ControllerDefinition = CreateControllerDefinition(sgb: IsSgb, sub: _syncSettings.FrameLength is GambatteSyncSettings.FrameLengthType.UserDefinedFrames, tilt: false); NewSaveCoreSetBuff(); } catch { Dispose(); throw; } }
public VectrexHawk(CoreComm comm, byte[] rom, VectrexHawk.VectrexSyncSettings syncSettings) { var ser = new BasicServiceProvider(this); cpu = new MC6809 { ReadMemory = ReadMemory, WriteMemory = WriteMemory, PeekMemory = PeekMemory, DummyReadMemory = ReadMemory, OnExecFetch = ExecFetch, }; audio = new Audio(); ppu = new PPU(); serialport = new SerialPort(); _settings = new object(); // TODO: wtf is this _syncSettings = (VectrexSyncSettings)syncSettings ?? new VectrexSyncSettings(); _controllerDeck = new VectrexHawkControllerDeck(_syncSettings.Port1, _syncSettings.Port2); /*var Bios =*/ _bios = comm.CoreFileProvider.GetFirmwareOrThrow(new("VEC", "Bios"), "BIOS Not Found, Cannot Load"); /*var Mine =*/ minestorm = comm.CoreFileProvider.GetFirmwareOrThrow(new("VEC", "Minestorm"), "Minestorm Not Found, Cannot Load"); var romHashSHA1 = SHA1Checksum.ComputePrefixedHex(rom); Console.WriteLine(romHashSHA1); _rom = rom; // If the game is minstorm, then no cartridge is inserted, retun 0xFF if (romHashSHA1 == RomChecksums.Minestorm) { _rom = new byte[0x8000]; for (int i = 0; i < 0x8000; i++) { _rom[i] = 0xFF; } } // mirror games that are too small if (_rom.Length < 0x8000) { _rom = new byte[0x8000]; for (int i = 0; i < 0x8000 / rom.Length; i++) { for (int j = 0; j < rom.Length; j++) { _rom[j + i * rom.Length] = rom[j]; } } } // RAM appears to power up to either random values or 0xFF, otherwise all the asteroids in minestorm are on the same side if RAM[0x7E]=0 for (int i = 0; i < RAM.Length; i++) { RAM[i] = 0xFF; } Setup_Mapper(); _frameHz = 50; audio.Core = this; ppu.Core = this; serialport.Core = this; ser.Register <IVideoProvider>(this); ser.Register <ISoundProvider>(audio); ServiceProvider = ser; _settings = new object(); // TODO: wtf is this _syncSettings = (VectrexSyncSettings)syncSettings ?? new VectrexSyncSettings(); _tracer = new TraceBuffer(cpu.TraceHeader); ser.Register <ITraceable>(_tracer); ser.Register <IStatable>(new StateSerializer(SyncState)); SetupMemoryDomains(); HardReset(); cpu.SetCallbacks(ReadMemory, PeekMemory, PeekMemory, WriteMemory); }