nes-mmc3/namcot108 differences have become more clear in the past few months. refactor those mappers (split them apart, theyre not as similar as once thought) to reflect new understandings. no functional improvement from this other than maybe some new bugs, but it will help on some other mappers later

This commit is contained in:
zeromus 2012-06-15 19:24:06 +00:00
parent 931c9c9b23
commit 916066c3f0
11 changed files with 335 additions and 175 deletions

View File

@ -154,23 +154,24 @@
<Compile Include="Consoles\Nintendo\NES\Boards\Mapper242.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Mapper078.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Mapper046.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\DRROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\HKROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper012.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper044.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper049.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper095.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper115.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper182.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper189.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\Mapper191.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\MMC3.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\MMC3_family\NES-QJ.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\Namcot1xx\DRROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Namcot1xx\Mapper095.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Namcot1xx\Mapper206.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Namcot1xx\Namcot1xx.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\NAMCOT_m19_m210.cs">
<SubType>Code</SubType>
</Compile>

View File

@ -1,121 +1,23 @@
//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
//this file contains the MMC3 family of boards
//fceux contains a comment in mmc3.cpp:
//Code for emulating iNES mappers 4,12,44,45,47,49,52,74,114,115,116,118,119,165,205,214,215,245,249,250,254
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
// this is the base class for the MMC3 mapper
public class Namcot109 : IDisposable
public class MMC3 : IDisposable
{
//state
public int chr_mode, prg_mode, reg_addr;
int reg_addr;
bool chr_mode, prg_mode;
ByteBuffer chr_regs_1k = new ByteBuffer(8);
ByteBuffer prg_regs_8k = new ByteBuffer(8);
ByteBuffer prg_regs_8k = new ByteBuffer(4);
ByteBuffer regs = new ByteBuffer(8);
protected NES.NESBoardBase board;
public Namcot109(NES.NESBoardBase board)
{
this.board = board;
prg_regs_8k[0] = 0;
prg_regs_8k[1] = 1;
prg_regs_8k[2] = 0xFE; //constant
prg_regs_8k[3] = 0xFF; //constant
prg_regs_8k[4 + 0] = 0xFE; //constant
prg_regs_8k[4 + 1] = 1;
prg_regs_8k[4 + 2] = 0;
prg_regs_8k[4 + 3] = 0xFF; //constant
chr_regs_1k[0] = 0;
chr_regs_1k[1] = 1;
chr_regs_1k[2] = 2;
chr_regs_1k[3] = 3;
chr_regs_1k[4] = 4;
chr_regs_1k[5] = 5;
chr_regs_1k[6] = 6;
chr_regs_1k[7] = 7;
}
public void Dispose()
{
chr_regs_1k.Dispose();
prg_regs_8k.Dispose();
}
public virtual void SyncState(Serializer ser)
{
ser.Sync("chr_mode", ref chr_mode);
ser.Sync("prg_mode", ref prg_mode);
ser.Sync("reg_addr", ref reg_addr);
ser.Sync("chr_regs_1k", ref chr_regs_1k);
ser.Sync("prg_regs_8k", ref prg_regs_8k);
}
public virtual void WritePRG(int addr, byte value)
{
switch (addr & 0x6001)
{
case 0x0000: //$8000
chr_mode = (value >> 7) & 1;
chr_mode <<= 2;
prg_mode = (value >> 6) & 1;
prg_mode <<= 2;
reg_addr = (value & 7);
break;
case 0x0001: //$8001
switch (reg_addr)
{
case 0: chr_regs_1k[0] = (byte)(value & ~1); chr_regs_1k[1] = (byte)(value | 1); break;
case 1: chr_regs_1k[2] = (byte)(value & ~1); chr_regs_1k[3] = (byte)(value | 1); break;
case 2: chr_regs_1k[4] = value; break;
case 3: chr_regs_1k[5] = value; break;
case 4: chr_regs_1k[6] = value; break;
case 5: chr_regs_1k[7] = value; break;
case 6: prg_regs_8k[0] = value; prg_regs_8k[4 + 2] = value; break;
case 7: prg_regs_8k[1] = value; prg_regs_8k[4 + 1] = value; break;
}
break;
}
}
public int Get_PRGBank_8K(int addr)
{
int bank_8k = addr >> 13;
bank_8k += prg_mode;
bank_8k = prg_regs_8k[bank_8k];
return bank_8k;
}
public int Get_CHRBank_1K(int addr)
{
int bank_1k = addr >> 10;
bank_1k ^= chr_mode;
bank_1k = chr_regs_1k[bank_1k];
return bank_1k;
}
}
public class MMC3 : Namcot109
{
//state
public byte mirror;
int a12_old;
byte irq_reload, irq_counter;
@ -147,22 +49,80 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
bool oldIrqType;
public void Dispose()
{
chr_regs_1k.Dispose();
prg_regs_8k.Dispose();
regs.Dispose();
}
public NES.NESBoardBase.EMirrorType MirrorType { get { return mirror == 0 ? NES.NESBoardBase.EMirrorType.Vertical : NES.NESBoardBase.EMirrorType.Horizontal; } }
protected NES.NESBoardBase board;
public MMC3(NES.NESBoardBase board, int num_prg_banks)
: base(board)
{
this.board = board;
if (board.Cart.chips.Contains("MMC3A")) MMC3Type = EMMC3Type.MMC3A;
else if (board.Cart.chips.Contains("MMC3B")) MMC3Type = EMMC3Type.MMC3BSharp;
else if (board.Cart.chips.Contains("MMC3BNONSHARP")) MMC3Type = EMMC3Type.MMC3BNonSharp;
else if (board.Cart.chips.Contains("MMC3C")) MMC3Type = EMMC3Type.MMC3C;
else MMC3Type = EMMC3Type.MMC3C; //arbitrary choice. is it the best choice?
Sync();
}
public override void SyncState(Serializer ser)
void Sync()
{
base.SyncState(ser);
if (prg_mode)
{
prg_regs_8k[0] = 0xFE;
prg_regs_8k[1] = regs[7];
prg_regs_8k[2] = regs[6];
prg_regs_8k[3] = 0xFF;
}
else
{
prg_regs_8k[0] = regs[6];
prg_regs_8k[1] = regs[7];
prg_regs_8k[2] = 0xFE;
prg_regs_8k[3] = 0xFF;
}
byte r0_0 = (byte)(regs[0] & ~1);
byte r0_1 = (byte)(regs[0] | 1);
byte r1_0 = (byte)(regs[1] & ~1);
byte r1_1 = (byte)(regs[1] | 1);
if (chr_mode)
{
chr_regs_1k[0] = regs[2];
chr_regs_1k[1] = regs[3];
chr_regs_1k[2] = regs[4];
chr_regs_1k[3] = regs[5];
chr_regs_1k[4] = r0_0;
chr_regs_1k[5] = r0_1;
chr_regs_1k[6] = r1_0;
chr_regs_1k[7] = r1_1;
}
else
{
chr_regs_1k[0] = r0_0;
chr_regs_1k[1] = r0_1;
chr_regs_1k[2] = r1_0;
chr_regs_1k[3] = r1_1;
chr_regs_1k[4] = regs[2];
chr_regs_1k[5] = regs[3];
chr_regs_1k[6] = regs[4];
chr_regs_1k[7] = regs[5];
}
}
public virtual void SyncState(Serializer ser)
{
ser.Sync("reg_addr", ref reg_addr);
ser.Sync("chr_mode", ref chr_mode);
ser.Sync("prg_mode", ref prg_mode);
ser.Sync("regs", ref regs);
ser.Sync("mirror", ref mirror);
ser.Sync("a12_old", ref a12_old);
ser.Sync("irq_reload", ref irq_reload);
@ -174,6 +134,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
ser.Sync("irq_reload_flag", ref irq_reload_flag);
ser.Sync("wram_enable", ref wram_enable);
ser.Sync("wram_write_protect", ref wram_write_protect);
Sync();
}
protected virtual void SyncIRQ()
@ -181,13 +142,19 @@ namespace BizHawk.Emulation.Consoles.Nintendo
board.NES.irq_cart = irq_pending;
}
public override void WritePRG(int addr, byte value)
public void WritePRG(int addr, byte value)
{
switch (addr & 0x6001)
{
case 0x0000: //$8000
chr_mode = value.Bit(7);
prg_mode = value.Bit(6);
reg_addr = (value & 7);
Sync();
break;
case 0x0001: //$8001
base.WritePRG(addr, value);
regs[reg_addr] = value;
Sync();
break;
case 0x2000: //$A000
//mirroring
@ -272,6 +239,20 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
}
public virtual int Get_PRGBank_8K(int addr)
{
int bank_8k = addr >> 13;
bank_8k = prg_regs_8k[bank_8k];
return bank_8k;
}
public virtual int Get_CHRBank_1K(int addr)
{
int bank_1k = addr >> 10;
bank_1k = chr_regs_1k[bank_1k];
return bank_1k;
}
public virtual void AddressPPU(int addr)
{
@ -292,30 +273,45 @@ namespace BizHawk.Emulation.Consoles.Nintendo
a12_old = a12;
}
}
public abstract class MMC3_Family_Board_Base : NES.NESBoardBase
public abstract class MMC3Board_Base : NES.NESBoardBase
{
protected Namcot109 mapper;
//state
protected MMC3 mmc3;
public override void AddressPPU(int addr)
{
mmc3.AddressPPU(addr);
}
public override void ClockPPU()
{
mmc3.ClockPPU();
}
//configuration
protected int prg_mask, chr_mask;
public override void Dispose()
{
mapper.Dispose();
mmc3.Dispose();
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
mapper.SyncState(ser);
mmc3.SyncState(ser);
}
protected virtual int Get_CHRBank_1K(int addr)
{
return mapper.Get_CHRBank_1K(addr);
return mmc3.Get_CHRBank_1K(addr);
}
protected virtual int Get_PRGBank_8K(int addr)
{
return mmc3.Get_PRGBank_8K(addr);
}
int MapCHR(int addr)
@ -352,12 +348,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override void WritePRG(int addr, byte value)
{
mapper.WritePRG(addr, value);
}
protected virtual int Get_PRGBank_8K(int addr)
{
return mapper.Get_PRGBank_8K(addr);
mmc3.WritePRG(addr, value);
}
public override byte ReadPRG(int addr)
@ -370,58 +361,28 @@ namespace BizHawk.Emulation.Consoles.Nintendo
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;
mmc3 = new MMC3(this, num_prg_banks);
SetMirrorType(EMirrorType.Vertical);
}
//used by a couple of boards for controlling nametable wiring with the mapper
protected int RewireNametable_Mapper095_and_TLSROM(int addr, int bitsel)
protected int RewireNametable_TLSROM(int addr, int bitsel)
{
int bank_1k = mapper.Get_CHRBank_1K(addr & 0x1FFF);
int bank_1k = mmc3.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
{
//state
protected MMC3 mmc3;
public override void AddressPPU(int addr)
{
mmc3.AddressPPU(addr);
}
public override void ClockPPU()
{
mmc3.ClockPPU();
}
protected override void BaseSetup()
{
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()
{
mapper = new Namcot109(this);
base.BaseSetup();
}
}
}

View File

@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
protected override int Get_PRGBank_8K(int addr)
{
int bank_8k = mapper.Get_PRGBank_8K(addr);
int bank_8k = mmc3.Get_PRGBank_8K(addr);
return (bank_8k & PRG_AND[block_select]) | PRG_OR[block_select];
}

View File

@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
protected override int Get_PRGBank_8K(int addr)
{
if (mode)
return (mapper.Get_PRGBank_8K(addr)&0xF) + block * (128 / 8);
return (mmc3.Get_PRGBank_8K(addr)&0xF) + block * (128 / 8);
int block_offset = addr >> 13;
return prg * 4 + block_offset;
}

View File

@ -54,7 +54,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
protected override int Get_PRGBank_8K(int addr)
{
int bank_8k = mapper.Get_PRGBank_8K(addr);
int bank_8k = mmc3.Get_PRGBank_8K(addr);
if (prg_mode == false) return bank_8k;
else if (addr < 0x4000)
{

View File

@ -17,13 +17,13 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return false;
}
throw new InvalidOperationException("THIS MAPPER ISNT TESTED! WHAT GAME USES IT? PLEASE REPORT!");
//this board has 2k of chr ram
Cart.vram_size = 2;
BaseSetup();
return true;
throw new InvalidOperationException("THIS MAPPER ISNT TESTED! WHAT GAME USES IT? PLEASE REPORT!");
//return true;
}
public override byte ReadPPU(int addr)

View File

@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
if (addr < 0x2000)
{
int bank_1k = mapper.Get_CHRBank_1K(addr);
int bank_1k = mmc3.Get_CHRBank_1K(addr);
int use_ram = (bank_1k >> 6) & 1;
if (use_ram == 1)
{
@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
if (addr < 0x2000)
{
int bank_1k = mapper.Get_CHRBank_1K(addr);
int bank_1k = mmc3.Get_CHRBank_1K(addr);
int use_ram = (bank_1k >> 6) & 1;
if (use_ram == 1)
{

View File

@ -5,7 +5,7 @@ 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 class DRROM : Namcot108Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{

View File

@ -6,7 +6,7 @@ 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 class Mapper095 : Namcot108Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{
@ -26,15 +26,26 @@ namespace BizHawk.Emulation.Consoles.Nintendo
return true;
}
int RewireNametable(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;
}
//mapper 095's chief unique contribution is to add this nametable rewiring logic: CHR A15 directly controls CIRAM A10
public override byte ReadPPU(int addr)
{
if (addr < 0x2000) return base.ReadPPU(addr);
else return base.ReadPPU(RewireNametable_Mapper095_and_TLSROM(addr, 5));
else return base.ReadPPU(RewireNametable(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);
else base.WritePPU(RewireNametable(addr, 5), value);
}
}
}

View File

@ -4,8 +4,8 @@ using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
//various japanese namcot 109 boards plus DEROM
public class Mapper206 : Namcot109Board_Base
//various japanese Namcot108 boards plus DEROM
public class Mapper206 : Namcot108Board_Base
{
public override bool Configure(NES.EDetectionOrigin origin)
{

View File

@ -0,0 +1,187 @@
//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 108
//but mostly http://wiki.nesdev.com/w/index.php/INES_Mapper_206
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
//also, Namcot109, Namcot118, Namcot119 chips are this exact same thing
public class Namcot108Chip : IDisposable
{
//state
int reg_addr;
ByteBuffer chr_regs_1k = new ByteBuffer(8);
ByteBuffer prg_regs_8k = new ByteBuffer(4);
NES.NESBoardBase board;
public Namcot108Chip(NES.NESBoardBase board)
{
this.board = board;
prg_regs_8k[0] = 0;
prg_regs_8k[1] = 1;
prg_regs_8k[2] = 0xFE; //constant
prg_regs_8k[3] = 0xFF; //constant
chr_regs_1k[0] = 0;
chr_regs_1k[1] = 1;
chr_regs_1k[2] = 2;
chr_regs_1k[3] = 3;
chr_regs_1k[4] = 4;
chr_regs_1k[5] = 5;
chr_regs_1k[6] = 6;
chr_regs_1k[7] = 7;
}
public void Dispose()
{
chr_regs_1k.Dispose();
prg_regs_8k.Dispose();
}
public virtual void SyncState(Serializer ser)
{
ser.Sync("reg_addr", ref reg_addr);
ser.Sync("chr_regs_1k", ref chr_regs_1k);
ser.Sync("prg_regs_8k", ref prg_regs_8k);
}
public virtual void WritePRG(int addr, byte value)
{
switch (addr & 0x6001)
{
case 0x0000: //$8000
reg_addr = (value & 7);
break;
case 0x0001: //$8001
switch (reg_addr)
{
//bottom bits of these chr regs are ignored
case 0:
chr_regs_1k[0] = (byte)(value & ~1);
chr_regs_1k[1] = (byte)(value | 1);
break;
case 1:
chr_regs_1k[2] = (byte)(value & ~1);
chr_regs_1k[3] = (byte)(value | 1);
break;
case 2: chr_regs_1k[4] = value; break;
case 3: chr_regs_1k[5] = value; break;
case 4: chr_regs_1k[6] = value; break;
case 5: chr_regs_1k[7] = value; break;
case 6: prg_regs_8k[0] = value; break;
case 7: prg_regs_8k[1] = value; break;
}
break;
}
}
public int Get_PRGBank_8K(int addr)
{
int bank_8k = addr >> 13;
bank_8k = prg_regs_8k[bank_8k];
return bank_8k;
}
public int Get_CHRBank_1K(int addr)
{
int bank_1k = addr >> 10;
bank_1k = chr_regs_1k[bank_1k];
return bank_1k;
}
}
public abstract class Namcot108Board_Base : NES.NESBoardBase
{
//state
protected Namcot108Chip mapper;
//configuration
protected int prg_mask, chr_mask;
public override void Dispose()
{
mapper.Dispose();
}
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
mapper.SyncState(ser);
}
public int Get_CHRBank_1K(int addr)
{
return mapper.Get_CHRBank_1K(addr);
}
public int Get_PRGBank_8K(int addr)
{
return mapper.Get_PRGBank_8K(addr);
}
int MapCHR(int addr)
{
int bank_1k = Get_CHRBank_1K(addr);
bank_1k &= chr_mask;
addr = (bank_1k << 10) | (addr & 0x3FF);
return addr;
}
public override byte ReadPPU(int addr)
{
if (addr < 0x2000)
{
addr = MapCHR(addr);
if (VROM != null)
return VROM[addr];
else return VRAM[addr];
}
else return base.ReadPPU(addr);
}
public override void WritePPU(int addr, byte value)
{
if (addr < 0x2000)
{
if (VRAM == null) return;
addr = MapCHR(addr);
VRAM[addr] = 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 = Get_PRGBank_8K(addr);
bank_8k &= prg_mask;
addr = (bank_8k << 13) | (addr & 0x1FFF);
return ROM[addr];
}
protected virtual void BaseSetup()
{
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;
mapper = new Namcot108Chip(this);
SetMirrorType(EMirrorType.Vertical);
}
}
}