BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/Mapper030.cs

242 lines
7.0 KiB
C#

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;
uint[] value = new uint[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);
if(value[0] < 0xFFFFFFFF) 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);
if (value[0] < 0xFFFFFFFF) 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)
{
switch (addr & 0x1FF)
{
case 0:
return 0xBF;
case 1:
switch (Cart.prg_size)
{
case 128:
return 0xB5;
case 256:
return 0xB6;
case 512:
return 0xB7;
}
return 0xFF; //Shouldn't ever reach here, as the size was asserted earlier.
default:
return 0xFF; //Other unknown data is returned from addresses 2-511, in software ID mode, mostly 0xFF.
}
}
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);
}
}
}