diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 9920c794a0..0088131b0f 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -56,6 +56,7 @@ + @@ -85,6 +86,7 @@ Code + Code diff --git a/BizHawk.Emulation/Buffer.cs b/BizHawk.Emulation/Buffer.cs new file mode 100644 index 0000000000..c8c9275330 --- /dev/null +++ b/BizHawk.Emulation/Buffer.cs @@ -0,0 +1,75 @@ +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Text; + +namespace BizHawk +{ + /// + /// Implements a data simple data buffer with proper life cycle and no bounds checking + /// + public unsafe class CBuffer : IDisposable + { + public GCHandle hnd; + public T[] arr; + public void* ptr; + public byte* byteptr; + public int len; + + public static CBuffer malloc(int amt) + { + return new CBuffer(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 + { + 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); } } + } + +} \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs index c7287fb5b1..7a4049f909 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/BoardSystem.cs @@ -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 diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/TxROM.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/TxROM.cs new file mode 100644 index 0000000000..e7adc0a67d --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/TxROM.cs @@ -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; + } + + } +}