namespace GarboDev { using System; using System.Collections.Generic; public class Memory { public const uint REG_BASE = 0x4000000; public const uint PAL_BASE = 0x5000000; public const uint VRAM_BASE = 0x6000000; public const uint OAM_BASE = 0x7000000; public const uint DISPCNT = 0x0; public const uint DISPSTAT = 0x4; public const uint VCOUNT = 0x6; public const uint BG0CNT = 0x8; public const uint BG1CNT = 0xA; public const uint BG2CNT = 0xC; public const uint BG3CNT = 0xE; public const uint BG0HOFS = 0x10; public const uint BG0VOFS = 0x12; public const uint BG1HOFS = 0x14; public const uint BG1VOFS = 0x16; public const uint BG2HOFS = 0x18; public const uint BG2VOFS = 0x1A; public const uint BG3HOFS = 0x1C; public const uint BG3VOFS = 0x1E; public const uint BG2PA = 0x20; public const uint BG2PB = 0x22; public const uint BG2PC = 0x24; public const uint BG2PD = 0x26; public const uint BG2X_L = 0x28; public const uint BG2X_H = 0x2A; public const uint BG2Y_L = 0x2C; public const uint BG2Y_H = 0x2E; public const uint BG3PA = 0x30; public const uint BG3PB = 0x32; public const uint BG3PC = 0x34; public const uint BG3PD = 0x36; public const uint BG3X_L = 0x38; public const uint BG3X_H = 0x3A; public const uint BG3Y_L = 0x3C; public const uint BG3Y_H = 0x3E; public const uint WIN0H = 0x40; public const uint WIN1H = 0x42; public const uint WIN0V = 0x44; public const uint WIN1V = 0x46; public const uint WININ = 0x48; public const uint WINOUT = 0x4A; public const uint BLDCNT = 0x50; public const uint BLDALPHA = 0x52; public const uint BLDY = 0x54; public const uint SOUNDCNT_L = 0x80; public const uint SOUNDCNT_H = 0x82; public const uint SOUNDCNT_X = 0x84; public const uint FIFO_A_L = 0xA0; public const uint FIFO_A_H = 0xA2; public const uint FIFO_B_L = 0xA4; public const uint FIFO_B_H = 0xA6; public const uint DMA0SAD = 0xB0; public const uint DMA0DAD = 0xB4; public const uint DMA0CNT_L = 0xB8; public const uint DMA0CNT_H = 0xBA; public const uint DMA1SAD = 0xBC; public const uint DMA1DAD = 0xC0; public const uint DMA1CNT_L = 0xC4; public const uint DMA1CNT_H = 0xC6; public const uint DMA2SAD = 0xC8; public const uint DMA2DAD = 0xCC; public const uint DMA2CNT_L = 0xD0; public const uint DMA2CNT_H = 0xD2; public const uint DMA3SAD = 0xD4; public const uint DMA3DAD = 0xD8; public const uint DMA3CNT_L = 0xDC; public const uint DMA3CNT_H = 0xDE; public const uint TM0D = 0x100; public const uint TM0CNT = 0x102; public const uint TM1D = 0x104; public const uint TM1CNT = 0x106; public const uint TM2D = 0x108; public const uint TM2CNT = 0x10A; public const uint TM3D = 0x10C; public const uint TM3CNT = 0x10E; public const uint KEYINPUT = 0x130; public const uint KEYCNT = 0x132; public const uint IE = 0x200; public const uint IF = 0x202; public const uint IME = 0x208; public const uint HALTCNT = 0x300; private const uint biosRamMask = 0x3FFF; private const uint ewRamMask = 0x3FFFF; private const uint iwRamMask = 0x7FFF; private const uint ioRegMask = 0x4FF; private const uint vRamMask = 0x1FFFF; private const uint palRamMask = 0x3FF; private const uint oamRamMask = 0x3FF; private const uint sRamMask = 0xFFFF; private byte[] biosRam = new byte[Memory.biosRamMask + 1]; private byte[] ewRam = new byte[Memory.ewRamMask + 1]; private byte[] iwRam = new byte[Memory.iwRamMask + 1]; private byte[] ioReg = new byte[Memory.ioRegMask + 1]; private byte[] vRam = new byte[Memory.vRamMask + 1]; private byte[] palRam = new byte[Memory.palRamMask + 1]; private byte[] oamRam = new byte[Memory.oamRamMask + 1]; private byte[] sRam = new byte[Memory.sRamMask + 1]; public byte[] VideoRam { get { return this.vRam; } } public byte[] PaletteRam { get { return this.palRam; } } public byte[] OamRam { get { return this.oamRam; } } public byte[] IORam { get { return this.ioReg; } } private ushort keyState = 0x3FF; public ushort KeyState { get { return this.keyState; } set { this.keyState = value; } } private Arm7Processor processor = null; public Arm7Processor Processor { get { return this.processor; } set { this.processor = value; } } private SoundManager soundManager = null; public SoundManager SoundManager { get { return this.soundManager; } set { this.soundManager = value; } } private byte[] romBank1 = null; private byte[] romBank2 = null; private uint romBank1Mask = 0; private uint romBank2Mask = 0; private int[] bankSTimes = new int[0x10]; private int[] bankNTimes = new int[0x10]; private int waitCycles = 0; public int WaitCycles { get { int tmp = this.waitCycles; this.waitCycles = 0; return tmp; } } private bool inUnreadable = false; private delegate byte ReadU8Delegate(uint address); private delegate void WriteU8Delegate(uint address, byte value); private delegate ushort ReadU16Delegate(uint address); private delegate void WriteU16Delegate(uint address, ushort value); private delegate uint ReadU32Delegate(uint address); private delegate void WriteU32Delegate(uint address, uint value); private ReadU8Delegate[] ReadU8Funcs = null; private WriteU8Delegate[] WriteU8Funcs = null; private ReadU16Delegate[] ReadU16Funcs = null; private WriteU16Delegate[] WriteU16Funcs = null; private ReadU32Delegate[] ReadU32Funcs = null; private WriteU32Delegate[] WriteU32Funcs = null; private uint[,] dmaRegs = new uint[4, 4]; private uint[] timerCnt = new uint[4]; private int[] bgx = new int[2], bgy = new int[2]; public uint[] TimerCnt { get { return this.timerCnt; } } public int[] Bgx { get { return this.bgx; } } public int[] Bgy { get { return this.bgy; } } public Memory() { this.ReadU8Funcs = new ReadU8Delegate[] { this.ReadBiosRam8, this.ReadNop8, this.ReadEwRam8, this.ReadIwRam8, this.ReadIO8, this.ReadPalRam8, this.ReadVRam8, this.ReadOamRam8, this.ReadNop8, this.ReadNop8, this.ReadNop8, this.ReadNop8, this.ReadNop8, this.ReadNop8, this.ReadSRam8, this.ReadNop8 }; this.WriteU8Funcs = new WriteU8Delegate[] { this.WriteNop8, this.WriteNop8, this.WriteEwRam8, this.WriteIwRam8, this.WriteIO8, this.WritePalRam8, this.WriteVRam8, this.WriteOamRam8, this.WriteNop8, this.WriteNop8, this.WriteNop8, this.WriteNop8, this.WriteNop8, this.WriteNop8, this.WriteSRam8, this.WriteNop8 }; this.ReadU16Funcs = new ReadU16Delegate[] { this.ReadBiosRam16, this.ReadNop16, this.ReadEwRam16, this.ReadIwRam16, this.ReadIO16, this.ReadPalRam16, this.ReadVRam16, this.ReadOamRam16, this.ReadNop16, this.ReadNop16, this.ReadNop16, this.ReadNop16, this.ReadNop16, this.ReadNop16, this.ReadSRam16, this.ReadNop16 }; this.WriteU16Funcs = new WriteU16Delegate[] { this.WriteNop16, this.WriteNop16, this.WriteEwRam16, this.WriteIwRam16, this.WriteIO16, this.WritePalRam16, this.WriteVRam16, this.WriteOamRam16, this.WriteNop16, this.WriteNop16, this.WriteNop16, this.WriteNop16, this.WriteNop16, this.WriteNop16, this.WriteSRam16, this.WriteNop16 }; this.ReadU32Funcs = new ReadU32Delegate[] { this.ReadBiosRam32, this.ReadNop32, this.ReadEwRam32, this.ReadIwRam32, this.ReadIO32, this.ReadPalRam32, this.ReadVRam32, this.ReadOamRam32, this.ReadNop32, this.ReadNop32, this.ReadNop32, this.ReadNop32, this.ReadNop32, this.ReadNop32, this.ReadSRam32, this.ReadNop32 }; this.WriteU32Funcs = new WriteU32Delegate[] { this.WriteNop32, this.WriteNop32, this.WriteEwRam32, this.WriteIwRam32, this.WriteIO32, this.WritePalRam32, this.WriteVRam32, this.WriteOamRam32, this.WriteNop32, this.WriteNop32, this.WriteNop32, this.WriteNop32, this.WriteNop32, this.WriteNop32, this.WriteSRam32, this.WriteNop32 }; } public void Reset() { Array.Clear(this.ewRam, 0, this.ewRam.Length); Array.Clear(this.iwRam, 0, this.iwRam.Length); Array.Clear(this.ioReg, 0, this.ioReg.Length); Array.Clear(this.vRam, 0, this.vRam.Length); Array.Clear(this.palRam, 0, this.palRam.Length); Array.Clear(this.oamRam, 0, this.oamRam.Length); Array.Clear(this.sRam, 0, this.sRam.Length); Memory.WriteU16(this.ioReg, Memory.BG2PA, 0x0100); Memory.WriteU16(this.ioReg, Memory.BG2PD, 0x0100); Memory.WriteU16(this.ioReg, Memory.BG3PA, 0x0100); Memory.WriteU16(this.ioReg, Memory.BG3PD, 0x0100); } public void HBlankDma() { for (int i = 0; i < 4; i++) { if (((this.dmaRegs[i, 3] >> 12) & 0x3) == 2) { this.DmaTransfer(i); } } } public void VBlankDma() { for (int i = 0; i < 4; i++) { if (((this.dmaRegs[i, 3] >> 12) & 0x3) == 1) { this.DmaTransfer(i); } } } public void FifoDma(int channel) { if (((this.dmaRegs[channel, 3] >> 12) & 0x3) == 0x3) { this.DmaTransfer(channel); } } public void DmaTransfer(int channel) { // Check if DMA is enabled if ((this.dmaRegs[channel, 3] & (1 << 15)) != 0) { bool wideTransfer = (this.dmaRegs[channel, 3] & (1 << 10)) != 0; uint srcDirection = 0, destDirection = 0; bool reload = false; switch ((this.dmaRegs[channel, 3] >> 5) & 0x3) { case 0: destDirection = 1; break; case 1: destDirection = 0xFFFFFFFF; break; case 2: destDirection = 0; break; case 3: destDirection = 1; reload = true; break; } switch ((this.dmaRegs[channel, 3] >> 7) & 0x3) { case 0: srcDirection = 1; break; case 1: srcDirection = 0xFFFFFFFF; break; case 2: srcDirection = 0; break; case 3: if (channel == 3) { // TODO return; } throw new Exception("Unhandled DMA mode."); } int numElements = (int)this.dmaRegs[channel, 2]; if (numElements == 0) numElements = 0x4000; if (((this.dmaRegs[channel, 3] >> 12) & 0x3) == 0x3) { // Sound FIFO mode wideTransfer = true; destDirection = 0; numElements = 4; reload = false; } if (wideTransfer) { srcDirection *= 4; destDirection *= 4; while (numElements-- > 0) { this.WriteU32(this.dmaRegs[channel, 1], this.ReadU32(this.dmaRegs[channel, 0])); this.dmaRegs[channel, 1] += destDirection; this.dmaRegs[channel, 0] += srcDirection; } } else { srcDirection *= 2; destDirection *= 2; while (numElements-- > 0) { this.WriteU16(this.dmaRegs[channel, 1], this.ReadU16(this.dmaRegs[channel, 0])); this.dmaRegs[channel, 1] += destDirection; this.dmaRegs[channel, 0] += srcDirection; } } // If not a repeating DMA, then disable the DMA if ((this.dmaRegs[channel, 3] & (1 << 9)) == 0) { this.dmaRegs[channel, 3] &= 0x7FFF; } else { // Reload dest and count switch (channel) { case 0: if (reload) this.dmaRegs[0, 1] = Memory.ReadU32(this.ioReg, Memory.DMA0DAD) & 0x07FFFFFF; this.dmaRegs[0, 2] = Memory.ReadU16(this.ioReg, Memory.DMA0CNT_L); break; case 1: if (reload) this.dmaRegs[1, 1] = Memory.ReadU32(this.ioReg, Memory.DMA1DAD) & 0x07FFFFFF; this.dmaRegs[1, 2] = Memory.ReadU16(this.ioReg, Memory.DMA1CNT_L); break; case 2: if (reload) this.dmaRegs[2, 1] = Memory.ReadU32(this.ioReg, Memory.DMA2DAD) & 0x07FFFFFF; this.dmaRegs[2, 2] = Memory.ReadU16(this.ioReg, Memory.DMA2CNT_L); break; case 3: if (reload) this.dmaRegs[3, 1] = Memory.ReadU32(this.ioReg, Memory.DMA3DAD) & 0x0FFFFFFF; this.dmaRegs[3, 2] = Memory.ReadU16(this.ioReg, Memory.DMA3CNT_L); break; } } if ((this.dmaRegs[channel, 3] & (1 << 14)) != 0) { this.processor.RequestIrq(8 + channel); } } } public void WriteDmaControl(int channel) { switch (channel) { case 0: if (((this.dmaRegs[0, 3] ^ Memory.ReadU16(this.ioReg, Memory.DMA0CNT_H)) & (1 << 15)) == 0) return; this.dmaRegs[0, 0] = Memory.ReadU32(this.ioReg, Memory.DMA0SAD) & 0x07FFFFFF; this.dmaRegs[0, 1] = Memory.ReadU32(this.ioReg, Memory.DMA0DAD) & 0x07FFFFFF; this.dmaRegs[0, 2] = Memory.ReadU16(this.ioReg, Memory.DMA0CNT_L); this.dmaRegs[0, 3] = Memory.ReadU16(this.ioReg, Memory.DMA0CNT_H); break; case 1: if (((this.dmaRegs[1, 3] ^ Memory.ReadU16(this.ioReg, Memory.DMA1CNT_H)) & (1 << 15)) == 0) return; this.dmaRegs[1, 0] = Memory.ReadU32(this.ioReg, Memory.DMA1SAD) & 0x0FFFFFFF; this.dmaRegs[1, 1] = Memory.ReadU32(this.ioReg, Memory.DMA1DAD) & 0x07FFFFFF; this.dmaRegs[1, 2] = Memory.ReadU16(this.ioReg, Memory.DMA1CNT_L); this.dmaRegs[1, 3] = Memory.ReadU16(this.ioReg, Memory.DMA1CNT_H); break; case 2: if (((this.dmaRegs[2, 3] ^ Memory.ReadU16(this.ioReg, Memory.DMA2CNT_H)) & (1 << 15)) == 0) return; this.dmaRegs[2, 0] = Memory.ReadU32(this.ioReg, Memory.DMA2SAD) & 0x0FFFFFFF; this.dmaRegs[2, 1] = Memory.ReadU32(this.ioReg, Memory.DMA2DAD) & 0x07FFFFFF; this.dmaRegs[2, 2] = Memory.ReadU16(this.ioReg, Memory.DMA2CNT_L); this.dmaRegs[2, 3] = Memory.ReadU16(this.ioReg, Memory.DMA2CNT_H); break; case 3: if (((this.dmaRegs[3, 3] ^ Memory.ReadU16(this.ioReg, Memory.DMA3CNT_H)) & (1 << 15)) == 0) return; this.dmaRegs[3, 0] = Memory.ReadU32(this.ioReg, Memory.DMA3SAD) & 0x0FFFFFFF; this.dmaRegs[3, 1] = Memory.ReadU32(this.ioReg, Memory.DMA3DAD) & 0x0FFFFFFF; this.dmaRegs[3, 2] = Memory.ReadU16(this.ioReg, Memory.DMA3CNT_L); this.dmaRegs[3, 3] = Memory.ReadU16(this.ioReg, Memory.DMA3CNT_H); break; } // Channel start timing switch ((this.dmaRegs[channel, 3] >> 12) & 0x3) { case 0: // Start immediately this.DmaTransfer(channel); break; case 1: case 2: // Hblank and Vblank DMA's break; case 3: // TODO (DMA sound) return; } } private void WriteTimerControl(int timer, ushort newCnt) { ushort control = Memory.ReadU16(this.ioReg, Memory.TM0CNT + (uint)(timer * 4)); uint count = Memory.ReadU16(this.ioReg, Memory.TM0D + (uint)(timer * 4)); if ((newCnt & (1 << 7)) != 0 && (control & (1 << 7)) == 0) { this.timerCnt[timer] = count << 10; } } #region Read/Write Helpers public static ushort ReadU16(byte[] array, uint position) { return (ushort)(array[position] | (array[position + 1] << 8)); } public static uint ReadU32(byte[] array, uint position) { return (uint)(array[position] | (array[position + 1] << 8) | (array[position + 2] << 16) | (array[position + 3] << 24)); } public static void WriteU16(byte[] array, uint position, ushort value) { array[position] = (byte)(value & 0xff); array[position + 1] = (byte)(value >> 8); } public static void WriteU32(byte[] array, uint position, uint value) { array[position] = (byte)(value & 0xff); array[position + 1] = (byte)((value >> 8) & 0xff); array[position + 2] = (byte)((value >> 16) & 0xff); array[position + 3] = (byte)(value >> 24); } #endregion #region Memory Reads private uint ReadUnreadable() { if (this.inUnreadable) { return 0; } this.inUnreadable = true; uint res; if (this.processor.ArmState) { res = this.ReadU32(this.processor.Registers[15]); } else { ushort val = this.ReadU16(this.processor.Registers[15]); res = (uint)(val | (val << 16)); } this.inUnreadable = false; return res; } private byte ReadNop8(uint address) { return (byte)(this.ReadUnreadable() & 0xFF); } private ushort ReadNop16(uint address) { return (ushort)(this.ReadUnreadable() & 0xFFFF); } private uint ReadNop32(uint address) { return this.ReadUnreadable(); } private byte ReadBiosRam8(uint address) { this.waitCycles++; if (this.processor.Registers[15] < 0x01000000) { return this.biosRam[address & Memory.biosRamMask]; } return (byte)(this.ReadUnreadable() & 0xFF); } private ushort ReadBiosRam16(uint address) { this.waitCycles++; if (this.processor.Registers[15] < 0x01000000) { return Memory.ReadU16(this.biosRam, address & Memory.biosRamMask); } return (ushort)(this.ReadUnreadable() & 0xFFFF); } private uint ReadBiosRam32(uint address) { this.waitCycles++; if (this.processor.Registers[15] < 0x01000000) { return Memory.ReadU32(this.biosRam, address & Memory.biosRamMask); } return this.ReadUnreadable(); } private byte ReadEwRam8(uint address) { this.waitCycles += 3; return this.ewRam[address & Memory.ewRamMask]; } private ushort ReadEwRam16(uint address) { this.waitCycles += 3; return Memory.ReadU16(this.ewRam, address & Memory.ewRamMask); } private uint ReadEwRam32(uint address) { this.waitCycles += 6; return Memory.ReadU32(this.ewRam, address & Memory.ewRamMask); } private byte ReadIwRam8(uint address) { this.waitCycles++; return this.iwRam[address & Memory.iwRamMask]; } private ushort ReadIwRam16(uint address) { this.waitCycles++; return Memory.ReadU16(this.iwRam, address & Memory.iwRamMask); } private uint ReadIwRam32(uint address) { this.waitCycles++; return Memory.ReadU32(this.iwRam, address & Memory.iwRamMask); } private byte ReadIO8(uint address) { this.waitCycles++; address &= 0xFFFFFF; if (address >= Memory.ioRegMask) return 0; switch (address) { case KEYINPUT: return (byte)(this.keyState & 0xFF); case KEYINPUT + 1: return (byte)(this.keyState >> 8); case DMA0CNT_H: return (byte)(this.dmaRegs[0, 3] & 0xFF); case DMA0CNT_H + 1: return (byte)(this.dmaRegs[0, 3] >> 8); case DMA1CNT_H: return (byte)(this.dmaRegs[1, 3] & 0xFF); case DMA1CNT_H + 1: return (byte)(this.dmaRegs[1, 3] >> 8); case DMA2CNT_H: return (byte)(this.dmaRegs[2, 3] & 0xFF); case DMA2CNT_H + 1: return (byte)(this.dmaRegs[2, 3] >> 8); case DMA3CNT_H: return (byte)(this.dmaRegs[3, 3] & 0xFF); case DMA3CNT_H + 1: return (byte)(this.dmaRegs[3, 3] >> 8); case TM0D: this.processor.UpdateTimers(); return (byte)((this.timerCnt[0] >> 10) & 0xFF); case TM0D + 1: this.processor.UpdateTimers(); return (byte)((this.timerCnt[0] >> 10) >> 8); case TM1D: this.processor.UpdateTimers(); return (byte)((this.timerCnt[1] >> 10) & 0xFF); case TM1D + 1: this.processor.UpdateTimers(); return (byte)((this.timerCnt[1] >> 10) >> 8); case TM2D: this.processor.UpdateTimers(); return (byte)((this.timerCnt[2] >> 10) & 0xFF); case TM2D + 1: this.processor.UpdateTimers(); return (byte)((this.timerCnt[2] >> 10) >> 8); case TM3D: this.processor.UpdateTimers(); return (byte)((this.timerCnt[3] >> 10) & 0xFF); case TM3D + 1: this.processor.UpdateTimers(); return (byte)((this.timerCnt[3] >> 10) >> 8); default: return this.ioReg[address]; } } private ushort ReadIO16(uint address) { this.waitCycles++; address &= 0xFFFFFF; if (address >= Memory.ioRegMask) return 0; switch (address) { case KEYINPUT: return this.keyState; case DMA0CNT_H: return (ushort)this.dmaRegs[0, 3]; case DMA1CNT_H: return (ushort)this.dmaRegs[1, 3]; case DMA2CNT_H: return (ushort)this.dmaRegs[2, 3]; case DMA3CNT_H: return (ushort)this.dmaRegs[3, 3]; case TM0D: this.processor.UpdateTimers(); return (ushort)((this.timerCnt[0] >> 10) & 0xFFFF); case TM1D: this.processor.UpdateTimers(); return (ushort)((this.timerCnt[1] >> 10) & 0xFFFF); case TM2D: this.processor.UpdateTimers(); return (ushort)((this.timerCnt[2] >> 10) & 0xFFFF); case TM3D: this.processor.UpdateTimers(); return (ushort)((this.timerCnt[3] >> 10) & 0xFFFF); default: return Memory.ReadU16(this.ioReg, address); } } private uint ReadIO32(uint address) { this.waitCycles++; address &= 0xFFFFFF; if (address >= Memory.ioRegMask) return 0; switch (address) { case KEYINPUT: return this.keyState | ((uint)Memory.ReadU16(this.ioReg, address + 0x2) << 16); case DMA0CNT_L: return (uint)Memory.ReadU16(this.ioReg, address) | (this.dmaRegs[0, 3] << 16); case DMA1CNT_L: return (uint)Memory.ReadU16(this.ioReg, address) | (this.dmaRegs[1, 3] << 16); case DMA2CNT_L: return (uint)Memory.ReadU16(this.ioReg, address) | (this.dmaRegs[2, 3] << 16); case DMA3CNT_L: return (uint)Memory.ReadU16(this.ioReg, address) | (this.dmaRegs[3, 3] << 16); case TM0D: this.processor.UpdateTimers(); return (uint)(((this.timerCnt[0] >> 10) & 0xFFFF) | (uint)(Memory.ReadU16(this.ioReg, address + 2) << 16)); case TM1D: this.processor.UpdateTimers(); return (uint)(((this.timerCnt[1] >> 10) & 0xFFFF) | (uint)(Memory.ReadU16(this.ioReg, address + 2) << 16)); case TM2D: this.processor.UpdateTimers(); return (uint)(((this.timerCnt[2] >> 10) & 0xFFFF) | (uint)(Memory.ReadU16(this.ioReg, address + 2) << 16)); case TM3D: this.processor.UpdateTimers(); return (uint)(((this.timerCnt[3] >> 10) & 0xFFFF) | (uint)(Memory.ReadU16(this.ioReg, address + 2) << 16)); default: return Memory.ReadU32(this.ioReg, address); } } private byte ReadPalRam8(uint address) { this.waitCycles++; return this.palRam[address & Memory.palRamMask]; } private ushort ReadPalRam16(uint address) { this.waitCycles++; return Memory.ReadU16(this.palRam, address & Memory.palRamMask); } private uint ReadPalRam32(uint address) { this.waitCycles += 2; return Memory.ReadU32(this.palRam, address & Memory.palRamMask); } private byte ReadVRam8(uint address) { this.waitCycles++; address &= Memory.vRamMask; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); return this.vRam[address]; } private ushort ReadVRam16(uint address) { this.waitCycles++; address &= Memory.vRamMask; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); return Memory.ReadU16(this.vRam, address); } private uint ReadVRam32(uint address) { this.waitCycles += 2; address &= Memory.vRamMask; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); return Memory.ReadU32(this.vRam, address & Memory.vRamMask); } private byte ReadOamRam8(uint address) { this.waitCycles++; return this.oamRam[address & Memory.oamRamMask]; } private ushort ReadOamRam16(uint address) { this.waitCycles++; return Memory.ReadU16(this.oamRam, address & Memory.oamRamMask); } private uint ReadOamRam32(uint address) { this.waitCycles++; return Memory.ReadU32(this.oamRam, address & Memory.oamRamMask); } private byte ReadRom1_8(uint address) { this.waitCycles += this.bankSTimes[(address >> 24) & 0xf]; return this.romBank1[address & this.romBank1Mask]; } private ushort ReadRom1_16(uint address) { this.waitCycles += this.bankSTimes[(address >> 24) & 0xf]; return Memory.ReadU16(this.romBank1, address & this.romBank1Mask); } private uint ReadRom1_32(uint address) { this.waitCycles += this.bankSTimes[(address >> 24) & 0xf] * 2 + 1; return Memory.ReadU32(this.romBank1, address & this.romBank1Mask); } private byte ReadRom2_8(uint address) { this.waitCycles += this.bankSTimes[(address >> 24) & 0xf]; return this.romBank2[address & this.romBank2Mask]; } private ushort ReadRom2_16(uint address) { this.waitCycles += this.bankSTimes[(address >> 24) & 0xf]; return Memory.ReadU16(this.romBank2, address & this.romBank2Mask); } private uint ReadRom2_32(uint address) { this.waitCycles += this.bankSTimes[(address >> 24) & 0xf] * 2 + 1; return Memory.ReadU32(this.romBank2, address & this.romBank2Mask); } private byte ReadSRam8(uint address) { return this.sRam[address & Memory.sRamMask]; } private ushort ReadSRam16(uint address) { // TODO return 0; } private uint ReadSRam32(uint address) { // TODO return 0; } #endregion #region Memory Writes private void WriteNop8(uint address, byte value) { } private void WriteNop16(uint address, ushort value) { } private void WriteNop32(uint address, uint value) { } private void WriteEwRam8(uint address, byte value) { this.waitCycles += 3; this.ewRam[address & Memory.ewRamMask] = value; } private void WriteEwRam16(uint address, ushort value) { this.waitCycles += 3; Memory.WriteU16(this.ewRam, address & Memory.ewRamMask, value); } private void WriteEwRam32(uint address, uint value) { this.waitCycles += 6; Memory.WriteU32(this.ewRam, address & Memory.ewRamMask, value); } private void WriteIwRam8(uint address, byte value) { this.waitCycles++; this.iwRam[address & Memory.iwRamMask] = value; } private void WriteIwRam16(uint address, ushort value) { this.waitCycles++; Memory.WriteU16(this.iwRam, address & Memory.iwRamMask, value); } private void WriteIwRam32(uint address, uint value) { this.waitCycles++; Memory.WriteU32(this.iwRam, address & Memory.iwRamMask, value); } private void WriteIO8(uint address, byte value) { this.waitCycles++; address &= 0xFFFFFF; if (address >= Memory.ioRegMask) return; switch (address) { case BG2X_L: case BG2X_L + 1: case BG2X_L + 2: case BG2X_L + 3: { this.ioReg[address] = value; uint tmp = Memory.ReadU32(this.ioReg, BG2X_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG2X_L, tmp); this.bgx[0] = (int)tmp; } break; case BG3X_L: case BG3X_L + 1: case BG3X_L + 2: case BG3X_L + 3: { this.ioReg[address] = value; uint tmp = Memory.ReadU32(this.ioReg, BG3X_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG3X_L, tmp); this.bgx[1] = (int)tmp; } break; case BG2Y_L: case BG2Y_L + 1: case BG2Y_L + 2: case BG2Y_L + 3: { this.ioReg[address] = value; uint tmp = Memory.ReadU32(this.ioReg, BG2Y_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG2Y_L, tmp); this.bgy[0] = (int)tmp; } break; case BG3Y_L: case BG3Y_L + 1: case BG3Y_L + 2: case BG3Y_L + 3: { this.ioReg[address] = value; uint tmp = Memory.ReadU32(this.ioReg, BG3Y_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG3Y_L, tmp); this.bgy[1] = (int)tmp; } break; case DMA0CNT_H: case DMA0CNT_H + 1: this.ioReg[address] = value; this.WriteDmaControl(0); break; case DMA1CNT_H: case DMA1CNT_H + 1: this.ioReg[address] = value; this.WriteDmaControl(1); break; case DMA2CNT_H: case DMA2CNT_H + 1: this.ioReg[address] = value; this.WriteDmaControl(2); break; case DMA3CNT_H: case DMA3CNT_H + 1: this.ioReg[address] = value; this.WriteDmaControl(3); break; case TM0CNT: case TM0CNT + 1: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM0CNT); this.ioReg[address] = value; this.WriteTimerControl(0, oldCnt); } break; case TM1CNT: case TM1CNT + 1: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM1CNT); this.ioReg[address] = value; this.WriteTimerControl(1, oldCnt); } break; case TM2CNT: case TM2CNT + 1: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM2CNT); this.ioReg[address] = value; this.WriteTimerControl(2, oldCnt); } break; case TM3CNT: case TM3CNT + 1: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM3CNT); this.ioReg[address] = value; this.WriteTimerControl(3, oldCnt); } break; case FIFO_A_L: case FIFO_A_L+1: case FIFO_A_H: case FIFO_A_H+1: this.ioReg[address] = value; this.soundManager.IncrementFifoA(); break; case FIFO_B_L: case FIFO_B_L + 1: case FIFO_B_H: case FIFO_B_H + 1: this.ioReg[address] = value; this.soundManager.IncrementFifoB(); break; case IF: case IF + 1: this.ioReg[address] &= (byte)~value; break; case HALTCNT + 1: this.ioReg[address] = value; this.processor.Halt(); break; default: this.ioReg[address] = value; break; } } private void WriteIO16(uint address, ushort value) { this.waitCycles++; address &= 0xFFFFFF; if (address >= Memory.ioRegMask) return; switch (address) { case BG2X_L: case BG2X_L + 2: { Memory.WriteU16(this.ioReg, address, value); uint tmp = Memory.ReadU32(this.ioReg, BG2X_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG2X_L, tmp); this.bgx[0] = (int)tmp; } break; case BG3X_L: case BG3X_L + 2: { Memory.WriteU16(this.ioReg, address, value); uint tmp = Memory.ReadU32(this.ioReg, BG3X_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG3X_L, tmp); this.bgx[1] = (int)tmp; } break; case BG2Y_L: case BG2Y_L + 2: { Memory.WriteU16(this.ioReg, address, value); uint tmp = Memory.ReadU32(this.ioReg, BG2Y_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG2Y_L, tmp); this.bgy[0] = (int)tmp; } break; case BG3Y_L: case BG3Y_L + 2: { Memory.WriteU16(this.ioReg, address, value); uint tmp = Memory.ReadU32(this.ioReg, BG3Y_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG3Y_L, tmp); this.bgy[1] = (int)tmp; } break; case DMA0CNT_H: Memory.WriteU16(this.ioReg, address, value); this.WriteDmaControl(0); break; case DMA1CNT_H: Memory.WriteU16(this.ioReg, address, value); this.WriteDmaControl(1); break; case DMA2CNT_H: Memory.WriteU16(this.ioReg, address, value); this.WriteDmaControl(2); break; case DMA3CNT_H: Memory.WriteU16(this.ioReg, address, value); this.WriteDmaControl(3); break; case TM0CNT: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM0CNT); Memory.WriteU16(this.ioReg, address, value); this.WriteTimerControl(0, oldCnt); } break; case TM1CNT: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM1CNT); Memory.WriteU16(this.ioReg, address, value); this.WriteTimerControl(1, oldCnt); } break; case TM2CNT: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM2CNT); Memory.WriteU16(this.ioReg, address, value); this.WriteTimerControl(2, oldCnt); } break; case TM3CNT: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM3CNT); Memory.WriteU16(this.ioReg, address, value); this.WriteTimerControl(3, oldCnt); } break; case FIFO_A_L: case FIFO_A_H: Memory.WriteU16(this.ioReg, address, value); this.soundManager.IncrementFifoA(); break; case FIFO_B_L: case FIFO_B_H: Memory.WriteU16(this.ioReg, address, value); this.soundManager.IncrementFifoB(); break; case SOUNDCNT_H: Memory.WriteU16(this.ioReg, address, value); if ((value & (1 << 11)) != 0) { this.soundManager.ResetFifoA(); } if ((value & (1 << 15)) != 0) { this.soundManager.ResetFifoB(); } break; case IF: { ushort tmp = Memory.ReadU16(this.ioReg, address); Memory.WriteU16(this.ioReg, address, (ushort)(tmp & (~value))); } break; case HALTCNT: Memory.WriteU16(this.ioReg, address, value); this.processor.Halt(); break; default: Memory.WriteU16(this.ioReg, address, value); break; } } private void WriteIO32(uint address, uint value) { this.waitCycles++; address &= 0xFFFFFF; if (address >= Memory.ioRegMask) return; switch (address) { case BG2X_L: { Memory.WriteU32(this.ioReg, address, value); uint tmp = Memory.ReadU32(this.ioReg, BG2X_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG2X_L, tmp); this.bgx[0] = (int)tmp; } break; case BG3X_L: { Memory.WriteU32(this.ioReg, address, value); uint tmp = Memory.ReadU32(this.ioReg, BG3X_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG3X_L, tmp); this.bgx[1] = (int)tmp; } break; case BG2Y_L: { Memory.WriteU32(this.ioReg, address, value); uint tmp = Memory.ReadU32(this.ioReg, BG2Y_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG2Y_L, tmp); this.bgy[0] = (int)tmp; } break; case BG3Y_L: { Memory.WriteU32(this.ioReg, address, value); uint tmp = Memory.ReadU32(this.ioReg, BG3Y_L); if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; Memory.WriteU32(this.ioReg, BG3Y_L, tmp); this.bgy[1] = (int)tmp; } break; case DMA0CNT_L: Memory.WriteU32(this.ioReg, address, value); this.WriteDmaControl(0); break; case DMA1CNT_L: Memory.WriteU32(this.ioReg, address, value); this.WriteDmaControl(1); break; case DMA2CNT_L: Memory.WriteU32(this.ioReg, address, value); this.WriteDmaControl(2); break; case DMA3CNT_L: Memory.WriteU32(this.ioReg, address, value); this.WriteDmaControl(3); break; case TM0D: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM0CNT); Memory.WriteU32(this.ioReg, address, value); this.WriteTimerControl(0, oldCnt); } break; case TM1D: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM1CNT); Memory.WriteU32(this.ioReg, address, value); this.WriteTimerControl(1, oldCnt); } break; case TM2D: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM2CNT); Memory.WriteU32(this.ioReg, address, value); this.WriteTimerControl(2, oldCnt); } break; case TM3D: { ushort oldCnt = Memory.ReadU16(this.ioReg, TM3CNT); Memory.WriteU32(this.ioReg, address, value); this.WriteTimerControl(3, oldCnt); } break; case FIFO_A_L: Memory.WriteU32(this.ioReg, address, value); this.soundManager.IncrementFifoA(); break; case FIFO_B_L: Memory.WriteU32(this.ioReg, address, value); this.soundManager.IncrementFifoB(); break; case SOUNDCNT_L: Memory.WriteU32(this.ioReg, address, value); if (((value >> 16) & (1 << 11)) != 0) { this.soundManager.ResetFifoA(); } if (((value >> 16) & (1 << 15)) != 0) { this.soundManager.ResetFifoB(); } break; case IE: { uint tmp = Memory.ReadU32(this.ioReg, address); Memory.WriteU32(this.ioReg, address, (uint)((value & 0xFFFF) | (tmp & (~(value & 0xFFFF0000))))); } break; case HALTCNT: Memory.WriteU32(this.ioReg, address, value); this.processor.Halt(); break; default: Memory.WriteU32(this.ioReg, address, value); break; } } private void WritePalRam8(uint address, byte value) { this.waitCycles++; address &= Memory.palRamMask & ~1U; this.palRam[address] = value; this.palRam[address + 1] = value; } private void WritePalRam16(uint address, ushort value) { this.waitCycles++; Memory.WriteU16(this.palRam, address & Memory.palRamMask, value); } private void WritePalRam32(uint address, uint value) { this.waitCycles += 2; Memory.WriteU32(this.palRam, address & Memory.palRamMask, value); } private void WriteVRam8(uint address, byte value) { this.waitCycles++; address &= Memory.vRamMask & ~1U; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); this.vRam[address] = value; this.vRam[address + 1] = value; } private void WriteVRam16(uint address, ushort value) { this.waitCycles++; address &= Memory.vRamMask; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); Memory.WriteU16(this.vRam, address, value); } private void WriteVRam32(uint address, uint value) { this.waitCycles += 2; address &= Memory.vRamMask; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); Memory.WriteU32(this.vRam, address, value); } private void WriteOamRam8(uint address, byte value) { this.waitCycles++; address &= Memory.oamRamMask & ~1U; this.oamRam[address] = value; this.oamRam[address + 1] = value; } private void WriteOamRam16(uint address, ushort value) { this.waitCycles++; Memory.WriteU16(this.oamRam, address & Memory.oamRamMask, value); } private void WriteOamRam32(uint address, uint value) { this.waitCycles++; Memory.WriteU32(this.oamRam, address & Memory.oamRamMask, value); } private void WriteSRam8(uint address, byte value) { this.sRam[address & Memory.sRamMask] = value; } private void WriteSRam16(uint address, ushort value) { // TODO } private void WriteSRam32(uint address, uint value) { // TODO } private enum EepromModes { Idle, ReadData } private EepromModes eepromMode = EepromModes.Idle; private byte[] eeprom = new byte[0xffff]; private byte[] eepromStore = new byte[0xff]; private int curEepromByte; private int eepromReadAddress = -1; private void WriteEeprom8(uint address, byte value) { // EEPROM writes must be done by DMA 3 if ((this.dmaRegs[3, 3] & (1 << 15)) == 0) return; // 0 length eeprom writes are bad if (this.dmaRegs[3, 2] == 0) return; if (this.eepromMode != EepromModes.ReadData) { this.curEepromByte = 0; this.eepromMode = EepromModes.ReadData; this.eepromReadAddress = -1; for (int i = 0; i < this.eepromStore.Length; i++) this.eepromStore[i] = 0; } this.eepromStore[this.curEepromByte >> 3] |= (byte)(value << (7 - (this.curEepromByte & 0x7))); this.curEepromByte++; if (this.curEepromByte == this.dmaRegs[3, 2]) { if ((this.eepromStore[0] & 0x80) == 0) return; if ((this.eepromStore[0] & 0x40) != 0) { // Read request if (this.curEepromByte == 9) { this.eepromReadAddress = this.eepromStore[0] & 0x3F; } else { this.eepromReadAddress = ((this.eepromStore[0] & 0x3F) << 8) | this.eepromStore[1]; } this.curEepromByte = 0; } else { // Write request int eepromAddress, offset; if (this.curEepromByte == 64 + 9) { eepromAddress = (int)(this.eepromStore[0] & 0x3F); offset = 1; } else { eepromAddress = ((this.eepromStore[0] & 0x3F) << 8) | this.eepromStore[1]; offset = 2; } for (int i = 0; i < 8; i++) { this.eeprom[eepromAddress * 8 + i] = this.eepromStore[i + offset]; } this.eepromMode = EepromModes.Idle; } } } private void WriteEeprom16(uint address, ushort value) { this.WriteEeprom8(address, (byte)(value & 0xff)); } private void WriteEeprom32(uint address, uint value) { this.WriteEeprom8(address, (byte)(value & 0xff)); } private byte ReadEeprom8(uint address) { if (this.eepromReadAddress == -1) return 1; byte retval = 0; if (this.curEepromByte >= 4) { retval = (byte)((this.eeprom[this.eepromReadAddress * 8 + ((this.curEepromByte - 4) / 8)] >> (7 - ((this.curEepromByte - 4) & 7))) & 1); } this.curEepromByte++; if (this.curEepromByte == this.dmaRegs[3, 2]) { this.eepromReadAddress = -1; this.eepromMode = EepromModes.Idle; } return retval; } private ushort ReadEeprom16(uint address) { return (ushort)this.ReadEeprom8(address); } private uint ReadEeprom32(uint address) { return (uint)this.ReadEeprom8(address); } #endregion #region Shader Renderer Vram Writes private List vramUpdated = new List(); private List palUpdated = new List(); public const int VramBlockSize = 64; public const int PalBlockSize = 32; private bool[] vramHit = new bool[(Memory.vRamMask + 1) / VramBlockSize]; private bool[] palHit = new bool[(Memory.palRamMask + 1) / PalBlockSize]; public List VramUpdated { get { List old = this.vramUpdated; for (int i = 0; i < old.Count; i++) { vramHit[old[i]] = false; } this.vramUpdated = new List(); return old; } } public List PalUpdated { get { List old = this.palUpdated; for (int i = 0; i < old.Count; i++) { palHit[old[i]] = false; } this.palUpdated = new List(); return old; } } private void UpdatePal(uint address) { uint index = address / PalBlockSize; if (!palHit[index]) { palHit[index] = true; this.palUpdated.Add(index); } } private void UpdateVram(uint address) { uint index = address / VramBlockSize; if (!vramHit[index]) { vramHit[index] = true; this.vramUpdated.Add(index); } } private void ShaderWritePalRam8(uint address, byte value) { this.waitCycles++; address &= Memory.palRamMask & ~1U; this.palRam[address] = value; this.palRam[address + 1] = value; this.UpdatePal(address); } private void ShaderWritePalRam16(uint address, ushort value) { this.waitCycles++; Memory.WriteU16(this.palRam, address & Memory.palRamMask, value); this.UpdatePal(address & Memory.palRamMask); } private void ShaderWritePalRam32(uint address, uint value) { this.waitCycles += 2; Memory.WriteU32(this.palRam, address & Memory.palRamMask, value); this.UpdatePal(address & Memory.palRamMask); } private void ShaderWriteVRam8(uint address, byte value) { this.waitCycles++; address &= Memory.vRamMask & ~1U; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); this.vRam[address] = value; this.vRam[address + 1] = value; this.UpdateVram(address); } private void ShaderWriteVRam16(uint address, ushort value) { this.waitCycles++; address &= Memory.vRamMask; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); Memory.WriteU16(this.vRam, address, value); this.UpdateVram(address); } private void ShaderWriteVRam32(uint address, uint value) { this.waitCycles += 2; address &= Memory.vRamMask; if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); Memory.WriteU32(this.vRam, address, value); this.UpdateVram(address); } public void EnableVramUpdating() { this.WriteU8Funcs[0x5] = this.ShaderWritePalRam8; this.WriteU16Funcs[0x5] = this.ShaderWritePalRam16; this.WriteU32Funcs[0x5] = this.ShaderWritePalRam32; this.WriteU8Funcs[0x6] = this.ShaderWriteVRam8; this.WriteU16Funcs[0x6] = this.ShaderWriteVRam16; this.WriteU32Funcs[0x6] = this.ShaderWriteVRam32; for (uint i = 0; i < (Memory.vRamMask + 1) / Memory.VramBlockSize; i++) { this.vramUpdated.Add(i); } for (uint i = 0; i < (Memory.palRamMask + 1) / Memory.PalBlockSize; i++) { this.palUpdated.Add(i); } } #endregion public byte ReadU8(uint address) { uint bank = (address >> 24) & 0xf; return this.ReadU8Funcs[bank](address); } public ushort ReadU16(uint address) { address &= ~1U; uint bank = (address >> 24) & 0xf; return this.ReadU16Funcs[bank](address); } public uint ReadU32(uint address) { int shiftAmt = (int)((address & 3U) << 3); address &= ~3U; uint bank = (address >> 24) & 0xf; uint res = this.ReadU32Funcs[bank](address); return (res >> shiftAmt) | (res << (32 - shiftAmt)); } public uint ReadU32Aligned(uint address) { uint bank = (address >> 24) & 0xf; return this.ReadU32Funcs[bank](address); } public ushort ReadU16Debug(uint address) { address &= ~1U; uint bank = (address >> 24) & 0xf; int oldWaitCycles = this.waitCycles; ushort res = this.ReadU16Funcs[bank](address); this.waitCycles = oldWaitCycles; return res; } public uint ReadU32Debug(uint address) { int shiftAmt = (int)((address & 3U) << 3); address &= ~3U; uint bank = (address >> 24) & 0xf; int oldWaitCycles = this.waitCycles; uint res = this.ReadU32Funcs[bank](address); this.waitCycles = oldWaitCycles; return (res >> shiftAmt) | (res << (32 - shiftAmt)); } public void WriteU8(uint address, byte value) { uint bank = (address >> 24) & 0xf; this.WriteU8Funcs[bank](address, value); } public void WriteU16(uint address, ushort value) { address &= ~1U; uint bank = (address >> 24) & 0xf; this.WriteU16Funcs[bank](address, value); } public void WriteU32(uint address, uint value) { address &= ~3U; uint bank = (address >> 24) & 0xf; this.WriteU32Funcs[bank](address, value); } public void WriteU8Debug(uint address, byte value) { uint bank = (address >> 24) & 0xf; int oldWaitCycles = this.waitCycles; this.WriteU8Funcs[bank](address, value); this.waitCycles = oldWaitCycles; } public void WriteU16Debug(uint address, ushort value) { address &= ~1U; uint bank = (address >> 24) & 0xf; int oldWaitCycles = this.waitCycles; this.WriteU16Funcs[bank](address, value); this.waitCycles = oldWaitCycles; } public void WriteU32Debug(uint address, uint value) { address &= ~3U; uint bank = (address >> 24) & 0xf; int oldWaitCycles = this.waitCycles; this.WriteU32Funcs[bank](address, value); this.waitCycles = oldWaitCycles; } public void LoadBios(byte[] biosRom) { Array.Copy(biosRom, this.biosRam, this.biosRam.Length); } public void LoadCartridge(byte[] cartRom) { this.ResetRomBank1(); this.ResetRomBank2(); // Set up the appropriate cart size int cartSize = 1; while (cartSize < cartRom.Length) { cartSize <<= 1; } if (cartSize != cartRom.Length) { throw new Exception("Unable to load non power of two carts"); } // Split across bank 1 and 2 if cart is too big if (cartSize > 1 << 24) { this.romBank1 = cartRom; this.romBank1Mask = (1 << 24) - 1; cartRom.CopyTo(this.romBank2, 1 << 24); this.romBank2Mask = (1 << 24) - 1; } else { this.romBank1 = cartRom; this.romBank1Mask = (uint)(cartSize - 1); } if (this.romBank1Mask != 0) { // TODO: Writes (i.e. eeprom, and other stuff) this.ReadU8Funcs[0x8] = this.ReadRom1_8; this.ReadU8Funcs[0xA] = this.ReadRom1_8; this.ReadU8Funcs[0xC] = this.ReadRom1_8; this.ReadU16Funcs[0x8] = this.ReadRom1_16; this.ReadU16Funcs[0xA] = this.ReadRom1_16; this.ReadU16Funcs[0xC] = this.ReadRom1_16; this.ReadU32Funcs[0x8] = this.ReadRom1_32; this.ReadU32Funcs[0xA] = this.ReadRom1_32; this.ReadU32Funcs[0xC] = this.ReadRom1_32; } if (this.romBank2Mask != 0) { this.ReadU8Funcs[0x9] = this.ReadRom2_8; this.ReadU8Funcs[0xB] = this.ReadRom2_8; this.ReadU8Funcs[0xD] = this.ReadRom2_8; this.ReadU16Funcs[0x9] = this.ReadRom2_16; this.ReadU16Funcs[0xB] = this.ReadRom2_16; this.ReadU16Funcs[0xD] = this.ReadRom2_16; this.ReadU32Funcs[0x9] = this.ReadRom2_32; this.ReadU32Funcs[0xB] = this.ReadRom2_32; this.ReadU32Funcs[0xD] = this.ReadRom2_32; } } private void ResetRomBank1() { this.romBank1 = null; this.romBank1Mask = 0; for (int i = 0; i < this.bankSTimes.Length; i++) { this.bankSTimes[i] = 2; } this.ReadU8Funcs[0x8] = this.ReadNop8; this.ReadU8Funcs[0xA] = this.ReadNop8; this.ReadU8Funcs[0xC] = this.ReadNop8; this.ReadU16Funcs[0x8] = this.ReadNop16; this.ReadU16Funcs[0xA] = this.ReadNop16; this.ReadU16Funcs[0xC] = this.ReadNop16; this.ReadU32Funcs[0x8] = this.ReadNop32; this.ReadU32Funcs[0xA] = this.ReadNop32; this.ReadU32Funcs[0xC] = this.ReadNop32; } private void ResetRomBank2() { this.romBank2 = null; this.romBank2Mask = 0; this.ReadU8Funcs[0x9] = this.ReadEeprom8; this.ReadU8Funcs[0xB] = this.ReadEeprom8; this.ReadU8Funcs[0xD] = this.ReadEeprom8; this.ReadU16Funcs[0x9] = this.ReadEeprom16; this.ReadU16Funcs[0xB] = this.ReadEeprom16; this.ReadU16Funcs[0xD] = this.ReadEeprom16; this.ReadU32Funcs[0x9] = this.ReadEeprom32; this.ReadU32Funcs[0xB] = this.ReadEeprom32; this.ReadU32Funcs[0xD] = this.ReadEeprom32; this.WriteU8Funcs[0x9] = this.WriteEeprom8; this.WriteU8Funcs[0xB] = this.WriteEeprom8; this.WriteU8Funcs[0xD] = this.WriteEeprom8; this.WriteU16Funcs[0x9] = this.WriteEeprom16; this.WriteU16Funcs[0xB] = this.WriteEeprom16; this.WriteU16Funcs[0xD] = this.WriteEeprom16; this.WriteU32Funcs[0x9] = this.WriteEeprom32; this.WriteU32Funcs[0xB] = this.WriteEeprom32; this.WriteU32Funcs[0xD] = this.WriteEeprom32; } } }