[NES] big mmc3 reorg and many new games

This commit is contained in:
zeromus 2011-05-28 09:52:32 +00:00
parent 21703d9f6a
commit fba7989f0a
12 changed files with 740 additions and 325 deletions

View File

@ -79,6 +79,14 @@
<Compile Include="Consoles\Nintendo\NES\Boards\Jaleco-JF_11_14.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Mapper107.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Mapper242.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\DRROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper095.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper206.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\MMC3_family.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\TQROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\TLSROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\TVROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\TxROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\NROM.cs">
<SubType>Code</SubType>
</Compile>
@ -86,7 +94,6 @@
<Compile Include="Consoles\Nintendo\NES\Boards\SxROM.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Consoles\Nintendo\NES\Boards\TxROM_DxROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\UxROM.cs">
<SubType>Code</SubType>
</Compile>

View File

@ -82,7 +82,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
else SetMirrorType(EMirrorType.OneScreenB);
}
protected void SetMirrorType(EMirrorType mirrorType)
public void SetMirrorType(EMirrorType mirrorType)
{
switch (mirrorType)
{

View File

@ -0,0 +1,61 @@
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
//this board contains a Namcot 109 and some extra ram for nametables
public class DRROM : Namcot109Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{
//analyze board type
switch (Cart.board_type)
{
case "NES-DRROM": //gauntlet (U)
AssertPrg(128); AssertChr(64); AssertVram(2); AssertWram(0);
break;
default:
return false;
}
BaseSetup();
SetMirrorType(Cart.pad_h, Cart.pad_v);
return true;
}
//the addressing logic for nametables is a bit speculative here
//how it is wired back to the NES and locally mirrored is unknown,
//but it probably doesnt matter in practice.
//still, purists could validate it.
public override byte ReadPPU(int addr)
{
if (addr < 0x2000)
{
//read patterns from mapper controlled area
return base.ReadPPU(addr);
}
else if (addr < 0x2800)
{
return VRAM[addr - 0x2000];
}
else return base.ReadPPU(addr);
}
public override void WritePPU(int addr, byte value)
{
if (addr < 0x2000)
{
//nothing wired here
}
else if (addr < 0x2800)
{
VRAM[addr - 0x2000] = value;
}
else base.WritePPU(addr, value);
}
}
}

View File

@ -0,0 +1,284 @@
//this file contains the MMC3 family of chips
//which includes:
//NAMCOT 109
//MMC3 (which was apparently based on NAMCOT 109 and shares enough functionality to be derived from it in this codebase)
//see http://nesdev.parodius.com/bbs/viewtopic.php?t=5426&sid=e7472c15a758ebf05c588c8330c2187f
//and http://nesdev.parodius.com/bbs/viewtopic.php?t=311
//for some info on NAMCOT 109
//mappers handled by this:
//004,095,118,119,206
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
public class Namcot109 : IDisposable
{
public Namcot109(int num_prg_banks)
{
bank_regs[8] = (byte)(num_prg_banks - 1);
bank_regs[9] = (byte)(num_prg_banks - 2);
}
public void Dispose()
{
bank_regs.Dispose();
prg_lookup.Dispose();
}
//state
public int chr_mode, prg_mode, reg_addr;
//this contains the 8 programmable regs and 2 more at the end to represent PRG banks -2 and -1; and 4 more at the end to break down chr regs 0 and 1
ByteBuffer bank_regs = new ByteBuffer(14);
ByteBuffer prg_lookup = new ByteBuffer(new byte[] { 6, 7, 9, 8, 9, 7, 6, 8 });
ByteBuffer chr_lookup = new ByteBuffer(new byte[] { 10, 11, 12, 13, 2, 3, 4, 5 });
public virtual void WritePRG(int addr, byte value)
{
switch (addr & 0x6001)
{
case 0x0000: //$8000
chr_mode = (value >> 7) & 1;
prg_mode = (value >> 6) & 1;
reg_addr = (value & 7);
break;
case 0x0001: //$8001
bank_regs[reg_addr] = value;
//setup the 2K chr regs
bank_regs[10] = (byte)((bank_regs[0] & ~1) + 0);
bank_regs[11] = (byte)((bank_regs[0] & ~1) + 1);
bank_regs[12] = (byte)((bank_regs[1] & ~1) + 0);
bank_regs[13] = (byte)((bank_regs[1] & ~1) + 1);
break;
}
}
public int Get_PRGBank_8K(int addr)
{
int bank_8k = addr >> 13;
bank_8k = bank_regs[prg_lookup[prg_mode * 4 + bank_8k]];
return bank_8k;
}
public int Get_CHRBank_1K(int addr)
{
int bank_1k = addr >> 10;
if (chr_mode == 1)
bank_1k ^= 4;
bank_1k = bank_regs[chr_lookup[bank_1k]];
return bank_1k;
}
}
public class MMC3 : Namcot109
{
NES.NESBoardBase board;
public MMC3(NES.NESBoardBase board, int num_prg_banks)
: base(num_prg_banks)
{
this.board = board;
}
//state
public NES.NESBoardBase.EMirrorType mirror;
int ppubus_state, ppubus_statecounter;
byte irq_reload, irq_counter;
bool irq_pending, irq_enable;
void SyncIRQ()
{
board.NES.irq_cart = irq_pending;
}
public override void WritePRG(int addr, byte value)
{
switch (addr & 0x6001)
{
case 0x0000: //$8000
case 0x0001: //$8001
base.WritePRG(addr, value);
break;
case 0x2000: //$A000
//mirroring
if ((value & 1) == 0) mirror = NES.NESBoardBase.EMirrorType.Vertical;
else mirror = NES.NESBoardBase.EMirrorType.Horizontal;
board.SetMirrorType(mirror);
break;
case 0x2001: //$A001
//wram enable/protect
break;
case 0x4000: //$C000
irq_reload = value;
break;
case 0x4001: //$C001
irq_counter = 0;
break;
case 0x6000: //$E000
irq_enable = false;
irq_pending = false;
SyncIRQ();
break;
case 0x6001: //$E001
irq_enable = true;
SyncIRQ();
break;
}
}
void ClockIRQ()
{
if (irq_counter == 0)
irq_counter = irq_reload;
else
{
irq_counter--;
//Console.WriteLine(irq_counter);
if (irq_counter == 0)
{
if (irq_enable)
irq_pending = true;
SyncIRQ();
}
}
}
//TODO - this should be determined from NES timestamps to correctly emulate ppu writes interfering
public void Tick_PPU(int addr)
{
ppubus_statecounter++;
int state = (addr >> 12) & 1;
if (ppubus_state == 0 && ppubus_statecounter > 1 && state == 1)
{
ppubus_statecounter = 0;
ClockIRQ();
}
if (ppubus_state != state)
{
ppubus_state = state;
}
}
}
public abstract class MMC3_Family_Board_Base : NES.NESBoardBase
{
//configuration
protected int prg_mask, chr_mask;
public override void Dispose()
{
mapper.Dispose();
}
protected Namcot109 mapper;
public override byte ReadPPU(int addr)
{
if (addr < 0x2000)
{
int bank_1k = mapper.Get_CHRBank_1K(addr);
bank_1k &= chr_mask;
addr = (bank_1k << 10) | (addr & 0x3FF);
if (VROM != null)
return VROM[addr];
else return VRAM[addr];
}
else return base.ReadPPU(addr);
}
public override void WritePPU(int addr, byte value)
{
base.WritePPU(addr, value);
}
public override void WritePRG(int addr, byte value)
{
mapper.WritePRG(addr, value);
}
public override byte ReadPRG(int addr)
{
int bank_8k = mapper.Get_PRGBank_8K(addr);
bank_8k &= prg_mask;
addr = (bank_8k << 13) | (addr & 0x1FFF);
return ROM[addr];
}
protected virtual void BaseSetup()
{
//remember to setup the PRG banks -1 and -2
int num_prg_banks = Cart.prg_size / 8;
prg_mask = num_prg_banks - 1;
int num_chr_banks = (Cart.chr_size);
chr_mask = num_chr_banks - 1;
}
//used by a couple of boards for controlling nametable wiring with the mapper
protected int RewireNametable_Mapper095_and_TLSROM(int addr, int bitsel)
{
int bank_1k = mapper.Get_CHRBank_1K(addr & 0x1FFF);
int nt = (bank_1k >> bitsel) & 1;
int ofs = addr & 0x3FF;
addr = 0x2000 + (nt << 10);
addr |= (ofs);
return addr;
}
}
public abstract class MMC3Board_Base : MMC3_Family_Board_Base
{
//configuration
protected int wram_mask;
//state
protected MMC3 mmc3;
public override byte ReadPPU(int addr)
{
mmc3.Tick_PPU(addr);
return base.ReadPPU(addr);
}
public override void WritePPU(int addr, byte value)
{
mmc3.Tick_PPU(addr);
base.WritePPU(addr, value);
}
public override void WritePRG(int addr, byte value)
{
base.WritePRG(addr, value);
}
protected override void BaseSetup()
{
wram_mask = (Cart.wram_size * 1024) - 1;
int num_prg_banks = Cart.prg_size / 8;
mapper = mmc3 = new MMC3(this,num_prg_banks);
base.BaseSetup();
SetMirrorType(EMirrorType.Vertical);
}
}
public abstract class Namcot109Board_Base : MMC3_Family_Board_Base
{
protected override void BaseSetup()
{
int num_prg_banks = Cart.prg_size / 8;
mapper = new Namcot109(num_prg_banks);
base.BaseSetup();
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
//pretty much just one game.
//wires the mapper outputs to control the nametables. check out the companion board TLSROM
public class Mapper095 : Namcot109Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{
//analyze board type
switch (Cart.board_type)
{
case "NAMCOT-3425": //dragon buster (J)
AssertPrg(128); AssertChr(32); AssertVram(0); AssertWram(0);
break;
default:
return false;
}
BaseSetup();
SetMirrorType(EMirrorType.Vertical);
return true;
}
public override byte ReadPPU(int addr)
{
if (addr < 0x2000) return base.ReadPPU(addr);
else return base.ReadPPU(RewireNametable_Mapper095_and_TLSROM(addr, 5));
}
public override void WritePPU(int addr, byte value)
{
if (addr < 0x2000) base.WritePPU(addr, value);
else base.WritePPU(RewireNametable_Mapper095_and_TLSROM(addr, 5), value);
}
}
}

View File

@ -0,0 +1,63 @@
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
//various japanese namcot 109 boards plus DEROM
public class Mapper206 : Namcot109Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{
//analyze board type
switch (Cart.board_type)
{
case "NES-DEROM": //R.B.I. baseball (U)
AssertPrg(64); AssertChr(32,64); AssertVram(0); AssertWram(0);
break;
case "NAMCOT-3401": //babel no tou (J)
AssertPrg(32); AssertChr(32); AssertVram(0); AssertWram(0);
break;
case "NAMCOT-3405": //side pocket (J)
AssertPrg(128); AssertChr(32); AssertVram(0); AssertWram(0);
throw new Exception("TODO - test please");
break;
case "NAMCOT-3406": //karnov (J)
AssertPrg(128); AssertChr(64); AssertVram(0); AssertWram(0);
break;
case "NAMCOT-3407": //family jockey (J)
AssertPrg(32); AssertChr(32); AssertVram(0); AssertWram(0);
break;
case "NAMCOT-3413": //pro yakyuu family stadium (J)
AssertPrg(64); AssertChr(32); AssertVram(0); AssertWram(0);
throw new Exception("TODO - test please");
break;
case "NAMCOT-3414": //family boxing (J)
AssertPrg(64); AssertChr(64); AssertVram(0); AssertWram(0);
break;
case "NAMCOT-3415": //mappy-land (J)
AssertPrg(128); AssertChr(32); AssertVram(0); AssertWram(0);
break;
case "NAMCOT-3416": //dragon slayer IV (J) (aka legacy of the wizard)
AssertPrg(128); AssertChr(64); AssertVram(0); AssertWram(0);
break;
case "NAMCOT-3417": //spy kid (J)
//apparently this shows up as namcot 108 as well but perhaps there is no difference
//(is this game older than the other namcot 109 games?)
AssertPrg(32); AssertChr(32); AssertVram(0); AssertWram(0);
throw new Exception("TODO - test please");
break;
default:
return false;
}
BaseSetup();
SetMirrorType(Cart.pad_h, Cart.pad_v);
return true;
}
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
//aka mapper 118
//wires the mapper outputs to control the nametables. check out the companion board Mapper095
public class TLSROM : MMC3Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{
//analyze board type
switch (Cart.board_type)
{
case "NES-TLSROM": //pro sport hockey (U)
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
break;
case "HVC-TKSROM": //ys III: wanderers from ys (J)
AssertPrg(256); AssertChr(128); AssertVram(0); AssertWram(8);
AssertBattery(true);
break;
default:
return false;
}
BaseSetup();
SetMirrorType(EMirrorType.Vertical);
return true;
}
public override void WritePRG(int addr, byte value)
{
base.WritePRG(addr, value);
}
public override byte ReadPPU(int addr)
{
if (addr < 0x2000) return base.ReadPPU(addr);
else return base.ReadPPU(RewireNametable_Mapper095_and_TLSROM(addr, 7));
}
public override void WritePPU(int addr, byte value)
{
if (addr < 0x2000) base.WritePPU(addr, value);
else base.WritePPU(RewireNametable_Mapper095_and_TLSROM(addr, 7), value);
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
//aka mapper 119
//just high speed and pinbot with an MMC3 and some custom logic to select between chr rom and chr ram
public class TQSROM : MMC3Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{
//analyze board type
switch (Cart.board_type)
{
case "NES-TQROM": //high speed and pinbot
AssertPrg(128); AssertChr(64); AssertVram(8); AssertWram(0);
break;
default:
return false;
}
BaseSetup();
return true;
}
public override byte ReadPPU(int addr)
{
if (addr < 0x2000)
{
int bank_1k = mapper.Get_CHRBank_1K(addr);
int use_ram = (bank_1k >> 6) & 1;
if (use_ram == 1)
{
mmc3.Tick_PPU(addr);
addr = ((bank_1k&0x3f) << 10) | (addr & 0x3FF);
addr &= 0x1FFF;
return VRAM[addr];
}
else return base.ReadPPU(addr);
}
else
return base.ReadPPU(addr);
}
public override void WritePPU(int addr, byte value)
{
if (addr < 0x2000)
{
int bank_1k = mapper.Get_CHRBank_1K(addr);
int use_ram = (bank_1k >> 6) & 1;
if (use_ram == 1)
{
mmc3.Tick_PPU(addr);
addr = ((bank_1k & 0x3f) << 10) | (addr & 0x3FF);
addr &= 0x1FFF;
VRAM[addr] = value;
}
else base.WritePPU(addr, value);
}
else
base.WritePPU(addr, value);
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
public class TVROM : MMC3Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{
//analyze board type
switch (Cart.board_type)
{
case "NES-TVROM": //rad racer II (U)
AssertPrg(64); AssertChr(64); AssertVram(8); AssertWram(0);
AssertBattery(false);
break;
default:
return false;
}
BaseSetup();
return true;
}
//nesdev wiki says that the nes CIRAM doesnt get used at all.
//and that even though 8KB is really here, only 4KB gets used.
//still, purists could validate it.
public override byte ReadPPU(int addr)
{
mmc3.Tick_PPU(addr);
if (addr < 0x2000)
{
//read patterns from mapper controlled area
return base.ReadPPU(addr);
}
else
{
return VRAM[addr & 0xFFF];
}
}
public override void WritePPU(int addr, byte value)
{
mmc3.Tick_PPU(addr);
if (addr < 0x2000)
{
//nothing wired here
}
else
{
VRAM[addr & 0xFFF] = value;
}
}
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.IO;
using System.Diagnostics;
//http://wiki.nesdev.com/w/index.php/TxROM
//read some background info on namco 108 and DxROM boards here
namespace BizHawk.Emulation.Consoles.Nintendo
{
public class TxROM : MMC3Board_Base
{
public override void WritePRG(int addr, byte value)
{
base.WritePRG(addr, value);
SetMirrorType(mmc3.mirror); //often redundant, but gets the job done
}
public override byte[] SaveRam
{
get
{
if (!Cart.wram_battery) return null;
return WRAM;
//some boards have a wram that is backed-up or not backed-up. need to handle that somehow
//(nestopia splits it into NVWRAM and WRAM but i didnt like that at first.. but it may player better with this architecture)
}
}
public override byte ReadWRAM(int addr)
{
if (Cart.wram_size != 0)
return WRAM[addr & wram_mask];
else return 0xFF;
}
public override void WriteWRAM(int addr, byte value)
{
if (Cart.wram_size != 0)
WRAM[addr & wram_mask] = value;
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
}
public override bool Configure(NES.EDetectionOrigin origin)
{
//analyze board type
switch (Cart.board_type)
{
case "NES-TBROM": //tecmo world cup soccer (DE) [untested]
AssertPrg(64); AssertChr(64); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TEROM": //Adv of lolo 2
AssertPrg(32); AssertChr(32); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TFROM": //legacy of the wizard
AssertPrg(128); AssertChr(32, 64); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TGROM": //mega man 4 & 6
AssertPrg(128, 256, 512); AssertChr(0); AssertVram(8); AssertWram(0);
AssertBattery(false);
break;
case "NES-TKROM": //kirby's adventure
AssertPrg(128, 256, 512); AssertChr(128, 256); AssertVram(0); AssertWram(8);
break;
case "NES-TLROM": //mega man 3
AssertPrg(128, 256, 512); AssertChr(128, 256); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TL1ROM": //Double dragon 2
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TL2ROM": //batman (U) ?
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TSROM": //super mario bros. 3 (U)
AssertPrg(128, 256, 512); AssertChr(128, 256); AssertVram(0); AssertWram(8);
AssertBattery(false);
break;
case "NES-B4": //batman (U)
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
default:
return false;
}
BaseSetup();
return true;
}
}
}

View File

@ -225,7 +225,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
if (!Cart.wram_battery) return null;
return WRAM;
//some boards have a pram that is backed-up or not backed-up. need to handle that somehow
//some boards have a wram that is backed-up or not backed-up. need to handle that somehow
//(nestopia splits it into NVWRAM and WRAM but i didnt like that at first.. but it may player better with this architecture)
}
}

View File

@ -1,322 +0,0 @@
using System;
using System.IO;
using System.Diagnostics;
//TODO - mapper 118 and 119 need to be gracefully worked in here.
//but we might want to break them down into a new board type and reuse class mmc3. be sure to try that to see how it goes
namespace BizHawk.Emulation.Consoles.Nintendo
{
public class TxROM_DxROM : NES.NESBoardBase
{
class MMC3 : IDisposable
{
NES.NESBoardBase board;
public MMC3(NES.NESBoardBase board, int num_prg_banks)
{
this.board = board;
bank_regs[8] = (byte)(num_prg_banks - 1);
bank_regs[9] = (byte)(num_prg_banks - 2);
}
public void Dispose()
{
bank_regs.Dispose();
prg_lookup.Dispose();
}
//state
int chr_mode, prg_mode, reg_addr;
public NES.NESBoardBase.EMirrorType mirror;
int ppubus_state, ppubus_statecounter;
//this contains the 8 programmable regs and 2 more at the end to represent PRG banks -2 and -1; and 4 more at the end to break down chr regs 0 and 1
ByteBuffer bank_regs = new ByteBuffer(14);
ByteBuffer prg_lookup = new ByteBuffer(new byte[] { 6, 7, 9, 8, 9, 7, 6, 8 });
ByteBuffer chr_lookup = new ByteBuffer(new byte[] { 10, 11, 12, 13, 2, 3, 4, 5 });
byte irq_reload, irq_counter;
bool irq_pending, irq_enable;
void SyncIRQ()
{
board.NES.irq_cart = irq_pending;
}
public void WritePRG(int addr, byte value)
{
switch (addr & 0x6001)
{
case 0x0000: //$8000
chr_mode = (value >> 7) & 1;
prg_mode = (value >> 6) & 1;
reg_addr = (value & 7);
break;
case 0x0001: //$8001
bank_regs[reg_addr] = value;
//setup the 2K chr regs
bank_regs[10] = (byte)((bank_regs[0] & ~1) + 0);
bank_regs[11] = (byte)((bank_regs[0] & ~1) + 1);
bank_regs[12] = (byte)((bank_regs[1] & ~1) + 0);
bank_regs[13] = (byte)((bank_regs[1] & ~1) + 1);
break;
case 0x2000: //$A000
//mirroring
if ((value & 1) == 0) mirror = EMirrorType.Vertical;
else mirror = EMirrorType.Horizontal;
break;
case 0x2001: //$A001
//wram enable/protect
break;
case 0x4000: //$C000
irq_reload = value;
break;
case 0x4001: //$C001
irq_counter = 0;
break;
case 0x6000: //$E000
irq_enable = false;
irq_pending = false;
SyncIRQ();
break;
case 0x6001: //$E001
irq_enable = true;
SyncIRQ();
break;
}
}
public int Get_PRGBank_8K(int addr)
{
int bank_8k = addr >> 13;
bank_8k = bank_regs[prg_lookup[prg_mode * 4 + bank_8k]];
return bank_8k;
}
public int Get_CHRBank_1K(int addr)
{
int bank_1k = addr >> 10;
if (chr_mode == 1)
bank_1k ^= 4;
bank_1k = bank_regs[chr_lookup[bank_1k]];
return bank_1k;
}
void ClockIRQ()
{
if (irq_counter == 0)
irq_counter = irq_reload;
else
{
irq_counter--;
//Console.WriteLine(irq_counter);
if (irq_counter == 0)
{
if (irq_enable)
irq_pending = true;
SyncIRQ();
}
}
}
//TODO - this should be determined from NES timestamps to correctly emulate ppu writes interfering
public void Tick_PPU(int addr)
{
ppubus_statecounter++;
int state = (addr >> 12) & 1;
if (ppubus_state == 0 && ppubus_statecounter > 1 && state == 1)
{
ppubus_statecounter = 0;
ClockIRQ();
}
if (ppubus_state != state)
{
ppubus_state = state;
}
}
}
//configuration
int prg_mask, chr_mask;
int wram_mask, vram_mask;
int vram_bytes;
bool fourscreen;
//state
MMC3 mmc3;
public override void Dispose()
{
mmc3.Dispose();
}
public override void WritePRG(int addr, byte value)
{
mmc3.WritePRG(addr, value);
SetMirrorType(mmc3.mirror); //often redundant, but gets the job done
}
public override byte ReadPRG(int addr)
{
int bank_8k = mmc3.Get_PRGBank_8K(addr);
bank_8k &= prg_mask;
addr = (bank_8k << 13) | (addr & 0x1FFF);
return ROM[addr];
}
public override byte ReadPPU(int addr)
{
mmc3.Tick_PPU(addr);
if (addr < 0x2000)
{
int bank_1k = mmc3.Get_CHRBank_1K(addr);
bank_1k &= chr_mask;
addr = (bank_1k << 10) | (addr & 0x3FF);
if (VROM != null)
return VROM[addr];
else return VRAM[addr];
}
else if (fourscreen)
{
int myaddr = addr - 0x2000;
if (myaddr < vram_bytes)
return VRAM[myaddr];
else
return base.ReadPPU(addr);
}
else return base.ReadPPU(addr);
}
public override void WritePPU(int addr, byte value)
{
mmc3.Tick_PPU(addr);
if (fourscreen)
{
if (addr >= 0x2000)
{
int myaddr = addr - 0x2000;
if (myaddr < vram_bytes)
VRAM[myaddr] = value;
else
base.WritePPU(addr, value);
}
else base.WritePPU(addr, value);
}
else base.WritePPU(addr, value);
}
public override byte ReadWRAM(int addr)
{
if (Cart.wram_size != 0)
return WRAM[addr & wram_mask];
else return 0xFF;
}
public override void WriteWRAM(int addr, byte value)
{
if (Cart.wram_size != 0)
WRAM[addr & wram_mask] = value;
}
public override byte[] SaveRam
{
get
{
if (!Cart.wram_battery) return null;
return WRAM;
//some boards have a pram that is backed-up or not backed-up. need to handle that somehow
//(nestopia splits it into NVWRAM and WRAM but i didnt like that at first.. but it may player better with this architecture)
}
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
}
public override bool Configure(NES.EDetectionOrigin origin)
{
//analyze board type
switch (Cart.board_type)
{
case "NES-TBROM": //tecmo world cup soccer (DE) [untested]
AssertPrg(64); AssertChr(64); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TEROM": //Adv of lolo 2
AssertPrg(32); AssertChr(32); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TFROM": //legacy of the wizard
AssertPrg(128); AssertChr(32, 64); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TGROM": //mega man 4 & 6
AssertPrg(128, 256, 512); AssertChr(0); AssertVram(8); AssertWram(0);
AssertBattery(false);
break;
case "NES-TKROM": //kirby's adventure
AssertPrg(128, 256, 512); AssertChr(128, 256); AssertVram(0); AssertWram(8);
break;
case "NES-TLROM": //mega man 3
AssertPrg(128, 256, 512); AssertChr(128, 256); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TL1ROM": //Double dragon 2
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TL2ROM": //batman (U) ?
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-TQROM": //{mapper 119} high speed - ALMOST POSITIVELTY NEEDS WORK
AssertPrg(128); AssertChr(64); AssertVram(8); AssertWram(0);
AssertBattery(false);
break;
case "NES-TR1ROM": //gauntlet (U) - someone please test this
AssertPrg(128); AssertChr(64); AssertVram(2); AssertWram(0);
fourscreen = true;
AssertBattery(false);
break;
case "NES-TSROM": //super mario bros. 3 (U)
AssertPrg(128, 256, 512); AssertChr(128, 256); AssertVram(0); AssertWram(8);
AssertBattery(false);
break;
case "NES-TVROM": //rad racer II (U)
AssertPrg(64); AssertChr(64); AssertVram(8); AssertWram(0);
fourscreen = true;
AssertBattery(false);
break;
case "NES-B4": //batman (U)
AssertPrg(128); AssertChr(128); AssertVram(0); AssertWram(0);
AssertBattery(false);
break;
case "NES-DRROM": //gauntlet (U) sometimes
AssertPrg(128); AssertChr(64); AssertVram(2); AssertWram(0);
fourscreen = true;
AssertBattery(false);
break;
default:
return false;
}
//remember to setup the PRG banks -1 and -2
int num_prg_banks = Cart.prg_size / 8;
prg_mask = num_prg_banks - 1;
int num_chr_banks = (Cart.chr_size);
chr_mask = num_chr_banks - 1;
wram_mask = (Cart.wram_size * 1024) - 1;
vram_mask = (Cart.vram_size * 1024) - 1;
vram_bytes = Cart.vram_size * 1024;
mmc3 = new MMC3(this, num_prg_banks);
SetMirrorType(EMirrorType.Vertical);
return true;
}
}
}