diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 9d387a75a1..655dfd0770 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -76,6 +76,7 @@ Code + diff --git a/BizHawk.Emulation/Buffer.cs b/BizHawk.Emulation/Buffer.cs index c8c9275330..c20925a022 100644 --- a/BizHawk.Emulation/Buffer.cs +++ b/BizHawk.Emulation/Buffer.cs @@ -69,7 +69,15 @@ namespace BizHawk { public ByteBuffer(int amt) : base(amt) { } public ByteBuffer(byte[] arr) : base(arr) { } - public byte this[int index] { get { return Read08(index); } set { Write08(index, value); } } + public byte this[int index] + { + #if DEBUG + get { return arr[index]; } + set { arr[index] = value; } + #else + set { Write08(index, value); } + get { return Read08(index);} + #endif + } } - } \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs index bd9d57a69a..f7f496a33c 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs @@ -6,6 +6,7 @@ using System.IO; using System.Collections.Generic; //TODO - consider bytebuffer for mirroring +//TODO - could stringpool the bootgod DB for a pedantic optimization namespace BizHawk.Emulation.Consoles.Nintendo { @@ -223,6 +224,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo public bool wram_battery; public string board_type; + public string pcb; public string sha1; public string system; @@ -230,7 +232,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo public override string ToString() { - return string.Format("pr={0},ch={1},wr={2},vr={3},ba={4},pa={5}|{6},brd={7},map={8},sys={9}", prg_size, chr_size, wram_size, vram_size, wram_battery?1:0, pad_h, pad_v, board_type, mapper, system); + return string.Format("map={0},pr={1},ch={2},wr={3},vr={4},ba={5},pa={6}|{7},brd={8},sys={9}", mapper, prg_size, chr_size, wram_size, vram_size, wram_battery ? 1 : 0, pad_h, pad_v, board_type, system); } } @@ -241,10 +243,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo { public string name; public List carts = new List(); - public override string ToString() - { - return string.Format("name={0}", name); - } } /// @@ -371,6 +369,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo if (xmlreader.NodeType == XmlNodeType.Element && xmlreader.Name == "board") { currCart.board_type = xmlreader.GetAttribute("type"); + currCart.pcb = xmlreader.GetAttribute("pcb"); int mapper = byte.Parse(xmlreader.GetAttribute("mapper")); if (validate && mapper > 255) throw new Exception("didnt expect mapper>255!"); currCart.mapper = (byte)mapper; diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Irem_G101.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Irem_G101.cs new file mode 100644 index 0000000000..e13e972031 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Irem_G101.cs @@ -0,0 +1,162 @@ +using System; +using System.IO; +using System.Diagnostics; + +namespace BizHawk.Emulation.Consoles.Nintendo +{ + //AKA mapper 032 + + //Image Fight + //Major League + //Kaiketsu Yanchamaru 2 + + class Irem_G101 : NES.NESBoardBase + { + //configuration + int prg_bank_mask, chr_bank_mask; + bool oneScreenHack; + + //state + ByteBuffer prg_regs_8k = new ByteBuffer(8); + ByteBuffer chr_regs_1k = new ByteBuffer(8); + int prg_mode, mirror_mode; + + public override void Dispose() + { + prg_regs_8k.Dispose(); + chr_regs_1k.Dispose(); + } + + public override bool Configure(NES.EDetectionOrigin origin) + { + //configure + switch (Cart.board_type) + { + case "IREM-G101": + if (Cart.pcb == "UNAMED-IF-13") + { + //special case for major league + oneScreenHack = true; + } + AssertPrg(128, 256); AssertChr(128); AssertWram(0, 8); AssertVram(0); + break; + default: + return false; + } + + prg_bank_mask = Cart.prg_size / 8 - 1; + chr_bank_mask = Cart.chr_size - 1; + + prg_regs_8k[0] = 0x00; + prg_regs_8k[1] = 0x01; + prg_regs_8k[2] = 0xFE; //constant + prg_regs_8k[3] = 0xFF; //constant + prg_regs_8k[4] = 0xFE; //constant //** NOTE ** according to disch's doc this would be fixed to 0. but it needs to be this to work. someone should let him know. + prg_regs_8k[5] = 0x01; + prg_regs_8k[6] = 0x00; + prg_regs_8k[7] = 0xFF; //constant + + SyncMirror(); + + return true; + } + + void SyncMirror() + { + if (oneScreenHack) + SetMirrorType(EMirrorType.OneScreenA); + else + if (mirror_mode == 0) + SetMirrorType(EMirrorType.Vertical); + else SetMirrorType(EMirrorType.Horizontal); + } + + public override void WritePRG(int addr, byte value) + { + addr &= 0xF007; + switch (addr) + { + //$8000-$8007: [PPPP PPPP] PRG Reg 0 + case 0x0000: + case 0x0001: + case 0x0002: + case 0x0003: + case 0x0004: + case 0x0005: + case 0x0006: + case 0x0007: + prg_regs_8k[0] = value; + prg_regs_8k[4 + 2] = value; + break; + + //$9000-$9007: [.... ..PM] + //P = PRG Mode + //M = Mirroring (0=Vert, 1=Horz) **Ignore for Major League** + case 0x1000: + case 0x1001: + case 0x1002: + case 0x1003: + case 0x1004: + case 0x1005: + case 0x1006: + case 0x1007: + prg_mode = (value >> 1) & 1; + prg_mode <<= 2; + mirror_mode = value & 1; + SyncMirror(); + break; + + //$A000-$A007: [PPPP PPPP] PRG Reg 1 + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + prg_regs_8k[1] = value; + prg_regs_8k[4 + 1] = value; + break; + + //$B000-$B007: [CCCC CCCC] CHR Regs + case 0x3000: + case 0x3001: + case 0x3002: + case 0x3003: + case 0x3004: + case 0x3005: + case 0x3006: + case 0x3007: + chr_regs_1k[addr - 0x3000] = value; + break; + } + } + + public override byte ReadPPU(int addr) + { + if (addr < 0x2000) + { + int bank_1k = addr >> 10; + int ofs = addr & ((1 << 10) - 1); + bank_1k = chr_regs_1k[bank_1k]; + bank_1k &= chr_bank_mask; + addr = (bank_1k << 10) | ofs; + return VROM[addr]; + } + else + return base.ReadPPU(addr); + } + + public override byte ReadPRG(int addr) + { + int bank_8k = addr >> 13; + int ofs = addr & ((1 << 13) - 1); + bank_8k += prg_mode; + bank_8k = prg_regs_8k[bank_8k]; + bank_8k &= prg_bank_mask; + addr = (bank_8k << 13) | ofs; + return ROM[addr]; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Irem_H3001.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Irem_H3001.cs index 98a6958be7..8cb6c14195 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Irem_H3001.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Irem_H3001.cs @@ -9,6 +9,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo //Daiku no Gen San 2 //Spartan X 2 + //NOTE - fceux support for this mapper has some kind of -4 cpu cycle delay built into the timer. not sure yet whether we need that + class Irem_H3001 : NES.NESBoardBase { //configuration @@ -21,6 +23,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo ushort irq_counter, irq_reload; int clock_counter; + public override void Dispose() + { + base.Dispose(); + prg_regs_8k.Dispose(); + chr_regs_1k.Dispose(); + } + public override bool Configure(NES.EDetectionOrigin origin) { //configure @@ -29,6 +38,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo case "IREM-H3001": AssertPrg(128, 256); AssertChr(128, 256); AssertVram(0); AssertWram(0); break; + case "IREM-H3001-FLEX": + break; default: return false; } @@ -43,11 +54,6 @@ namespace BizHawk.Emulation.Consoles.Nintendo return true; } - public override void Dispose() - { - prg_regs_8k.Dispose(); - chr_regs_1k.Dispose(); - } public override void ClockPPU() { diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs index b876c5038a..3f6496faf2 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/NES.cs @@ -339,6 +339,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo public unsafe void LoadGame(IGame game) { + Console.WriteLine("------"); + Console.WriteLine("BEGIN NES rom analysis:"); byte[] file = game.GetFileData(); if (file.Length < 16) throw new Exception("Alleged NES rom too small to be anything useful"); if (file.Take(4).SequenceEqual(System.Text.Encoding.ASCII.GetBytes("UNIF"))) @@ -364,6 +366,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo md5.TransformFinalBlock(file, 16, file.Length - 16); hash_md5 = "md5:" + Util.BytesToHexString(md5.Hash); } + + Console.WriteLine("Found iNES header:"); + CartInfo iNesHeaderInfo = header->Analyze(); + Console.WriteLine("Since this is iNES we can confidently parse PRG/CHR banks to hash."); + Console.WriteLine("headerless rom hash: {0}", hash_sha1); Console.WriteLine("headerless rom hash: {0}", hash_md5); @@ -372,6 +379,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo choice = IdentifyFromBootGodDB(hash_sha1); if (choice == null) { + Console.WriteLine("Could not locate game in nescartdb"); if (USE_DATABASE) { choice = IdentifyFromGameDB(hash_md5); @@ -382,12 +390,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo } if (choice == null) { + Console.WriteLine("Could not locate game in bizhawk gamedb"); Console.WriteLine("Attempting inference from iNES header"); - choice = header->Analyze(); + choice = iNesHeaderInfo; string iNES_board = iNESBoardDetector.Detect(choice); if (iNES_board == null) throw new Exception("couldnt identify NES rom"); - Console.WriteLine("trying board " + iNES_board); + Console.WriteLine("Chose board from iNES heuristics: " + iNES_board); choice.board_type = iNES_board; choice.game.name = game.Name; origin = EDetectionOrigin.INES; @@ -395,7 +404,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo else { origin = EDetectionOrigin.GameDB; - Console.WriteLine("Chose board from gamedb: " + board); + Console.WriteLine("Chose board from bizhawk gamedb: " + choice.board_type); } } else @@ -404,10 +413,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo origin = EDetectionOrigin.BootGodDB; } - Console.WriteLine(choice.game); - Console.WriteLine(choice); - - //todo - generate better name with region and system + //TODO - generate better name with region and system game_name = choice.game.name; //find a INESBoard to handle this @@ -416,6 +422,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo if (boardType == null) throw new Exception("No class implements the necessary board type: " + choice.board_type); + Console.WriteLine("Final game detection results:"); + Console.WriteLine(choice); + Console.WriteLine("\"" + game_name + "\""); + + Console.WriteLine("END NES rom analysis"); + Console.WriteLine("------"); + board = (INESBoard)Activator.CreateInstance(boardType); cart = choice; diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/iNES.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/iNES.cs index 5f811050d4..2cfcc54f81 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/iNES.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/iNES.cs @@ -25,6 +25,9 @@ namespace BizHawk.Emulation.Consoles.Nintendo //if it didnt work, try again with a different wram size. because iNES is weird that way key = string.Format("{0} {1} {2} {3} {4}", cartInfo.mapper, cartInfo.prg_size, cartInfo.chr_size, 8, cartInfo.vram_size); Table.TryGetValue(key, out board); + //if it still didnt work, look for one with empty keys, to detect purely based on mapper + key = string.Format("{0} {1} {2} {3} {4}", cartInfo.mapper, -1,-1,-1,-1); + Table.TryGetValue(key, out board); } return board; } @@ -89,6 +92,7 @@ static string ClassifyTable = @" 7 128 0 8 0 NES-ANROM; marble madness 7 256 0 8 8 NES-AOROM; battletoads 13 32 0 8 16 NES-CPROM; videomation +65 -1 -1 -1 -1 IREM-H3001-FLEX; //Ai Sensei No Oshiete - Watashi No Hoshi (J).nes 66 64 16 8 0 NES-MHROM; super mario bros / duck hunt 66 128 32 8 0 NES-GNROM; gumshoe 68 128 256 8 0 SUNSOFT-4; After Burner 2 (J) @@ -173,7 +177,7 @@ static string ClassifyTable = @" //let's not put a lot of hacks in here. that's what the databases are for. //for example of one not to add: videomation hack to change vram = 8 -> 16 - Console.WriteLine("iNES: map:{0}, mirror:{1}, PRG:{2}, CHR:{3}, WRAM:{4}, VRAM:{5}, bat:{6}", ret.mapper, mirroring, ret.prg_size, ret.chr_size, ret.wram_size, ret.vram_size, ret.wram_battery ? 1 : 0); + Console.WriteLine("map={0},pr={1},ch={2},wr={3},vr={4},ba={5},mir={6}",ret.mapper, ret.prg_size, ret.chr_size, ret.wram_size, ret.vram_size, ret.wram_battery ? 1 : 0, mirroring); return ret; } diff --git a/BizHawk.MultiClient/output/gamedb.txt b/BizHawk.MultiClient/output/gamedb.txt index 1df246dae7..4b4af9ea21 100644 --- a/BizHawk.MultiClient/output/gamedb.txt +++ b/BizHawk.MultiClient/output/gamedb.txt @@ -2261,7 +2261,7 @@ B486A8ED Dai Makai Mura SGX D4448D09BBFDE687C04F9E3310E023AB ti83_1.rom TI83 initPC=6ce ;--nes--; -;these games seem to be in goodNES but not bootgod's DB +;these games seem to be in goodNES but not bootgod's DB. maybe secretly bad dumps or unknown variants. sha1:22E6986903141495BA4589AC65982F3FB4D0E37B Adventures of Lolo (U) NES board=NES-SEROM;PRG=32;CHR=32 sha1:99C18C91F051BFBA6836A7B6B213C77569D83767 After Burner 2 (J) NES board=SUNSOFT-4;PRG=128;CHR=256;WRAM=8 sha1:CF655333DCE649A3C7060E9989860F2FC74E473A Demon Sword (U) NES board=NES-SL1ROM;PRG=128;CHR=128 @@ -2269,6 +2269,7 @@ sha1:7786BA1FE8E7E9E542EEB13CF2A6E2A1AD7F696D Metal Gear (U) NES board=KONAMI- sha1:894F20405286F5F75133CE4648300E2C67972B40 Solomon's Key (U) NES board=NES-CNROM;PRG=32;CHR=32 sha1:0C53B06E1D13AE917536BB39010914EA3D111FF5 Thunder & Lightning (U) NES board=NES-GNROM;PRG=128;CHR=32;bad sha1:9BDFF9A19265D84979F43F0F6E925A735DDEB38B Wai Xing Zhan Shi (Ch) NES board=Mapper242;PRG=512;CHR=0;VRAM=8;WRAM=8 +sha1:91CECCFCAC90E417E9AEE80E8F7B560A20EB33CC Ai Sensei No Oshiete - Watashi No Hoshi (J) NES board=IREM-G101;PRG=256;CHR=128;WRAM=8 ;and these are even labeled as bad in goodNES sha1:984ADAEB85403EEF1BA85CDCF310FAAECEB409A0 Adventures of Captain Comic, The (Bad Dump) (U) NES board=COLORDREAMS-74*377;PRG=64;CHR=64;WRAM=0;VRAM=0;bad