From 5f66376f8219ed9e0ba39b64668ea2c53c7ebaa9 Mon Sep 17 00:00:00 2001 From: goyuken Date: Fri, 11 Apr 2014 15:01:22 +0000 Subject: [PATCH] implement caitsith6502's mapper 30 support patch (with a few minor cleanups). fixes issue 153 --- .../BizHawk.Emulation.Cores.csproj | 1 + .../Consoles/Nintendo/NES/Boards/Mapper030.cs | 235 ++++++++++++++++++ .../Consoles/Nintendo/NES/iNES.cs | 4 +- 3 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/Mapper030.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 865b834ad4..28d256678e 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -284,6 +284,7 @@ + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/Mapper030.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/Mapper030.cs new file mode 100644 index 0000000000..454cf1088f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/Mapper030.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.NES +{ + public class Mapper030 : NES.NESBoardBase + { + + enum flashmode { fm_default, fm_erase, fm_write, fm_id } + + // config + int prg_bank_mask_16k; + int vram_bank_mask_8k; + + // state + int prg; + int chr; + int flash_state = 0; + flashmode flash_mode = flashmode.fm_default; + byte[] flash_rom = null; + + int get_flash_write_count(int addr) + { + if (flash_rom == null) + return 0; + int[] value = new int[1]; + int bank = (addr >= 0x4000) ? prg_bank_mask_16k : prg; + Buffer.BlockCopy(flash_rom, (bank << 2 | (addr >> 12) & 3) << 2, value, 0, 4); + return value[0]; + } + + void increment_flash_write_count(int addr, bool direct = false) + { + if (flash_rom == null) + return; + int[] value = new int[1]; + int bank = (addr >= 0x4000) ? prg_bank_mask_16k : prg; + if (!direct) + { + Buffer.BlockCopy(flash_rom, (bank << 2 | (addr >> 12) & 3) << 2, value, 0, 4); + value[0]++; + Buffer.BlockCopy(value, 0, flash_rom, (bank << 2 | (addr >> 12) & 3) << 2, 4); + } + else + { + Buffer.BlockCopy(flash_rom, addr << 2, value, 0, 4); + value[0]++; + Buffer.BlockCopy(value, 0, flash_rom, addr << 2, 4); + } + } + + public override void SyncState(Serializer ser) + { + base.SyncState(ser); + ser.Sync("prg", ref prg); + ser.Sync("chr", ref chr); + ser.Sync("flash_state", ref flash_state); + int tmp = (int)flash_mode; + ser.Sync("flash_mode", ref tmp); + flash_mode = (flashmode)tmp; + ser.Sync("flash_rom", ref flash_rom, true); + } + + public override bool Configure(NES.EDetectionOrigin origin) + { + switch (Cart.board_type) + { + case "MAPPER030": + Cart.vram_size = 32; + break; + case "MAPPER0030-00": + AssertVram(8, 16, 32); + break; + case "UNIF_UNROM-512-8": + Cart.vram_size = 8; + break; + case "UNIF_UNROM-512-16": + Cart.vram_size = 16; + break; + case "UNIF_UNROM-512-32": + Cart.vram_size = 32; + break; + default: + return false; + } + + if (Cart.wram_battery) + { + flash_state = 0; + flash_mode = flashmode.fm_default; + if (flash_rom == null) + { + // extra space is used to hold information about what sectors have been flashed + flash_rom = new byte[Cart.prg_size * 1024 + Cart.prg_size]; + } + } + SetMirrorType(CalculateMirrorType(Cart.pad_h, Cart.pad_v)); + AssertChr(0); + AssertPrg(128, 256, 512); //Flash chip sizes that fits sealie unrom-512 are 39SF010, 39SF020, 39SF040. + Cart.wram_size = 0; + prg_bank_mask_16k = Cart.prg_size / 16 - 1; + vram_bank_mask_8k = Cart.vram_size / 8 - 1; + return true; + } + + static readonly int[] addr_state = new int[5] { 0x1555, 0x2AAA, 0x1555, 0x1555, 0x2AAA }; + static readonly int[] addr_bank = new int[5] { 1, 0, 1, 1, 0 }; + static readonly byte[] addr_data = new byte[5] { 0xAA, 0x55, 0x80, 0xAA, 0x55 }; + + public override void WritePRG(int addr, byte value) + { + if ((!Cart.wram_battery) || (addr >= 0x4000)) + { + byte value2 = value; + + if (!Cart.wram_battery) + value2 = HandleNormalPRGConflict(addr, value); + chr = value2 >> 5 & 3 & vram_bank_mask_8k; + prg = value2 & prg_bank_mask_16k; + if ((Cart.pad_h == 0) && (Cart.pad_v == 0)) + { + int mirror = (value2 & 0x80) >> 7; + SetMirrorType(CalculateMirrorType(mirror, mirror)); + } + } + else + { + if (flash_mode == flashmode.fm_default) + { + if (addr_state[flash_state] == addr && addr_bank[flash_state] == prg && addr_data[flash_state] == value) + { + flash_state++; + if (flash_state == 5) + flash_mode = flashmode.fm_erase; + } + else if (flash_state == 2 && addr == 0x1555 && prg == 1 && value == 0x90) + { + flash_mode = flashmode.fm_id; + } + else if (flash_state == 2 && addr == 0x1555 && prg == 1 && value == 0xA0) + { + flash_state++; + flash_mode = flashmode.fm_write; + } + else + { + flash_state = 0; + flash_mode = flashmode.fm_default; + } + } + else if (flash_mode == flashmode.fm_erase) + { + if (value == 0x10) //You probably don't want to do this, as this is erase entire flash chip. :) + { //Of course, we gotta emulate the behaviour. + for (int i = 0; i < (Cart.prg_size / 4); i++) + increment_flash_write_count(i, true); + for (int i = 0; i < flash_rom.Count(); i++) + flash_rom[Cart.prg_size + i] = 0xFF; + } + else if (value == 0x30) + { + increment_flash_write_count(addr); + for (int i = 0; i < 0x1000; i++) + flash_rom[(prg << 14 | addr & 0x3000) + i + Cart.prg_size] = 0xFF; + } + flash_mode = 0; + flash_state = 0; + } + else if (flash_mode == flashmode.fm_write) + { + if (get_flash_write_count(addr) == 0) + { + increment_flash_write_count(addr); + for (int i = 0; i < 0x1000; i++) + flash_rom[(prg << 14 | addr & 0x3000) + i + Cart.prg_size] = ROM[(prg << 14 | addr & 0x3000) + i]; + } + flash_rom[Cart.prg_size + (prg << 14 | addr & 0x3fff)] &= value; + flash_state = 0; + flash_mode = 0; + } + if (flash_mode == flashmode.fm_id && value == 0xF0) + { + flash_state = 0; + flash_mode = 0; + } + } + } + + public override byte ReadPRG(int addr) + { + int bank = addr >= 0x4000 ? prg_bank_mask_16k : prg; + if (Cart.wram_battery) + { + if (flash_mode == flashmode.fm_id) + { + if ((addr & 1) == 0) + return 0xBF; + else + switch (Cart.prg_size) + { + case 128: + return 0xB5; + case 256: + return 0xB6; + case 512: + return 0xB7; + } + } + if (get_flash_write_count(addr) > 0) + return flash_rom[Cart.prg_size + (bank << 14 | addr & 0x3fff)]; + } + return ROM[bank << 14 | addr & 0x3fff]; + } + + public override byte[] SaveRam { get { return flash_rom; } } + + public override byte ReadPPU(int addr) + { + if (addr < 0x2000) + return VRAM[addr | chr << 13]; + else + return base.ReadPPU(addr); + } + public override void WritePPU(int addr, byte value) + { + if (addr < 0x2000) + VRAM[addr | chr << 13] = value; + else + base.WritePPU(addr, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/iNES.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/iNES.cs index 8092a11fd1..379f696813 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/iNES.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/iNES.cs @@ -36,9 +36,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES CartV2.prg_size *= 16; CartV2.chr_size *= 8; + CartV2.wram_battery = (data[6] & 2) != 0; // should this be respected in v2 mode?? + int wrambat = iNES2Wram(data[10] >> 4); int wramnon = iNES2Wram(data[10] & 15); - CartV2.wram_battery = wrambat > 0; + CartV2.wram_battery |= wrambat > 0; // fixme - doesn't handle sizes not divisible by 1024 CartV2.wram_size = (short)((wrambat + wramnon) / 1024);