[NES] big mmc3 reorg and many new games
This commit is contained in:
parent
21703d9f6a
commit
fba7989f0a
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue