[NES] 50% of mmc3, enough to boot some games

This commit is contained in:
zeromus 2011-05-20 09:30:20 +00:00
parent fa503a17e9
commit c5cc2b8b5e
4 changed files with 282 additions and 1 deletions

View File

@ -56,6 +56,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Buffer.cs" />
<Compile Include="Consoles\Calculator\TI83.cs" />
<Compile Include="Consoles\Nintendo\NES\APU.cs" />
<Compile Include="Consoles\Nintendo\NES\BoardSystem.cs" />
@ -85,6 +86,7 @@
<Compile Include="Consoles\Nintendo\NES\Boards\SxROM.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Consoles\Nintendo\NES\Boards\TxROM.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\UxROM.cs">
<SubType>Code</SubType>
</Compile>

View File

@ -0,0 +1,75 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Text;
namespace BizHawk
{
/// <summary>
/// Implements a data simple data buffer with proper life cycle and no bounds checking
/// </summary>
public unsafe class CBuffer<T> : IDisposable
{
public GCHandle hnd;
public T[] arr;
public void* ptr;
public byte* byteptr;
public int len;
public static CBuffer<T> malloc(int amt)
{
return new CBuffer<T>(amt);
}
public void Write08(uint addr, byte val) { byteptr[addr] = val; }
public void Write16(uint addr, ushort val) { *(ushort*)(byteptr + addr) = val; }
public void Write32(uint addr, uint val) { *(uint*)(byteptr + addr) = val; }
public void Write64(uint addr, ulong val) { *(ulong*)(byteptr + addr) = val; }
public byte Read08(uint addr) { return byteptr[addr]; }
public ushort Read16(uint addr) { return *(ushort*)(byteptr + addr); }
public uint Read32(uint addr) { return *(uint*)(byteptr + addr); }
public ulong Read64(uint addr) { return *(ulong*)(byteptr + addr); }
public void Write08(int addr, byte val) { byteptr[addr] = val; }
public void Write16(int addr, ushort val) { *(ushort*)(byteptr + addr) = val; }
public void Write32(int addr, uint val) { *(uint*)(byteptr + addr) = val; }
public void Write64(int addr, ulong val) { *(ulong*)(byteptr + addr) = val; }
public byte Read08(int addr) { return byteptr[addr]; }
public ushort Read16(int addr) { return *(ushort*)(byteptr + addr); }
public uint Read32(int addr) { return *(uint*)(byteptr + addr); }
public ulong Read64(int addr) { return *(ulong*)(byteptr + addr); }
public CBuffer(T[] arr)
{
len = arr.Length;
this.arr = arr;
hnd = GCHandle.Alloc(arr, GCHandleType.Pinned);
ptr = hnd.AddrOfPinnedObject().ToPointer();
byteptr = (byte*)ptr;
}
public CBuffer(int amt)
{
len = amt;
arr = new T[amt];
hnd = GCHandle.Alloc(arr, GCHandleType.Pinned);
ptr = hnd.AddrOfPinnedObject().ToPointer();
byteptr = (byte*)ptr;
}
public void Dispose()
{
if (arr != null)
hnd.Free();
arr = null;
}
~CBuffer() { Dispose(); }
}
public class ByteBuffer : CBuffer<byte>
{
public ByteBuffer(int amt) : base(amt) { }
public ByteBuffer(byte[] arr) : base(arr) { }
public byte this[int index] { get { return Read08(index); } set { Write08(index, value); } }
}
}

View File

@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
{
partial class NES
{
public interface INESBoard
public interface INESBoard : IDisposable
{
void Create(NES nes);
bool Configure(NES.EDetectionOrigin origin);
@ -59,6 +59,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
for (int i = 0; i < 4; i++) ser.Sync("mirroring" + i, ref mirroring[i]);
}
public virtual void Dispose() { }
int[] mirroring = new int[4];
protected void SetMirroring(int a, int b, int c, int d)
@ -179,6 +180,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
foreach (int i in valid) if (value == i) return;
Assert(false, "unhandled {0} size", name);
}
protected void AssertBattery(bool has_bat) { Assert(Cart.wram_battery == has_bat); }
}
//this will be used to track classes that implement boards

View File

@ -0,0 +1,202 @@
using System;
using System.IO;
using System.Diagnostics;
namespace BizHawk.Emulation.Consoles.Nintendo
{
public class TxROM : NES.NESBoardBase
{
class MMC3 : IDisposable
{
public MMC3(NES.NESBoardBase board, 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
int chr_mode, prg_mode, reg_addr;
public NES.NESBoardBase.EMirrorType mirror;
//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 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
break;
case 0x4001: //$C001
//IRQ clear
break;
case 0x6000: //$E000
//IRQ ack/disable
break;
case 0x6001: //$E001
//IRQ enable
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;
}
}
//configuration
int prg_mask, chr_mask;
int wram_mask;
//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)
{
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 return base.ReadPPU(addr);
}
public override void WritePPU(int addr, byte value)
{
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-TSROM": //super mario bros. 3 USA
AssertPrg(128,256,512); AssertChr(128,256); AssertVram(0); AssertWram(8);
AssertBattery(false);
break;
case "NES-TGROM": //mega man 4
AssertPrg(128, 256, 512); AssertChr(0); AssertVram(8); AssertWram(0);
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);
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;
mmc3 = new MMC3(this, num_prg_banks);
SetMirrorType(EMirrorType.Vertical);
return true;
}
}
}