BizHawk/attic/GarboDev/Memory.cs

1982 lines
69 KiB
C#

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<uint> vramUpdated = new List<uint>();
private List<uint> palUpdated = new List<uint>();
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<uint> VramUpdated
{
get
{
List<uint> old = this.vramUpdated;
for (int i = 0; i < old.Count; i++)
{
vramHit[old[i]] = false;
}
this.vramUpdated = new List<uint>();
return old;
}
}
public List<uint> PalUpdated
{
get
{
List<uint> old = this.palUpdated;
for (int i = 0; i < old.Count; i++)
{
palHit[old[i]] = false;
}
this.palUpdated = new List<uint>();
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;
}
}
}