220 lines
6.6 KiB
C#
220 lines
6.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using BizHawk.Emulation.Common;
|
|
|
|
//TODO - could stringpool the BootGod DB for a pedantic optimization
|
|
namespace BizHawk.Emulation.Cores.Nintendo.NES
|
|
{
|
|
partial class NES
|
|
{
|
|
static List<Type> INESBoardImplementors = new List<Type>();
|
|
|
|
private static INesBoard CreateBoardInstance(Type boardType)
|
|
{
|
|
var board = (INesBoard)Activator.CreateInstance(boardType);
|
|
lock (INESBoardImplementors)
|
|
{
|
|
//put the one we chose at the top of the list, for quicker access in the future
|
|
int x = INESBoardImplementors.IndexOf(boardType);
|
|
//(swap)
|
|
var temp = INESBoardImplementors[0];
|
|
INESBoardImplementors[0] = boardType;
|
|
INESBoardImplementors[x] = temp;
|
|
}
|
|
return board;
|
|
}
|
|
|
|
public string BoardName => Board.GetType().Name;
|
|
|
|
private void BoardSystemHardReset()
|
|
{
|
|
INesBoard newboard;
|
|
// FDS and NSF have a unique activation setup
|
|
if (Board is FDS)
|
|
{
|
|
var newfds = new FDS();
|
|
var oldfds = Board as FDS;
|
|
newfds.biosrom = oldfds.biosrom;
|
|
newfds.SetDiskImage(oldfds.GetDiskImage());
|
|
newboard = newfds;
|
|
}
|
|
else if (Board is NSFBoard)
|
|
{
|
|
var newnsf = new NSFBoard();
|
|
var oldnsf = Board as NSFBoard;
|
|
newnsf.InitNSF(oldnsf.nsf);
|
|
newboard = newnsf;
|
|
}
|
|
else
|
|
{
|
|
newboard = CreateBoardInstance(Board.GetType());
|
|
}
|
|
newboard.Create(this);
|
|
// i suppose the old board could have changed its initial register values, although it really shouldn't
|
|
// you can't use SyncSettings.BoardProperties here because they very well might be different than before
|
|
// in case the user actually changed something in the UI
|
|
newboard.InitialRegisterValues = Board.InitialRegisterValues;
|
|
newboard.Configure(origin);
|
|
newboard.Rom = Board.Rom;
|
|
newboard.Vrom = Board.Vrom;
|
|
if (Board.Wram != null)
|
|
newboard.Wram = new byte[Board.Wram.Length];
|
|
if (Board.Vram != null)
|
|
newboard.Vram = new byte[Board.Vram.Length];
|
|
newboard.PostConfigure();
|
|
// the old board's sram must be restored
|
|
if (newboard is FDS)
|
|
{
|
|
var newfds = newboard as FDS;
|
|
var oldfds = Board as FDS;
|
|
newfds.StoreSaveRam(oldfds.ReadSaveRam());
|
|
}
|
|
else if (Board.SaveRam != null)
|
|
{
|
|
Buffer.BlockCopy(Board.SaveRam, 0, newboard.SaveRam, 0, Board.SaveRam.Length);
|
|
}
|
|
|
|
Board = newboard;
|
|
ppu.HasClockPPU = Board.GetType().GetMethod(nameof(INesBoard.ClockPpu)).DeclaringType != typeof(NesBoardBase);
|
|
}
|
|
|
|
|
|
static NES()
|
|
{
|
|
var highPriority = new List<Type>();
|
|
var normalPriority = new List<Type>();
|
|
|
|
//scan types in this assembly to find ones that implement boards to add them to the list
|
|
foreach (Type type in typeof(NES).Assembly.GetTypes())
|
|
{
|
|
var attrs = type.GetCustomAttributes(typeof(NesBoardImplAttribute), true);
|
|
if (attrs.Length == 0) continue;
|
|
if (type.IsAbstract) continue;
|
|
var cancelAttrs = type.GetCustomAttributes(typeof(NesBoardImplCancelAttribute), true);
|
|
if (cancelAttrs.Length != 0) continue;
|
|
var priorityAttrs = type.GetCustomAttributes(typeof(NesBoardImplPriorityAttribute), true);
|
|
if (priorityAttrs.Length != 0)
|
|
highPriority.Add(type);
|
|
else normalPriority.Add(type);
|
|
}
|
|
|
|
INESBoardImplementors.AddRange(highPriority);
|
|
INESBoardImplementors.AddRange(normalPriority);
|
|
}
|
|
|
|
/// <summary>
|
|
/// finds a board class which can handle the provided cart
|
|
/// </summary>
|
|
static Type FindBoard(CartInfo cart, EDetectionOrigin origin, Dictionary<string, string> properties)
|
|
{
|
|
NES nes = new NES { cart = cart };
|
|
Type ret = null;
|
|
lock(INESBoardImplementors)
|
|
foreach (var type in INESBoardImplementors)
|
|
{
|
|
NesBoardBase board = (NesBoardBase)Activator.CreateInstance(type);
|
|
//unif demands that the boards set themselves up with expected legal values based on the board size
|
|
//except, i guess, for the rom/chr sizes. go figure.
|
|
//so, disable the asserts here
|
|
if (origin == EDetectionOrigin.UNIF)
|
|
board.DisableConfigAsserts = true;
|
|
|
|
board.Create(nes);
|
|
board.InitialRegisterValues = properties;
|
|
if (board.Configure(origin))
|
|
{
|
|
#if DEBUG
|
|
if (ret != null)
|
|
throw new Exception($"Boards {ret} and {type} both responded to {nameof(NesBoardBase.Configure)}!");
|
|
ret = type;
|
|
#else
|
|
return type;
|
|
#endif
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/// <summary>
|
|
/// looks up from the bootgod DB
|
|
/// </summary>
|
|
CartInfo IdentifyFromBootGodDB(IEnumerable<string> hash_sha1)
|
|
{
|
|
BootGodDb.Initialize();
|
|
foreach (var hash in hash_sha1)
|
|
{
|
|
List<CartInfo> choices = BootGodDb.Instance.Identify(hash);
|
|
//pick the first board for this hash arbitrarily. it probably doesn't make a difference
|
|
if (choices.Count != 0)
|
|
return choices[0];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// looks up from the game DB
|
|
/// </summary>
|
|
CartInfo IdentifyFromGameDB(string hash)
|
|
{
|
|
var gi = Database.CheckDatabase(hash);
|
|
if (gi == null) return null;
|
|
|
|
CartInfo cart = new CartInfo();
|
|
|
|
//try generating a bootgod cart descriptor from the game database
|
|
var dict = gi.GetOptionsDict();
|
|
cart.GameInfo = gi;
|
|
if (!dict.ContainsKey("board"))
|
|
throw new Exception("NES gamedb entries must have a board identifier!");
|
|
cart.BoardType = dict["board"];
|
|
if (dict.ContainsKey("system"))
|
|
cart.System = dict["system"];
|
|
cart.PrgSize = -1;
|
|
cart.VramSize = -1;
|
|
cart.WramSize = -1;
|
|
cart.ChrSize = -1;
|
|
if (dict.ContainsKey("PRG"))
|
|
cart.PrgSize = short.Parse(dict["PRG"]);
|
|
if (dict.ContainsKey("CHR"))
|
|
cart.ChrSize = short.Parse(dict["CHR"]);
|
|
if(dict.ContainsKey("VRAM"))
|
|
cart.VramSize = short.Parse(dict["VRAM"]);
|
|
if (dict.ContainsKey("WRAM"))
|
|
cart.WramSize = short.Parse(dict["WRAM"]);
|
|
if (dict.ContainsKey("PAD_H"))
|
|
cart.PadH = byte.Parse(dict["PAD_H"]);
|
|
if (dict.ContainsKey("PAD_V"))
|
|
cart.PadV = byte.Parse(dict["PAD_V"]);
|
|
if(dict.ContainsKey("MIR"))
|
|
if (dict["MIR"] == "H")
|
|
{
|
|
cart.PadV = 1; cart.PadH = 0;
|
|
}
|
|
else if (dict["MIR"] == "V")
|
|
{
|
|
cart.PadH = 1; cart.PadV = 0;
|
|
}
|
|
if (dict.ContainsKey("BAD"))
|
|
cart.Bad = true;
|
|
if (dict.ContainsKey("MMC3"))
|
|
cart.Chips.Add(dict["MMC3"]);
|
|
if (dict.ContainsKey("PCB"))
|
|
cart.Pcb = dict["PCB"];
|
|
if (dict.ContainsKey("BATT"))
|
|
cart.WramBattery = bool.Parse(dict["BATT"]);
|
|
|
|
if(dict.ContainsKey("palette"))
|
|
{
|
|
cart.Palette = dict["palette"];
|
|
}
|
|
|
|
if (dict.ContainsKey("vs_security"))
|
|
{
|
|
cart.VsSecurity = byte.Parse(dict["vs_security"]);
|
|
}
|
|
|
|
return cart;
|
|
}
|
|
}
|
|
}
|