public void NativeInit(LibsnesApi api) { for (int i = 0; i < 2; i++) { api.SetInputPortBeforeInit(i, _ports[i].PortType); } }
public SNESGraphicsDecoder(LibsnesApi api, SnesColors.ColorType pal) { this.api = api; colortable = SnesColors.GetLUT(pal); IntPtr block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.VRAM); vram = (byte*)block; vram16 = (ushort*)block; block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CGRAM); cgram = (ushort*)block; block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.OAM); oam = (byte*)block; }
public SNESGraphicsDecoder(LibsnesApi api, SnesColors.ColorType pal) { this.api = api; colortable = SnesColors.GetLUT(pal); IntPtr block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.VRAM); vram = (byte *)block; vram16 = (ushort *)block; block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CGRAM); cgram = (ushort *)block; block = (IntPtr)api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.OAM); oam = (byte *)block; }
public LibsnesCore(GameInfo game, byte[] romData, byte[] xmlData, CoreComm comm, object settings, object syncSettings) { var ser = new BasicServiceProvider(this); ServiceProvider = ser; _tracer = new TraceBuffer { Header = "65816: PC, mnemonic, operands, registers (A, X, Y, S, D, DB, flags (NVMXDIZC), V, H)" }; ser.Register <IDisassemblable>(new W65816_DisassemblerService()); _game = game; CoreComm = comm; byte[] sgbRomData = null; if (game["SGB"]) { if ((romData[0x143] & 0xc0) == 0xc0) { throw new CGBNotSupportedException(); } sgbRomData = CoreComm.CoreFileProvider.GetFirmware("SNES", "Rom_SGB", true, "SGB Rom is required for SGB emulation."); game.FirmwareHash = sgbRomData.HashSHA1(); } _settings = (SnesSettings)settings ?? new SnesSettings(); _syncSettings = (SnesSyncSettings)syncSettings ?? new SnesSyncSettings(); // TODO: pass profile here Api = new LibsnesApi(CoreComm.CoreFileProvider.DllPath()) { ReadHook = ReadHook, ExecHook = ExecHook, WriteHook = WriteHook }; ScanlineHookManager = new MyScanlineHookManager(this); _controllerDeck = new LibsnesControllerDeck(_syncSettings); _controllerDeck.NativeInit(Api); Api.CMD_init(); Api.QUERY_set_path_request(snes_path_request); _scanlineStartCb = new LibsnesApi.snes_scanlineStart_t(snes_scanlineStart); _tracecb = new LibsnesApi.snes_trace_t(snes_trace); _soundcb = new LibsnesApi.snes_audio_sample_t(snes_audio_sample); // start up audio resampler InitAudio(); ser.Register <ISoundProvider>(_resampler); // strip header if ((romData?.Length & 0x7FFF) == 512) { var newData = new byte[romData.Length - 512]; Array.Copy(romData, 512, newData, 0, newData.Length); romData = newData; } if (game["SGB"]) { IsSGB = true; SystemId = "SNES"; ser.Register <IBoardInfo>(new SGBBoardInfo()); _currLoadParams = new LoadParams() { type = LoadParamType.SuperGameBoy, rom_xml = null, rom_data = sgbRomData, rom_size = (uint)sgbRomData.Length, dmg_data = romData, }; if (!LoadCurrent()) { throw new Exception("snes_load_cartridge_normal() failed"); } } else { // we may need to get some information out of the cart, even during the following bootup/load process if (xmlData != null) { _romxml = new XmlDocument(); _romxml.Load(new MemoryStream(xmlData)); // bsnes wont inspect the xml to load the necessary sfc file. // so, we have to do that here and pass it in as the romData :/ if (_romxml["cartridge"]?["rom"] != null) { romData = File.ReadAllBytes(CoreComm.CoreFileProvider.PathSubfile(_romxml["cartridge"]["rom"].Attributes["name"].Value)); } else { throw new Exception("Could not find rom file specification in xml file. Please check the integrity of your xml file"); } } SystemId = "SNES"; _currLoadParams = new LoadParams { type = LoadParamType.Normal, xml_data = xmlData, rom_data = romData }; if (!LoadCurrent()) { throw new Exception("snes_load_cartridge_normal() failed"); } } if (Api.Region == LibsnesApi.SNES_REGION.NTSC) { // similar to what aviout reports from snes9x and seems logical from bsnes first principles. bsnes uses that numerator (ntsc master clockrate) for sure. VsyncNumerator = 21477272; VsyncDenominator = 4 * 341 * 262; } else { // http://forums.nesdev.com/viewtopic.php?t=5367&start=19 VsyncNumerator = 21281370; VsyncDenominator = 4 * 341 * 312; } Api.CMD_power(); SetupMemoryDomains(romData, sgbRomData); if (CurrentProfile == "Compatibility") { ser.Register <ITraceable>(_tracer); } Api.QUERY_set_path_request(null); Api.QUERY_set_video_refresh(snes_video_refresh); Api.QUERY_set_input_poll(snes_input_poll); Api.QUERY_set_input_state(snes_input_state); Api.QUERY_set_input_notify(snes_input_notify); Api.QUERY_set_audio_sample(_soundcb); Api.Seal(); RefreshPalette(); }
public static ScreenInfo GetScreenInfo(LibsnesApi api) { var si = new ScreenInfo(); si.Mode1_BG3_Priority = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_PRIORITY) == 1; si.OBSEL_Size = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_SIZE); si.OBSEL_NameSel = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMESEL); si.OBSEL_NameBase = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMEBASE); si.ObjSizeBounds = ObjSizes[si.OBSEL_Size,1]; int square = Math.Max(si.ObjSizeBounds.Width, si.ObjSizeBounds.Height); si.ObjSizeBoundsSquare = new Dimensions(square, square); si.OBJTable0Addr = si.OBSEL_NameBase << 14; si.OBJTable1Addr = (si.OBJTable0Addr + ((si.OBSEL_NameSel + 1) << 13)) & 0xFFFF; si.SETINI_Mode7ExtBG = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_MODE7_EXTBG) == 1; si.SETINI_HiRes = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_HIRES) == 1; si.SETINI_Overscan = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OVERSCAN) == 1; si.SETINI_ObjInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OBJ_INTERLACE) == 1; si.SETINI_ScreenInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_SCREEN_INTERLACE) == 1; si.CGWSEL_ColorMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORMASK); si.CGWSEL_ColorSubMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORSUBMASK); si.CGWSEL_AddSubMode = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_ADDSUBMODE); si.CGWSEL_DirectColor = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_DIRECTCOLOR) == 1; si.CGADSUB_AddSub = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_MODE); si.CGADSUB_Half = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_HALF) == 1; si.OBJ_MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_OBJ) == 1; si.OBJ_SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_OBJ) == 1; si.OBJ_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_OBJ) == 1; si.BK_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BACKDROP) == 1; si.Mode.MODE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG_MODE); si.BG.BG1.Bpp = ModeBpps[si.Mode.MODE, 0]; si.BG.BG2.Bpp = ModeBpps[si.Mode.MODE, 1]; si.BG.BG3.Bpp = ModeBpps[si.Mode.MODE, 2]; si.BG.BG4.Bpp = ModeBpps[si.Mode.MODE, 3]; //initial setting of mode type (derived from bpp table.. mode7 bg types will be fixed up later) for(int i=1;i<=4;i++) si.BG[i].BGMode = si.BG[i].Bpp == 0 ? BGMode.Unavailable : BGMode.Text; si.BG.BG1.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TILESIZE); si.BG.BG2.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TILESIZE); si.BG.BG3.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TILESIZE); si.BG.BG4.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TILESIZE); si.BG.BG1.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCSIZE); si.BG.BG2.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCSIZE); si.BG.BG3.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCSIZE); si.BG.BG4.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCSIZE); si.BG.BG1.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCADDR); si.BG.BG2.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCADDR); si.BG.BG3.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCADDR); si.BG.BG4.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCADDR); si.BG.BG1.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TDADDR); si.BG.BG2.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TDADDR); si.BG.BG3.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TDADDR); si.BG.BG4.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TDADDR); si.BG.BG1.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG1) == 1; si.BG.BG2.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG2) == 1; si.BG.BG3.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG3) == 1; si.BG.BG4.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG4) == 1; si.BG.BG1.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG1) == 1; si.BG.BG2.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG2) == 1; si.BG.BG3.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG3) == 1; si.BG.BG4.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG4) == 1; si.BG.BG1.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG1) == 1; si.BG.BG2.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG2) == 1; si.BG.BG3.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG3) == 1; si.BG.BG4.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG4) == 1; si.BG.BG1.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1HOFS); si.BG.BG1.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1VOFS); si.BG.BG2.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2HOFS); si.BG.BG2.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2VOFS); si.BG.BG3.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3HOFS); si.BG.BG3.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3VOFS); si.BG.BG4.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4HOFS); si.BG.BG4.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4VOFS); si.M7HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7HOFS); si.M7VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7VOFS); si.M7A = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7A); si.M7B = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7B); si.M7C = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7C); si.M7D = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7D); si.M7X = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7X); si.M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y); si.M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y); si.M7SEL_REPEAT = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_REPEAT); si.M7SEL_HFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_HFLIP)!=0; si.M7SEL_VFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_VFLIP)!=0; for (int i = 1; i <= 4; i++) { si.BG[i].Mode = si.Mode.MODE; si.BG[i].TiledataAddr = si.BG[i].TDADDR << 13; si.BG[i].ScreenAddr = si.BG[i].SCADDR << 9; } //fixup irregular things for mode 7 if (si.Mode.MODE == 7) { si.BG.BG1.TiledataAddr = 0; si.BG.BG1.ScreenAddr = 0; if (si.CGWSEL_DirectColor) { si.BG.BG1.BGMode = BGMode.Mode7DC; } else si.BG.BG1.BGMode = BGMode.Mode7; if (si.SETINI_Mode7ExtBG) { si.BG.BG2.BGMode = BGMode.Mode7Ext; si.BG.BG2.Bpp = 7; si.BG.BG2.TiledataAddr = 0; si.BG.BG2.ScreenAddr = 0; } } //determine which colors each BG could use switch (si.Mode.MODE) { case 0: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG2.PaletteSelection = new PaletteSelection(32, 32); si.BG.BG3.PaletteSelection = new PaletteSelection(64, 32); si.BG.BG4.PaletteSelection = new PaletteSelection(96, 32); break; case 1: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 2: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 3: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 4: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 5: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 6: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 7: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; } return si; }
public LibsnesCore(GameInfo game, byte[] romData, bool deterministicEmulation, byte[] xmlData, CoreComm comm, object Settings, object SyncSettings) { CoreComm = comm; byte[] sgbRomData = null; if (game["SGB"]) { if ((romData[0x143] & 0xc0) == 0xc0) throw new CGBNotSupportedException(); sgbRomData = CoreComm.CoreFileProvider.GetFirmware("SNES", "Rom_SGB", true, "SGB Rom is required for SGB emulation."); game.FirmwareHash = sgbRomData.HashSHA1(); } this.Settings = (SnesSettings)Settings ?? new SnesSettings(); this.SyncSettings = (SnesSyncSettings)SyncSettings ?? new SnesSyncSettings(); api = new LibsnesApi(GetExePath()); api.CMD_init(); api.ReadHook = ReadHook; api.ExecHook = ExecHook; api.WriteHook = WriteHook; ScanlineHookManager = new MyScanlineHookManager(this); api.CMD_init(); api.QUERY_set_video_refresh(snes_video_refresh); api.QUERY_set_input_poll(snes_input_poll); api.QUERY_set_input_state(snes_input_state); api.QUERY_set_input_notify(snes_input_notify); api.QUERY_set_path_request(snes_path_request); scanlineStart_cb = new LibsnesApi.snes_scanlineStart_t(snes_scanlineStart); tracecb = new LibsnesApi.snes_trace_t(snes_trace); soundcb = new LibsnesApi.snes_audio_sample_t(snes_audio_sample); api.QUERY_set_audio_sample(soundcb); RefreshPalette(); // start up audio resampler InitAudio(); //strip header if(romData != null) if ((romData.Length & 0x7FFF) == 512) { var newData = new byte[romData.Length - 512]; Array.Copy(romData, 512, newData, 0, newData.Length); romData = newData; } if (game["SGB"]) { IsSGB = true; SystemId = "SNES"; BoardName = "SGB"; CurrLoadParams = new LoadParams() { type = LoadParamType.SuperGameBoy, rom_xml = null, rom_data = sgbRomData, rom_size = (uint)sgbRomData.Length, dmg_xml = null, dmg_data = romData, dmg_size = (uint)romData.Length }; if (!LoadCurrent()) throw new Exception("snes_load_cartridge_normal() failed"); } else { //we may need to get some information out of the cart, even during the following bootup/load process if (xmlData != null) { romxml = new System.Xml.XmlDocument(); romxml.Load(new MemoryStream(xmlData)); //bsnes wont inspect the xml to load the necessary sfc file. //so, we have to do that here and pass it in as the romData :/ if (romxml["cartridge"] != null && romxml["cartridge"]["rom"] != null) romData = File.ReadAllBytes(CoreComm.CoreFileProvider.PathSubfile(romxml["cartridge"]["rom"].Attributes["name"].Value)); else throw new Exception("Could not find rom file specification in xml file. Please check the integrity of your xml file"); } SystemId = "SNES"; CurrLoadParams = new LoadParams() { type = LoadParamType.Normal, xml_data = xmlData, rom_data = romData }; if(!LoadCurrent()) throw new Exception("snes_load_cartridge_normal() failed"); } if (api.QUERY_get_region() == LibsnesApi.SNES_REGION.NTSC) { //similar to what aviout reports from snes9x and seems logical from bsnes first principles. bsnes uses that numerator (ntsc master clockrate) for sure. CoreComm.VsyncNum = 21477272; CoreComm.VsyncDen = 4 * 341 * 262; } else { //http://forums.nesdev.com/viewtopic.php?t=5367&start=19 CoreComm.VsyncNum = 21281370; CoreComm.VsyncDen = 4 * 341 * 312; } CoreComm.CpuTraceAvailable = true; api.CMD_power(); SetupMemoryDomains(romData,sgbRomData); DeterministicEmulation = deterministicEmulation; if (DeterministicEmulation) // save frame-0 savestate now { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(CoreSaveState()); bw.Write(true); // framezero, so no controller follows and don't frameadvance on load // hack: write fake dummy controller info bw.Write(new byte[536]); bw.Close(); savestatebuff = ms.ToArray(); } }
// ----- Client Debugging API stuff ----- unsafe MemoryDomain MakeMemoryDomain(string name, LibsnesApi.SNES_MEMORY id, MemoryDomain.Endian endian) { int size = api.QUERY_get_memory_size(id); int mask = size - 1; bool pow2 = Util.IsPowerOfTwo(size); //if this type of memory isnt available, dont make the memory domain (most commonly save ram) if (size == 0) return null; byte* blockptr = api.QUERY_get_memory_data(id); MemoryDomain md; if(id == LibsnesApi.SNES_MEMORY.OAM) { //OAM is actually two differently sized banks of memory which arent truly considered adjacent. //maybe a better way to visualize it is with an empty bus and adjacent banks //so, we just throw away everything above its size of 544 bytes if (size != 544) throw new InvalidOperationException("oam size isnt 544 bytes.. wtf?"); md = new MemoryDomain(name, size, endian, (addr) => (addr < 544) ? blockptr[addr] : (byte)0x00, (addr, value) => { if (addr < 544) blockptr[addr] = value; } ); } else if(pow2) md = new MemoryDomain(name, size, endian, (addr) => blockptr[addr & mask], (addr, value) => blockptr[addr & mask] = value); else md = new MemoryDomain(name, size, endian, (addr) => blockptr[addr % size], (addr, value) => blockptr[addr % size] = value); _memoryDomains.Add(md); return md; }
public static ScreenInfo GetScreenInfo(LibsnesApi api) { var si = new ScreenInfo(); si.Mode1_BG3_Priority = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_PRIORITY) == 1; si.OBSEL_Size = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_SIZE); si.OBSEL_NameSel = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMESEL); si.OBSEL_NameBase = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.OBSEL_NAMEBASE); si.ObjSizeBounds = ObjSizes[si.OBSEL_Size, 1]; int square = Math.Max(si.ObjSizeBounds.Width, si.ObjSizeBounds.Height); si.ObjSizeBoundsSquare = new Dimensions(square, square); si.OBJTable0Addr = si.OBSEL_NameBase << 14; si.OBJTable1Addr = (si.OBJTable0Addr + ((si.OBSEL_NameSel + 1) << 13)) & 0xFFFF; si.SETINI_Mode7ExtBG = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_MODE7_EXTBG) == 1; si.SETINI_HiRes = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_HIRES) == 1; si.SETINI_Overscan = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OVERSCAN) == 1; si.SETINI_ObjInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_OBJ_INTERLACE) == 1; si.SETINI_ScreenInterlace = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.SETINI_SCREEN_INTERLACE) == 1; si.CGWSEL_ColorMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORMASK); si.CGWSEL_ColorSubMask = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_COLORSUBMASK); si.CGWSEL_AddSubMode = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_ADDSUBMODE); si.CGWSEL_DirectColor = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGWSEL_DIRECTCOLOR) == 1; si.CGADSUB_AddSub = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_MODE); si.CGADSUB_Half = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_HALF) == 1; si.OBJ_MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_OBJ) == 1; si.OBJ_SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_OBJ) == 1; si.OBJ_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_OBJ) == 1; si.BK_MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BACKDROP) == 1; si.Mode.MODE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG_MODE); si.BG.BG1.Bpp = ModeBpps[si.Mode.MODE, 0]; si.BG.BG2.Bpp = ModeBpps[si.Mode.MODE, 1]; si.BG.BG3.Bpp = ModeBpps[si.Mode.MODE, 2]; si.BG.BG4.Bpp = ModeBpps[si.Mode.MODE, 3]; //initial setting of mode type (derived from bpp table.. mode7 bg types will be fixed up later) for (int i = 1; i <= 4; i++) { si.BG[i].BGMode = si.BG[i].Bpp == 0 ? BGMode.Unavailable : BGMode.Text; } si.BG.BG1.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TILESIZE); si.BG.BG2.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TILESIZE); si.BG.BG3.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TILESIZE); si.BG.BG4.TILESIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TILESIZE); si.BG.BG1.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCSIZE); si.BG.BG2.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCSIZE); si.BG.BG3.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCSIZE); si.BG.BG4.SCSIZE = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCSIZE); si.BG.BG1.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_SCADDR); si.BG.BG2.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_SCADDR); si.BG.BG3.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_SCADDR); si.BG.BG4.SCADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_SCADDR); si.BG.BG1.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1_TDADDR); si.BG.BG2.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2_TDADDR); si.BG.BG3.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3_TDADDR); si.BG.BG4.TDADDR = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4_TDADDR); si.BG.BG1.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG1) == 1; si.BG.BG2.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG2) == 1; si.BG.BG3.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG3) == 1; si.BG.BG4.MainEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TM_BG4) == 1; si.BG.BG1.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG1) == 1; si.BG.BG2.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG2) == 1; si.BG.BG3.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG3) == 1; si.BG.BG4.SubEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.TS_BG4) == 1; si.BG.BG1.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG1) == 1; si.BG.BG2.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG2) == 1; si.BG.BG3.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG3) == 1; si.BG.BG4.MathEnabled = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.CGADSUB_BG4) == 1; si.BG.BG1.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1HOFS); si.BG.BG1.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG1VOFS); si.BG.BG2.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2HOFS); si.BG.BG2.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG2VOFS); si.BG.BG3.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3HOFS); si.BG.BG3.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG3VOFS); si.BG.BG4.HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4HOFS); si.BG.BG4.VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.BG4VOFS); si.M7HOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7HOFS); si.M7VOFS = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7VOFS); si.M7A = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7A); si.M7B = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7B); si.M7C = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7C); si.M7D = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7D); si.M7X = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7X); si.M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y); si.M7Y = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7Y); si.M7SEL_REPEAT = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_REPEAT); si.M7SEL_HFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_HFLIP) != 0; si.M7SEL_VFLIP = api.QUERY_peek_logical_register(LibsnesApi.SNES_REG.M7SEL_VFLIP) != 0; for (int i = 1; i <= 4; i++) { si.BG[i].Mode = si.Mode.MODE; si.BG[i].TiledataAddr = si.BG[i].TDADDR << 13; si.BG[i].ScreenAddr = si.BG[i].SCADDR << 9; } //fixup irregular things for mode 7 if (si.Mode.MODE == 7) { si.BG.BG1.TiledataAddr = 0; si.BG.BG1.ScreenAddr = 0; if (si.CGWSEL_DirectColor) { si.BG.BG1.BGMode = BGMode.Mode7DC; } else { si.BG.BG1.BGMode = BGMode.Mode7; } if (si.SETINI_Mode7ExtBG) { si.BG.BG2.BGMode = BGMode.Mode7Ext; si.BG.BG2.Bpp = 7; si.BG.BG2.TiledataAddr = 0; si.BG.BG2.ScreenAddr = 0; } } //determine which colors each BG could use switch (si.Mode.MODE) { case 0: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG2.PaletteSelection = new PaletteSelection(32, 32); si.BG.BG3.PaletteSelection = new PaletteSelection(64, 32); si.BG.BG4.PaletteSelection = new PaletteSelection(96, 32); break; case 1: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 2: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 3: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 4: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 5: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 6: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 32); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; case 7: si.BG.BG1.PaletteSelection = new PaletteSelection(0, 256); si.BG.BG2.PaletteSelection = new PaletteSelection(0, 128); si.BG.BG3.PaletteSelection = new PaletteSelection(0, 0); si.BG.BG4.PaletteSelection = new PaletteSelection(0, 0); break; } return(si); }
public LibsnesCore(GameInfo game, byte[] romData, bool deterministicEmulation, byte[] xmlData, CoreComm comm, object Settings, object SyncSettings) { CoreComm = comm; byte[] sgbRomData = null; if (game["SGB"]) { if ((romData[0x143] & 0xc0) == 0xc0) { throw new CGBNotSupportedException(); } sgbRomData = CoreComm.CoreFileProvider.GetFirmware("SNES", "Rom_SGB", true, "SGB Rom is required for SGB emulation."); game.FirmwareHash = sgbRomData.HashSHA1(); } this.Settings = (SnesSettings)Settings ?? new SnesSettings(); this.SyncSettings = (SnesSyncSettings)SyncSettings ?? new SnesSyncSettings(); api = new LibsnesApi(GetExePath()); api.CMD_init(); api.ReadHook = ReadHook; api.ExecHook = ExecHook; api.WriteHook = WriteHook; ScanlineHookManager = new MyScanlineHookManager(this); api.CMD_init(); api.QUERY_set_video_refresh(snes_video_refresh); api.QUERY_set_input_poll(snes_input_poll); api.QUERY_set_input_state(snes_input_state); api.QUERY_set_input_notify(snes_input_notify); api.QUERY_set_path_request(snes_path_request); scanlineStart_cb = new LibsnesApi.snes_scanlineStart_t(snes_scanlineStart); tracecb = new LibsnesApi.snes_trace_t(snes_trace); soundcb = new LibsnesApi.snes_audio_sample_t(snes_audio_sample); api.QUERY_set_audio_sample(soundcb); RefreshPalette(); // start up audio resampler InitAudio(); //strip header if (romData != null) { if ((romData.Length & 0x7FFF) == 512) { var newData = new byte[romData.Length - 512]; Array.Copy(romData, 512, newData, 0, newData.Length); romData = newData; } } if (game["SGB"]) { IsSGB = true; SystemId = "SNES"; BoardName = "SGB"; CurrLoadParams = new LoadParams() { type = LoadParamType.SuperGameBoy, rom_xml = null, rom_data = sgbRomData, rom_size = (uint)sgbRomData.Length, dmg_xml = null, dmg_data = romData, dmg_size = (uint)romData.Length }; if (!LoadCurrent()) { throw new Exception("snes_load_cartridge_normal() failed"); } } else { //we may need to get some information out of the cart, even during the following bootup/load process if (xmlData != null) { romxml = new System.Xml.XmlDocument(); romxml.Load(new MemoryStream(xmlData)); //bsnes wont inspect the xml to load the necessary sfc file. //so, we have to do that here and pass it in as the romData :/ if (romxml["cartridge"] != null && romxml["cartridge"]["rom"] != null) { romData = File.ReadAllBytes(CoreComm.CoreFileProvider.PathSubfile(romxml["cartridge"]["rom"].Attributes["name"].Value)); } else { throw new Exception("Could not find rom file specification in xml file. Please check the integrity of your xml file"); } } SystemId = "SNES"; CurrLoadParams = new LoadParams() { type = LoadParamType.Normal, xml_data = xmlData, rom_data = romData }; if (!LoadCurrent()) { throw new Exception("snes_load_cartridge_normal() failed"); } } if (api.QUERY_get_region() == LibsnesApi.SNES_REGION.NTSC) { //similar to what aviout reports from snes9x and seems logical from bsnes first principles. bsnes uses that numerator (ntsc master clockrate) for sure. CoreComm.VsyncNum = 21477272; CoreComm.VsyncDen = 4 * 341 * 262; } else { //http://forums.nesdev.com/viewtopic.php?t=5367&start=19 CoreComm.VsyncNum = 21281370; CoreComm.VsyncDen = 4 * 341 * 312; } CoreComm.CpuTraceAvailable = true; api.CMD_power(); SetupMemoryDomains(romData, sgbRomData); DeterministicEmulation = deterministicEmulation; if (DeterministicEmulation) // save frame-0 savestate now { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); bw.Write(CoreSaveState()); bw.Write(true); // framezero, so no controller follows and don't frameadvance on load // hack: write fake dummy controller info bw.Write(new byte[536]); bw.Close(); savestatebuff = ms.ToArray(); } }