play around with garbodev core. probably worse than meteor, in general. managed. small compile-time change in MainForm.cs is needed to switch between the two. garbodev source files don't have any license on them at all, so 99.99% chance that this will be axed. why did i do it?
This commit is contained in:
parent
c3a74edd7f
commit
4834f40f6c
|
@ -231,6 +231,18 @@
|
|||
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.cs" />
|
||||
<Compile Include="Consoles\Nintendo\Gameboy\GBColors.cs" />
|
||||
<Compile Include="Consoles\Nintendo\Gameboy\LibGambatte.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\Arm7Processor.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\ArmCore.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\FastArmCore.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\FastDispatchCore.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\GbaManager.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\IRenderer.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\Memory.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\Renderer.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\Renderers.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\SoundManager.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\ThumbCore.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBAAlt\VideoManager.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBA\LibMeteor.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBA\Meteor.cs" />
|
||||
<Compile Include="Consoles\Nintendo\NES\APU.cs" />
|
||||
|
|
|
@ -0,0 +1,501 @@
|
|||
namespace GarboDev
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
public class Arm7Processor
|
||||
{
|
||||
private Memory memory = null;
|
||||
private FastArmCore armCore = null;
|
||||
private ThumbCore thumbCore = null;
|
||||
private Dictionary<uint, bool> breakpoints = null;
|
||||
|
||||
private int cycles = 0;
|
||||
private int timerCycles = 0;
|
||||
private int soundCycles = 0;
|
||||
|
||||
// CPU mode definitions
|
||||
public const uint USR = 0x10;
|
||||
public const uint FIQ = 0x11;
|
||||
public const uint IRQ = 0x12;
|
||||
public const uint SVC = 0x13;
|
||||
public const uint ABT = 0x17;
|
||||
public const uint UND = 0x1B;
|
||||
public const uint SYS = 0x1F;
|
||||
|
||||
// CPSR bit definitions
|
||||
public const int N_BIT = 31;
|
||||
public const int Z_BIT = 30;
|
||||
public const int C_BIT = 29;
|
||||
public const int V_BIT = 28;
|
||||
public const int I_BIT = 7;
|
||||
public const int F_BIT = 6;
|
||||
public const int T_BIT = 5;
|
||||
|
||||
public const uint N_MASK = (uint)(1U << N_BIT);
|
||||
public const uint Z_MASK = (uint)(1U << Z_BIT);
|
||||
public const uint C_MASK = (uint)(1U << C_BIT);
|
||||
public const uint V_MASK = (uint)(1U << V_BIT);
|
||||
public const uint I_MASK = (uint)(1U << I_BIT);
|
||||
public const uint F_MASK = (uint)(1U << F_BIT);
|
||||
public const uint T_MASK = (uint)(1U << T_BIT);
|
||||
|
||||
// Standard registers
|
||||
private uint[] registers = new uint[16];
|
||||
private uint cpsr = 0;
|
||||
|
||||
// Banked registers
|
||||
private uint[] bankedFIQ = new uint[7];
|
||||
private uint[] bankedIRQ = new uint[2];
|
||||
private uint[] bankedSVC = new uint[2];
|
||||
private uint[] bankedABT = new uint[2];
|
||||
private uint[] bankedUND = new uint[2];
|
||||
|
||||
// Saved CPSR's
|
||||
private uint spsrFIQ = 0;
|
||||
private uint spsrIRQ = 0;
|
||||
private uint spsrSVC = 0;
|
||||
private uint spsrABT = 0;
|
||||
private uint spsrUND = 0;
|
||||
|
||||
private ushort keyState;
|
||||
|
||||
private bool breakpointHit = false;
|
||||
private bool cpuHalted = false;
|
||||
|
||||
public ushort KeyState
|
||||
{
|
||||
set { this.keyState = value; }
|
||||
}
|
||||
|
||||
public int Cycles
|
||||
{
|
||||
get { return this.cycles; }
|
||||
set { this.cycles = value; }
|
||||
}
|
||||
|
||||
public bool ArmState
|
||||
{
|
||||
get { return (this.cpsr & Arm7Processor.T_MASK) != Arm7Processor.T_MASK; }
|
||||
}
|
||||
|
||||
public uint[] Registers
|
||||
{
|
||||
get { return this.registers; }
|
||||
}
|
||||
|
||||
public uint CPSR
|
||||
{
|
||||
get { return this.cpsr; }
|
||||
set { this.cpsr = value; }
|
||||
}
|
||||
|
||||
public bool SPSRExists
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (this.cpsr & 0x1F)
|
||||
{
|
||||
case USR:
|
||||
case SYS:
|
||||
return false;
|
||||
case FIQ:
|
||||
case SVC:
|
||||
case ABT:
|
||||
case IRQ:
|
||||
case UND:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint SPSR
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (this.cpsr & 0x1F)
|
||||
{
|
||||
case USR:
|
||||
case SYS:
|
||||
return 0xFFFFFFFF;
|
||||
case FIQ:
|
||||
return this.spsrFIQ;
|
||||
case SVC:
|
||||
return this.spsrSVC;
|
||||
case ABT:
|
||||
return this.spsrABT;
|
||||
case IRQ:
|
||||
return this.spsrIRQ;
|
||||
case UND:
|
||||
return this.spsrUND;
|
||||
default:
|
||||
throw new Exception("Unhandled CPSR state...");
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (this.cpsr & 0x1F)
|
||||
{
|
||||
case USR:
|
||||
case SYS:
|
||||
break;
|
||||
case FIQ:
|
||||
this.spsrFIQ = value;
|
||||
break;
|
||||
case SVC:
|
||||
this.spsrSVC = value;
|
||||
break;
|
||||
case ABT:
|
||||
this.spsrABT = value;
|
||||
break;
|
||||
case IRQ:
|
||||
this.spsrIRQ = value;
|
||||
break;
|
||||
case UND:
|
||||
this.spsrUND = value;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unhandled CPSR state...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<uint, bool> Breakpoints
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.breakpoints;
|
||||
}
|
||||
}
|
||||
|
||||
public bool BreakpointHit
|
||||
{
|
||||
get { return this.breakpointHit; }
|
||||
set { this.breakpointHit = value; }
|
||||
}
|
||||
|
||||
public Arm7Processor(Memory memory)
|
||||
{
|
||||
this.memory = memory;
|
||||
this.memory.Processor = this;
|
||||
this.armCore = new FastArmCore(this, this.memory);
|
||||
this.thumbCore = new ThumbCore(this, this.memory);
|
||||
this.breakpoints = new Dictionary<uint, bool>();
|
||||
this.breakpointHit = false;
|
||||
}
|
||||
|
||||
private void SwapRegsHelper(uint[] swapRegs)
|
||||
{
|
||||
for (int i = 14; i > 14 - swapRegs.Length; i--)
|
||||
{
|
||||
uint tmp = this.registers[i];
|
||||
this.registers[i] = swapRegs[swapRegs.Length - (14 - i) - 1];
|
||||
swapRegs[swapRegs.Length - (14 - i) - 1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
private void SwapRegisters(uint bank)
|
||||
{
|
||||
switch (bank & 0x1F)
|
||||
{
|
||||
case FIQ:
|
||||
this.SwapRegsHelper(this.bankedFIQ);
|
||||
break;
|
||||
case SVC:
|
||||
this.SwapRegsHelper(this.bankedSVC);
|
||||
break;
|
||||
case ABT:
|
||||
this.SwapRegsHelper(this.bankedABT);
|
||||
break;
|
||||
case IRQ:
|
||||
this.SwapRegsHelper(this.bankedIRQ);
|
||||
break;
|
||||
case UND:
|
||||
this.SwapRegsHelper(this.bankedUND);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteCpsr(uint newCpsr)
|
||||
{
|
||||
if ((newCpsr & 0x1F) != (this.cpsr & 0x1F))
|
||||
{
|
||||
// Swap out the old registers
|
||||
this.SwapRegisters(this.cpsr);
|
||||
// Swap in the new registers
|
||||
this.SwapRegisters(newCpsr);
|
||||
}
|
||||
|
||||
this.cpsr = newCpsr;
|
||||
}
|
||||
|
||||
public void EnterException(uint mode, uint vector, bool interruptsDisabled, bool fiqDisabled)
|
||||
{
|
||||
uint oldCpsr = this.cpsr;
|
||||
|
||||
if ((oldCpsr & Arm7Processor.T_MASK) != 0)
|
||||
{
|
||||
registers[15] += 2U;
|
||||
}
|
||||
|
||||
// Clear T bit, and set mode
|
||||
uint newCpsr = (oldCpsr & ~0x3FU) | mode;
|
||||
if (interruptsDisabled) newCpsr |= 1 << 7;
|
||||
if (fiqDisabled) newCpsr |= 1 << 6;
|
||||
this.WriteCpsr(newCpsr);
|
||||
|
||||
this.SPSR = oldCpsr;
|
||||
registers[14] = registers[15];
|
||||
registers[15] = vector;
|
||||
|
||||
this.ReloadQueue();
|
||||
}
|
||||
|
||||
public void RequestIrq(int irq)
|
||||
{
|
||||
ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF);
|
||||
iflag |= (ushort)(1 << irq);
|
||||
Memory.WriteU16(this.memory.IORam, Memory.IF, iflag);
|
||||
}
|
||||
|
||||
public void FireIrq()
|
||||
{
|
||||
ushort ime = Memory.ReadU16(this.memory.IORam, Memory.IME);
|
||||
ushort ie = Memory.ReadU16(this.memory.IORam, Memory.IE);
|
||||
ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF);
|
||||
|
||||
if ((ie & (iflag)) != 0 && (ime & 1) != 0 && (this.cpsr & (1 << 7)) == 0)
|
||||
{
|
||||
// Off to the irq exception vector
|
||||
this.EnterException(Arm7Processor.IRQ, 0x18, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset(bool skipBios)
|
||||
{
|
||||
this.breakpointHit = false;
|
||||
this.cpuHalted = false;
|
||||
|
||||
// Default to ARM state
|
||||
this.cycles = 0;
|
||||
this.timerCycles = 0;
|
||||
this.soundCycles = 0;
|
||||
|
||||
this.bankedSVC[0] = 0x03007FE0;
|
||||
this.bankedIRQ[0] = 0x03007FA0;
|
||||
|
||||
this.cpsr = SYS;
|
||||
this.spsrSVC = this.cpsr;
|
||||
for (int i = 0; i < 15; i++) this.registers[i] = 0;
|
||||
|
||||
if (skipBios)
|
||||
{
|
||||
this.registers[15] = 0x8000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.registers[15] = 0;
|
||||
}
|
||||
|
||||
this.armCore.BeginExecution();
|
||||
}
|
||||
|
||||
public void Halt()
|
||||
{
|
||||
this.cpuHalted = true;
|
||||
this.cycles = 0;
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
this.breakpointHit = false;
|
||||
|
||||
if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK)
|
||||
{
|
||||
this.thumbCore.Step();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.armCore.Step();
|
||||
}
|
||||
|
||||
this.UpdateTimers();
|
||||
}
|
||||
|
||||
public void ReloadQueue()
|
||||
{
|
||||
if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK)
|
||||
{
|
||||
this.thumbCore.BeginExecution();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.armCore.BeginExecution();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTimer(int timer, int cycles, bool countUp)
|
||||
{
|
||||
ushort control = Memory.ReadU16(this.memory.IORam, Memory.TM0CNT + (uint)(timer * 4));
|
||||
|
||||
// Make sure timer is enabled, or count up is disabled
|
||||
if ((control & (1 << 7)) == 0) return;
|
||||
if (!countUp && (control & (1 << 2)) != 0) return;
|
||||
|
||||
if (!countUp)
|
||||
{
|
||||
switch (control & 3)
|
||||
{
|
||||
case 0: cycles *= 1 << 10; break;
|
||||
case 1: cycles *= 1 << 4; break;
|
||||
case 2: cycles *= 1 << 2; break;
|
||||
// Don't need to do anything for case 3
|
||||
}
|
||||
}
|
||||
|
||||
this.memory.TimerCnt[timer] += (uint)cycles;
|
||||
uint timerCnt = this.memory.TimerCnt[timer] >> 10;
|
||||
|
||||
if (timerCnt > 0xffff)
|
||||
{
|
||||
ushort soundCntX = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_X);
|
||||
if ((soundCntX & (1 << 7)) != 0)
|
||||
{
|
||||
ushort soundCntH = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_H);
|
||||
if (timer == ((soundCntH >> 10) & 1))
|
||||
{
|
||||
// FIFO A overflow
|
||||
this.memory.SoundManager.DequeueA();
|
||||
if (this.memory.SoundManager.QueueSizeA < 16)
|
||||
{
|
||||
this.memory.FifoDma(1);
|
||||
// TODO
|
||||
if (this.memory.SoundManager.QueueSizeA < 16)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timer == ((soundCntH >> 14) & 1))
|
||||
{
|
||||
// FIFO B overflow
|
||||
this.memory.SoundManager.DequeueB();
|
||||
if (this.memory.SoundManager.QueueSizeB < 16)
|
||||
{
|
||||
this.memory.FifoDma(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overflow, attempt to fire IRQ
|
||||
if ((control & (1 << 6)) != 0)
|
||||
{
|
||||
this.RequestIrq(3 + timer);
|
||||
}
|
||||
|
||||
if (timer < 3)
|
||||
{
|
||||
ushort control2 = Memory.ReadU16(this.memory.IORam, Memory.TM0CNT + (uint)((timer + 1) * 4));
|
||||
if ((control2 & (1 << 2)) != 0)
|
||||
{
|
||||
// Count-up
|
||||
this.UpdateTimer(timer + 1, (int)((timerCnt >> 16) << 10), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the original value
|
||||
uint count = Memory.ReadU16(this.memory.IORam, Memory.TM0D + (uint)(timer * 4));
|
||||
this.memory.TimerCnt[timer] = count << 10;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTimers()
|
||||
{
|
||||
int cycles = this.timerCycles - this.cycles;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
this.UpdateTimer(i, cycles, false);
|
||||
}
|
||||
|
||||
this.timerCycles = this.cycles;
|
||||
}
|
||||
|
||||
public void UpdateKeyState()
|
||||
{
|
||||
ushort KEYCNT = this.memory.ReadU16Debug(Memory.REG_BASE + Memory.KEYCNT);
|
||||
|
||||
if ((KEYCNT & (1 << 14)) != 0)
|
||||
{
|
||||
if ((KEYCNT & (1 << 15)) != 0)
|
||||
{
|
||||
KEYCNT &= 0x3FF;
|
||||
if (((~this.keyState) & KEYCNT) == KEYCNT)
|
||||
this.RequestIrq(12);
|
||||
}
|
||||
else
|
||||
{
|
||||
KEYCNT &= 0x3FF;
|
||||
if (((~this.keyState) & KEYCNT) != 0)
|
||||
this.RequestIrq(12);
|
||||
}
|
||||
}
|
||||
|
||||
this.memory.KeyState = this.keyState;
|
||||
}
|
||||
|
||||
public void UpdateSound()
|
||||
{
|
||||
this.memory.SoundManager.Mix(this.soundCycles);
|
||||
this.soundCycles = 0;
|
||||
}
|
||||
|
||||
public void Execute(int cycles)
|
||||
{
|
||||
this.cycles += cycles;
|
||||
this.timerCycles += cycles;
|
||||
this.soundCycles += cycles;
|
||||
this.breakpointHit = false;
|
||||
|
||||
if (this.cpuHalted)
|
||||
{
|
||||
ushort ie = Memory.ReadU16(this.memory.IORam, Memory.IE);
|
||||
ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF);
|
||||
|
||||
if ((ie & iflag) != 0)
|
||||
{
|
||||
this.cpuHalted = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.cycles = 0;
|
||||
this.UpdateTimers();
|
||||
this.UpdateSound();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
while (this.cycles > 0)
|
||||
{
|
||||
if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK)
|
||||
{
|
||||
this.thumbCore.Execute();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.armCore.Execute();
|
||||
}
|
||||
|
||||
this.UpdateTimers();
|
||||
this.UpdateSound();
|
||||
|
||||
if (this.breakpointHit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,482 @@
|
|||
//#define ARM_DEBUG
|
||||
|
||||
namespace GarboDev
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Timers;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation;
|
||||
using BizHawk;
|
||||
|
||||
public class GbaManager : IEmulator, ISoundProvider, IVideoProvider
|
||||
{
|
||||
public const int cpuFreq = 16 * 1024 * 1024;
|
||||
|
||||
private int framesRendered;
|
||||
|
||||
private Arm7Processor arm7 = null;
|
||||
private Memory memory = null;
|
||||
private VideoManager videoManager = null;
|
||||
private SoundManager soundManager = null;
|
||||
|
||||
private bool skipBios = false;
|
||||
|
||||
public delegate void CpuUpdateDelegate(Arm7Processor processor, Memory memory);
|
||||
private event CpuUpdateDelegate onCpuUpdate = null;
|
||||
|
||||
public Arm7Processor Arm7
|
||||
{
|
||||
get { return this.arm7; }
|
||||
}
|
||||
|
||||
public VideoManager VideoManager
|
||||
{
|
||||
get { return this.videoManager; }
|
||||
}
|
||||
|
||||
public SoundManager SoundManager
|
||||
{
|
||||
get { return this.soundManager; }
|
||||
}
|
||||
|
||||
public Memory Memory
|
||||
{
|
||||
get { return this.memory; }
|
||||
}
|
||||
|
||||
public Dictionary<uint, bool> Breakpoints
|
||||
{
|
||||
get { return this.arm7.Breakpoints; }
|
||||
}
|
||||
|
||||
public ushort KeyState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.memory != null)
|
||||
{
|
||||
return this.memory.KeyState;
|
||||
}
|
||||
|
||||
return 0x3FF;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.arm7.KeyState = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int FramesRendered
|
||||
{
|
||||
get { return this.framesRendered; }
|
||||
set { this.framesRendered = value; }
|
||||
}
|
||||
|
||||
public event CpuUpdateDelegate OnCpuUpdate
|
||||
{
|
||||
add
|
||||
{
|
||||
this.onCpuUpdate += value;
|
||||
this.onCpuUpdate(this.arm7, this.memory);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.onCpuUpdate -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SkipBios
|
||||
{
|
||||
get { return this.skipBios; }
|
||||
set { this.skipBios = value; }
|
||||
}
|
||||
|
||||
public GbaManager(CoreComm comm)
|
||||
{
|
||||
_corecomm = comm;
|
||||
|
||||
this.memory = new Memory();
|
||||
this.arm7 = new Arm7Processor(this.memory);
|
||||
this.videoManager = new VideoManager(this);
|
||||
this.videoManager.Memory = this.memory;
|
||||
this.soundManager = new SoundManager(this.memory, 44100);
|
||||
|
||||
this.framesRendered = 0;
|
||||
Renderer renderer = new Renderer();
|
||||
renderer.Initialize(null);
|
||||
VideoManager.Renderer = renderer;
|
||||
|
||||
videoManager.Presenter = delegate(uint[] data)
|
||||
{
|
||||
Buffer.BlockCopy(data, 0, this.vbuf, 0, 240 * 160 * 4);
|
||||
};
|
||||
}
|
||||
|
||||
public void Load(byte[] rom, byte[] bios)
|
||||
{
|
||||
LoadBios(bios);
|
||||
LoadRom(rom);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
//this.Halt();
|
||||
|
||||
this.arm7.Reset(this.skipBios);
|
||||
this.memory.Reset();
|
||||
this.videoManager.Reset();
|
||||
}
|
||||
|
||||
public void AudioMixerStereo(short[] buffer, int length)
|
||||
{
|
||||
// even = left, odd = right
|
||||
if (this.soundManager.SamplesMixed > Math.Max(500, length))
|
||||
{
|
||||
this.soundManager.GetSamples(buffer, length);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader state)
|
||||
{
|
||||
}
|
||||
|
||||
public void SaveState(BinaryWriter state)
|
||||
{
|
||||
state.Write("GARB");
|
||||
}
|
||||
|
||||
public void LoadBios(byte[] biosRom)
|
||||
{
|
||||
this.memory.LoadBios(biosRom);
|
||||
|
||||
if (this.onCpuUpdate != null)
|
||||
{
|
||||
this.onCpuUpdate(this.arm7, this.memory);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadRom(byte[] cartRom)
|
||||
{
|
||||
//this.Halt();
|
||||
|
||||
/*
|
||||
byte[] logo = new byte[]
|
||||
{
|
||||
0x24,0xff,0xae,0x51,0x69,0x9a,0xa2,0x21,
|
||||
0x3d,0x84,0x82,0x0a,0x84,0xe4,0x09,0xad,
|
||||
0x11,0x24,0x8b,0x98,0xc0,0x81,0x7f,0x21,
|
||||
0xa3,0x52,0xbe,0x19,0x93,0x09,0xce,0x20,
|
||||
0x10,0x46,0x4a,0x4a,0xf8,0x27,0x31,0xec,
|
||||
0x58,0xc7,0xe8,0x33,0x82,0xe3,0xce,0xbf,
|
||||
0x85,0xf4,0xdf,0x94,0xce,0x4b,0x09,0xc1,
|
||||
0x94,0x56,0x8a,0xc0,0x13,0x72,0xa7,0xfc,
|
||||
0x9f,0x84,0x4d,0x73,0xa3,0xca,0x9a,0x61,
|
||||
0x58,0x97,0xa3,0x27,0xfc,0x03,0x98,0x76,
|
||||
0x23,0x1d,0xc7,0x61,0x03,0x04,0xae,0x56,
|
||||
0xbf,0x38,0x84,0x00,0x40,0xa7,0x0e,0xfd,
|
||||
0xff,0x52,0xfe,0x03,0x6f,0x95,0x30,0xf1,
|
||||
0x97,0xfb,0xc0,0x85,0x60,0xd6,0x80,0x25,
|
||||
0xa9,0x63,0xbe,0x03,0x01,0x4e,0x38,0xe2,
|
||||
0xf9,0xa2,0x34,0xff,0xbb,0x3e,0x03,0x44,
|
||||
0x78,0x00,0x90,0xcb,0x88,0x11,0x3a,0x94,
|
||||
0x65,0xc0,0x7c,0x63,0x87,0xf0,0x3c,0xaf,
|
||||
0xd6,0x25,0xe4,0x8b,0x38,0x0a,0xac,0x72,
|
||||
0x21,0xd4,0xf8,0x07
|
||||
};
|
||||
|
||||
Array.Copy(logo, 0, cartRom, 4, logo.Length);
|
||||
cartRom[0xB2] = 0x96;
|
||||
cartRom[0xBD] = 0;
|
||||
for (int i = 0xA0; i <= 0xBC; i++) cartRom[0xBD] = (byte)(cartRom[0xBD] - cartRom[i]);
|
||||
cartRom[0xBD] = (byte)((cartRom[0xBD] - 0x19) & 0xFF);
|
||||
*/
|
||||
this.memory.LoadCartridge(cartRom);
|
||||
|
||||
this.Reset();
|
||||
|
||||
if (this.onCpuUpdate != null)
|
||||
{
|
||||
this.onCpuUpdate(this.arm7, this.memory);
|
||||
}
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
//this.Halt();
|
||||
|
||||
this.arm7.Step();
|
||||
|
||||
if (this.onCpuUpdate != null)
|
||||
{
|
||||
this.onCpuUpdate(this.arm7, this.memory);
|
||||
}
|
||||
}
|
||||
|
||||
public void StepScanline()
|
||||
{
|
||||
//this.Halt();
|
||||
|
||||
this.arm7.Execute(960);
|
||||
this.videoManager.RenderLine();
|
||||
this.videoManager.EnterHBlank(this.arm7);
|
||||
this.arm7.Execute(272);
|
||||
this.videoManager.LeaveHBlank(this.arm7);
|
||||
|
||||
if (this.onCpuUpdate != null)
|
||||
{
|
||||
this.onCpuUpdate(this.arm7, this.memory);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateInputState()
|
||||
{
|
||||
ushort ret = 0;
|
||||
if (_controller["Up"]) ret |= 64;
|
||||
if (_controller["Down"]) ret |= 128;
|
||||
if (_controller["Left"]) ret |= 32;
|
||||
if (_controller["Right"]) ret |= 16;
|
||||
if (_controller["Select"]) ret |= 4;
|
||||
if (_controller["Start"]) ret |= 8;
|
||||
if (_controller["B"]) ret |= 2;
|
||||
if (_controller["A"]) ret |= 1;
|
||||
if (_controller["L"]) ret |= 512;
|
||||
if (_controller["R"]) ret |= 256;
|
||||
ret ^= 0x3ff;
|
||||
KeyState = ret;
|
||||
}
|
||||
|
||||
private void StepFrame()
|
||||
{
|
||||
UpdateInputState();
|
||||
if (_controller["Power"])
|
||||
Reset();
|
||||
|
||||
int vramCycles = 0;
|
||||
bool inHblank = false;
|
||||
|
||||
//HighPerformanceTimer profileTimer = new HighPerformanceTimer();
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
|
||||
const int cycleStep = 123;
|
||||
|
||||
|
||||
if (vramCycles <= 0)
|
||||
{
|
||||
if (inHblank)
|
||||
{
|
||||
vramCycles += 960;
|
||||
bool HitVBlank = this.videoManager.LeaveHBlank(this.arm7);
|
||||
inHblank = false;
|
||||
if (HitVBlank)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
vramCycles += 272;
|
||||
this.videoManager.RenderLine();
|
||||
this.videoManager.EnterHBlank(this.arm7);
|
||||
inHblank = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.arm7.Execute(cycleStep);
|
||||
#if ARM_DEBUG
|
||||
if (this.arm7.BreakpointHit)
|
||||
{
|
||||
this.waitingToHalt = true;
|
||||
Monitor.Wait(this);
|
||||
}
|
||||
#endif
|
||||
vramCycles -= cycleStep;
|
||||
this.arm7.FireIrq();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
IVideoProvider IEmulator.VideoProvider
|
||||
{
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
ISoundProvider IEmulator.SoundProvider
|
||||
{
|
||||
get { return this; }
|
||||
}
|
||||
|
||||
ISyncSoundProvider IEmulator.SyncSoundProvider
|
||||
{
|
||||
get { return new FakeSyncSound(this, 735); }
|
||||
}
|
||||
|
||||
bool IEmulator.StartAsyncSound()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void IEmulator.EndAsyncSound()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ControllerDefinition IEmulator.ControllerDefinition
|
||||
{
|
||||
get { return BizHawk.Emulation.Consoles.Nintendo.GBA.GBA.GBAController; }
|
||||
}
|
||||
|
||||
IController _controller;
|
||||
|
||||
IController IEmulator.Controller
|
||||
{
|
||||
get { return _controller; }
|
||||
set { _controller = value; }
|
||||
}
|
||||
|
||||
void IEmulator.FrameAdvance(bool render, bool rendersound)
|
||||
{
|
||||
StepFrame();
|
||||
}
|
||||
|
||||
int IEmulator.Frame
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
int IEmulator.LagCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool IEmulator.IsLagFrame
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
string IEmulator.SystemId
|
||||
{
|
||||
get { return "GBA"; }
|
||||
}
|
||||
|
||||
bool IEmulator.DeterministicEmulation
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
byte[] IEmulator.ReadSaveRam()
|
||||
{
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
void IEmulator.StoreSaveRam(byte[] data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IEmulator.ClearSaveRam()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool IEmulator.SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
set
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void IEmulator.ResetFrameCounter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IEmulator.SaveStateText(TextWriter writer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IEmulator.LoadStateText(TextReader reader)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IEmulator.SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IEmulator.LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
byte[] IEmulator.SaveStateBinary()
|
||||
{
|
||||
return new byte[16];
|
||||
}
|
||||
|
||||
CoreComm _corecomm;
|
||||
|
||||
CoreComm IEmulator.CoreComm
|
||||
{
|
||||
get { return _corecomm; }
|
||||
}
|
||||
|
||||
IList<MemoryDomain> IEmulator.MemoryDomains
|
||||
{
|
||||
get { return new List<MemoryDomain>(); }
|
||||
}
|
||||
|
||||
MemoryDomain IEmulator.MainMemory
|
||||
{
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ISoundProvider.GetSamples(short[] samples)
|
||||
{
|
||||
AudioMixerStereo(samples, samples.Length);
|
||||
}
|
||||
|
||||
void ISoundProvider.DiscardSamples()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int ISoundProvider.MaxVolume
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
int[] vbuf = new int[240 * 160];
|
||||
int[] IVideoProvider.GetVideoBuffer() { return vbuf; }
|
||||
int IVideoProvider.VirtualWidth { get { return 240; } }
|
||||
int IVideoProvider.BufferWidth { get { return 240; } }
|
||||
int IVideoProvider.BufferHeight { get { return 160; } }
|
||||
int IVideoProvider.BackgroundColor { get { return unchecked((int)0xff000000); } }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
namespace GarboDev
|
||||
{
|
||||
using System;
|
||||
|
||||
public interface IRenderer
|
||||
{
|
||||
Memory Memory { set; }
|
||||
void Initialize(object data);
|
||||
void Reset();
|
||||
void RenderLine(int line);
|
||||
uint[] ShowFrame();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,952 @@
|
|||
namespace GarboDev
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
public partial class Renderer : IRenderer
|
||||
{
|
||||
private Memory memory;
|
||||
private uint[] scanline = new uint[240];
|
||||
private byte[] blend = new byte[240];
|
||||
private byte[] windowCover = new byte[240];
|
||||
private uint[] back = new uint[240 * 160];
|
||||
//private uint[] front = new uint[240 * 160];
|
||||
private const uint pitch = 240;
|
||||
|
||||
// Convenience variable as I use it everywhere, set once in RenderLine
|
||||
private ushort dispCnt;
|
||||
|
||||
// Window helper variables
|
||||
private byte win0x1, win0x2, win0y1, win0y2;
|
||||
private byte win1x1, win1x2, win1y1, win1y2;
|
||||
private byte win0Enabled, win1Enabled, winObjEnabled, winOutEnabled;
|
||||
private bool winEnabled;
|
||||
|
||||
private byte blendSource, blendTarget;
|
||||
private byte blendA, blendB, blendY;
|
||||
private int blendType;
|
||||
|
||||
private int curLine = 0;
|
||||
|
||||
private static uint[] colorLUT;
|
||||
|
||||
static Renderer()
|
||||
{
|
||||
colorLUT = new uint[0x10000];
|
||||
// Pre-calculate the color LUT
|
||||
for (uint i = 0; i <= 0xFFFF; i++)
|
||||
{
|
||||
uint r = (i & 0x1FU);
|
||||
uint g = (i & 0x3E0U) >> 5;
|
||||
uint b = (i & 0x7C00U) >> 10;
|
||||
r = (r << 3) | (r >> 2);
|
||||
g = (g << 3) | (g >> 2);
|
||||
b = (b << 3) | (b >> 2);
|
||||
colorLUT[i] = (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
|
||||
public Memory Memory
|
||||
{
|
||||
set { this.memory = value; }
|
||||
}
|
||||
|
||||
public void Initialize(object data)
|
||||
{
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
public uint[] ShowFrame()
|
||||
{
|
||||
//Array.Copy(this.back, this.front, this.front.Length);
|
||||
|
||||
//return this.front;
|
||||
return this.back;
|
||||
}
|
||||
|
||||
public void RenderLine(int line)
|
||||
{
|
||||
this.curLine = line;
|
||||
|
||||
// Render the line
|
||||
this.dispCnt = Memory.ReadU16(this.memory.IORam, Memory.DISPCNT);
|
||||
|
||||
if ((this.dispCnt & (1 << 7)) != 0)
|
||||
{
|
||||
uint bgColor = Renderer.GbaTo32((ushort)0x7FFF);
|
||||
for (int i = 0; i < 240; i++) this.scanline[i] = bgColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.winEnabled = false;
|
||||
|
||||
if ((this.dispCnt & (1 << 13)) != 0)
|
||||
{
|
||||
// Calculate window 0 information
|
||||
ushort winy = Memory.ReadU16(this.memory.IORam, Memory.WIN0V);
|
||||
this.win0y1 = (byte)(winy >> 8);
|
||||
this.win0y2 = (byte)(winy & 0xff);
|
||||
ushort winx = Memory.ReadU16(this.memory.IORam, Memory.WIN0H);
|
||||
this.win0x1 = (byte)(winx >> 8);
|
||||
this.win0x2 = (byte)(winx & 0xff);
|
||||
|
||||
if (this.win0x2 > 240 || this.win0x1 > this.win0x2)
|
||||
{
|
||||
this.win0x2 = 240;
|
||||
}
|
||||
|
||||
if (this.win0y2 > 160 || this.win0y1 > this.win0y2)
|
||||
{
|
||||
this.win0y2 = 160;
|
||||
}
|
||||
|
||||
this.win0Enabled = this.memory.IORam[Memory.WININ];
|
||||
this.winEnabled = true;
|
||||
}
|
||||
|
||||
if ((this.dispCnt & (1 << 14)) != 0)
|
||||
{
|
||||
// Calculate window 1 information
|
||||
ushort winy = Memory.ReadU16(this.memory.IORam, Memory.WIN1V);
|
||||
this.win1y1 = (byte)(winy >> 8);
|
||||
this.win1y2 = (byte)(winy & 0xff);
|
||||
ushort winx = Memory.ReadU16(this.memory.IORam, Memory.WIN1H);
|
||||
this.win1x1 = (byte)(winx >> 8);
|
||||
this.win1x2 = (byte)(winx & 0xff);
|
||||
|
||||
if (this.win1x2 > 240 || this.win1x1 > this.win1x2)
|
||||
{
|
||||
this.win1x2 = 240;
|
||||
}
|
||||
|
||||
if (this.win1y2 > 160 || this.win1y1 > this.win1y2)
|
||||
{
|
||||
this.win1y2 = 160;
|
||||
}
|
||||
|
||||
this.win1Enabled = this.memory.IORam[Memory.WININ + 1];
|
||||
this.winEnabled = true;
|
||||
}
|
||||
|
||||
if ((this.dispCnt & (1 << 15)) != 0 && (this.dispCnt & (1 << 12)) != 0)
|
||||
{
|
||||
// Object windows are enabled
|
||||
this.winObjEnabled = this.memory.IORam[Memory.WINOUT + 1];
|
||||
this.winEnabled = true;
|
||||
}
|
||||
|
||||
if (this.winEnabled)
|
||||
{
|
||||
this.winOutEnabled = this.memory.IORam[Memory.WINOUT];
|
||||
}
|
||||
|
||||
// Calculate blending information
|
||||
ushort bldcnt = Memory.ReadU16(this.memory.IORam, Memory.BLDCNT);
|
||||
this.blendType = (bldcnt >> 6) & 0x3;
|
||||
this.blendSource = (byte)(bldcnt & 0x3F);
|
||||
this.blendTarget = (byte)((bldcnt >> 8) & 0x3F);
|
||||
|
||||
ushort bldalpha = Memory.ReadU16(this.memory.IORam, Memory.BLDALPHA);
|
||||
this.blendA = (byte)(bldalpha & 0x1F);
|
||||
if (this.blendA > 0x10) this.blendA = 0x10;
|
||||
this.blendB = (byte)((bldalpha >> 8) & 0x1F);
|
||||
if (this.blendB > 0x10) this.blendB = 0x10;
|
||||
|
||||
this.blendY = (byte)(this.memory.IORam[Memory.BLDY] & 0x1F);
|
||||
if (this.blendY > 0x10) this.blendY = 0x10;
|
||||
|
||||
switch (this.dispCnt & 0x7)
|
||||
{
|
||||
case 0: this.RenderMode0Line(); break;
|
||||
case 1: this.RenderMode1Line(); break;
|
||||
case 2: this.RenderMode2Line(); break;
|
||||
case 3: this.RenderMode3Line(); break;
|
||||
case 4: this.RenderMode4Line(); break;
|
||||
case 5: this.RenderMode5Line(); break;
|
||||
}
|
||||
}
|
||||
|
||||
Array.Copy(this.scanline, 0, this.back, this.curLine * Renderer.pitch, Renderer.pitch);
|
||||
}
|
||||
|
||||
private void DrawBackdrop()
|
||||
{
|
||||
byte[] palette = this.memory.PaletteRam;
|
||||
|
||||
// Initialize window coverage buffer if neccesary
|
||||
if (this.winEnabled)
|
||||
{
|
||||
for (int i = 0; i < 240; i++)
|
||||
{
|
||||
this.windowCover[i] = this.winOutEnabled;
|
||||
}
|
||||
|
||||
if ((this.dispCnt & (1 << 15)) != 0)
|
||||
{
|
||||
// Sprite window
|
||||
this.DrawSpriteWindows();
|
||||
}
|
||||
|
||||
if ((this.dispCnt & (1 << 14)) != 0)
|
||||
{
|
||||
// Window 1
|
||||
if (this.curLine >= this.win1y1 && this.curLine < this.win1y2)
|
||||
{
|
||||
for (int i = this.win1x1; i < this.win1x2; i++)
|
||||
{
|
||||
this.windowCover[i] = this.win1Enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.dispCnt & (1 << 13)) != 0)
|
||||
{
|
||||
// Window 0
|
||||
if (this.curLine >= this.win0y1 && this.curLine < this.win0y2)
|
||||
{
|
||||
for (int i = this.win0x1; i < this.win0x2; i++)
|
||||
{
|
||||
this.windowCover[i] = this.win0Enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw backdrop first
|
||||
uint bgColor = Renderer.GbaTo32((ushort)(palette[0] | (palette[1] << 8)));
|
||||
uint modColor = bgColor;
|
||||
|
||||
if (this.blendType == 2 && (this.blendSource & (1 << 5)) != 0)
|
||||
{
|
||||
// Brightness increase
|
||||
uint r = bgColor & 0xFF;
|
||||
uint g = (bgColor >> 8) & 0xFF;
|
||||
uint b = (bgColor >> 16) & 0xFF;
|
||||
r = r + (((0xFF - r) * this.blendY) >> 4);
|
||||
g = g + (((0xFF - g) * this.blendY) >> 4);
|
||||
b = b + (((0xFF - b) * this.blendY) >> 4);
|
||||
modColor = r | (g << 8) | (b << 16);
|
||||
}
|
||||
else if (this.blendType == 3 && (this.blendSource & (1 << 5)) != 0)
|
||||
{
|
||||
// Brightness decrease
|
||||
uint r = bgColor & 0xFF;
|
||||
uint g = (bgColor >> 8) & 0xFF;
|
||||
uint b = (bgColor >> 16) & 0xFF;
|
||||
r = r - ((r * this.blendY) >> 4);
|
||||
g = g - ((g * this.blendY) >> 4);
|
||||
b = b - ((b * this.blendY) >> 4);
|
||||
modColor = r | (g << 8) | (b << 16);
|
||||
}
|
||||
|
||||
if (this.winEnabled)
|
||||
{
|
||||
for (int i = 0; i < 240; i++)
|
||||
{
|
||||
if ((this.windowCover[i] & (1 << 5)) != 0)
|
||||
{
|
||||
this.scanline[i] = modColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.scanline[i] = bgColor;
|
||||
}
|
||||
this.blend[i] = 1 << 5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < 240; i++)
|
||||
{
|
||||
this.scanline[i] = modColor;
|
||||
this.blend[i] = 1 << 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderTextBg(int bg)
|
||||
{
|
||||
if (this.winEnabled)
|
||||
{
|
||||
switch (this.blendType)
|
||||
{
|
||||
case 0:
|
||||
this.RenderTextBgWindow(bg);
|
||||
break;
|
||||
case 1:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderTextBgWindowBlend(bg);
|
||||
else
|
||||
this.RenderTextBgWindow(bg);
|
||||
break;
|
||||
case 2:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderTextBgWindowBrightInc(bg);
|
||||
else
|
||||
this.RenderTextBgWindow(bg);
|
||||
break;
|
||||
case 3:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderTextBgWindowBrightDec(bg);
|
||||
else
|
||||
this.RenderTextBgWindow(bg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (this.blendType)
|
||||
{
|
||||
case 0:
|
||||
this.RenderTextBgNormal(bg);
|
||||
break;
|
||||
case 1:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderTextBgBlend(bg);
|
||||
else
|
||||
this.RenderTextBgNormal(bg);
|
||||
break;
|
||||
case 2:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderTextBgBrightInc(bg);
|
||||
else
|
||||
this.RenderTextBgNormal(bg);
|
||||
break;
|
||||
case 3:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderTextBgBrightDec(bg);
|
||||
else
|
||||
this.RenderTextBgNormal(bg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderRotScaleBg(int bg)
|
||||
{
|
||||
if (this.winEnabled)
|
||||
{
|
||||
switch (this.blendType)
|
||||
{
|
||||
case 0:
|
||||
this.RenderRotScaleBgWindow(bg);
|
||||
break;
|
||||
case 1:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderRotScaleBgWindowBlend(bg);
|
||||
else
|
||||
this.RenderRotScaleBgWindow(bg);
|
||||
break;
|
||||
case 2:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderRotScaleBgWindowBrightInc(bg);
|
||||
else
|
||||
this.RenderRotScaleBgWindow(bg);
|
||||
break;
|
||||
case 3:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderRotScaleBgWindowBrightDec(bg);
|
||||
else
|
||||
this.RenderRotScaleBgWindow(bg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (this.blendType)
|
||||
{
|
||||
case 0:
|
||||
this.RenderRotScaleBgNormal(bg);
|
||||
break;
|
||||
case 1:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderRotScaleBgBlend(bg);
|
||||
else
|
||||
this.RenderRotScaleBgNormal(bg);
|
||||
break;
|
||||
case 2:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderRotScaleBgBrightInc(bg);
|
||||
else
|
||||
this.RenderRotScaleBgNormal(bg);
|
||||
break;
|
||||
case 3:
|
||||
if ((this.blendSource & (1 << bg)) != 0)
|
||||
this.RenderRotScaleBgBrightDec(bg);
|
||||
else
|
||||
this.RenderRotScaleBgNormal(bg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSprites(int pri)
|
||||
{
|
||||
if (this.winEnabled)
|
||||
{
|
||||
switch (this.blendType)
|
||||
{
|
||||
case 0:
|
||||
this.DrawSpritesWindow(pri);
|
||||
break;
|
||||
case 1:
|
||||
if ((this.blendSource & (1 << 4)) != 0)
|
||||
this.DrawSpritesWindowBlend(pri);
|
||||
else
|
||||
this.DrawSpritesWindow(pri);
|
||||
break;
|
||||
case 2:
|
||||
if ((this.blendSource & (1 << 4)) != 0)
|
||||
this.DrawSpritesWindowBrightInc(pri);
|
||||
else
|
||||
this.DrawSpritesWindow(pri);
|
||||
break;
|
||||
case 3:
|
||||
if ((this.blendSource & (1 << 4)) != 0)
|
||||
this.DrawSpritesWindowBrightDec(pri);
|
||||
else
|
||||
this.DrawSpritesWindow(pri);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (this.blendType)
|
||||
{
|
||||
case 0:
|
||||
this.DrawSpritesNormal(pri);
|
||||
break;
|
||||
case 1:
|
||||
if ((this.blendSource & (1 << 4)) != 0)
|
||||
this.DrawSpritesBlend(pri);
|
||||
else
|
||||
this.DrawSpritesNormal(pri);
|
||||
break;
|
||||
case 2:
|
||||
if ((this.blendSource & (1 << 4)) != 0)
|
||||
this.DrawSpritesBrightInc(pri);
|
||||
else
|
||||
this.DrawSpritesNormal(pri);
|
||||
break;
|
||||
case 3:
|
||||
if ((this.blendSource & (1 << 4)) != 0)
|
||||
this.DrawSpritesBrightDec(pri);
|
||||
else
|
||||
this.DrawSpritesNormal(pri);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderMode0Line()
|
||||
{
|
||||
byte[] palette = this.memory.PaletteRam;
|
||||
byte[] vram = this.memory.VideoRam;
|
||||
|
||||
this.DrawBackdrop();
|
||||
|
||||
for (int pri = 3; pri >= 0; pri--)
|
||||
{
|
||||
for (int i = 3; i >= 0; i--)
|
||||
{
|
||||
if ((this.dispCnt & (1 << (8 + i))) != 0)
|
||||
{
|
||||
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
|
||||
|
||||
if ((bgcnt & 0x3) == pri)
|
||||
{
|
||||
this.RenderTextBg(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderMode1Line()
|
||||
{
|
||||
byte[] palette = this.memory.PaletteRam;
|
||||
byte[] vram = this.memory.VideoRam;
|
||||
|
||||
this.DrawBackdrop();
|
||||
|
||||
for (int pri = 3; pri >= 0; pri--)
|
||||
{
|
||||
if ((this.dispCnt & (1 << (8 + 2))) != 0)
|
||||
{
|
||||
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
|
||||
|
||||
if ((bgcnt & 0x3) == pri)
|
||||
{
|
||||
this.RenderRotScaleBg(2);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i >= 0; i--)
|
||||
{
|
||||
if ((this.dispCnt & (1 << (8 + i))) != 0)
|
||||
{
|
||||
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
|
||||
|
||||
if ((bgcnt & 0x3) == pri)
|
||||
{
|
||||
this.RenderTextBg(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderMode2Line()
|
||||
{
|
||||
byte[] palette = this.memory.PaletteRam;
|
||||
byte[] vram = this.memory.VideoRam;
|
||||
|
||||
this.DrawBackdrop();
|
||||
|
||||
for (int pri = 3; pri >= 0; pri--)
|
||||
{
|
||||
for (int i = 3; i >= 2; i--)
|
||||
{
|
||||
if ((this.dispCnt & (1 << (8 + i))) != 0)
|
||||
{
|
||||
ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i);
|
||||
|
||||
if ((bgcnt & 0x3) == pri)
|
||||
{
|
||||
this.RenderRotScaleBg(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderMode3Line()
|
||||
{
|
||||
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
|
||||
|
||||
byte[] palette = this.memory.PaletteRam;
|
||||
byte[] vram = this.memory.VideoRam;
|
||||
|
||||
this.DrawBackdrop();
|
||||
|
||||
byte blendMaskType = (byte)(1 << 2);
|
||||
|
||||
int bgPri = bg2Cnt & 0x3;
|
||||
for (int pri = 3; pri > bgPri; pri--)
|
||||
{
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
|
||||
if ((this.dispCnt & (1 << 10)) != 0)
|
||||
{
|
||||
// Background enabled, render it
|
||||
int x = this.memory.Bgx[0];
|
||||
int y = this.memory.Bgy[0];
|
||||
|
||||
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
|
||||
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
|
||||
|
||||
for (int i = 0; i < 240; i++)
|
||||
{
|
||||
int ax = ((int)x) >> 8;
|
||||
int ay = ((int)y) >> 8;
|
||||
|
||||
if (ax >= 0 && ax < 240 && ay >= 0 && ay < 160)
|
||||
{
|
||||
int curIdx = ((ay * 240) + ax) * 2;
|
||||
this.scanline[i] = Renderer.GbaTo32((ushort)(vram[curIdx] | (vram[curIdx + 1] << 8)));
|
||||
this.blend[i] = blendMaskType;
|
||||
}
|
||||
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
}
|
||||
|
||||
for (int pri = bgPri; pri >= 0; pri--)
|
||||
{
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderMode4Line()
|
||||
{
|
||||
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
|
||||
|
||||
byte[] palette = this.memory.PaletteRam;
|
||||
byte[] vram = this.memory.VideoRam;
|
||||
|
||||
this.DrawBackdrop();
|
||||
|
||||
byte blendMaskType = (byte)(1 << 2);
|
||||
|
||||
int bgPri = bg2Cnt & 0x3;
|
||||
for (int pri = 3; pri > bgPri; pri--)
|
||||
{
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
|
||||
if ((this.dispCnt & (1 << 10)) != 0)
|
||||
{
|
||||
// Background enabled, render it
|
||||
int baseIdx = 0;
|
||||
if ((this.dispCnt & (1 << 4)) == 1 << 4) baseIdx = 0xA000;
|
||||
|
||||
int x = this.memory.Bgx[0];
|
||||
int y = this.memory.Bgy[0];
|
||||
|
||||
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
|
||||
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
|
||||
|
||||
for (int i = 0; i < 240; i++)
|
||||
{
|
||||
int ax = ((int)x) >> 8;
|
||||
int ay = ((int)y) >> 8;
|
||||
|
||||
if (ax >= 0 && ax < 240 && ay >= 0 && ay < 160)
|
||||
{
|
||||
int lookup = vram[baseIdx + (ay * 240) + ax];
|
||||
if (lookup != 0)
|
||||
{
|
||||
this.scanline[i] = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8)));
|
||||
this.blend[i] = blendMaskType;
|
||||
}
|
||||
}
|
||||
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
}
|
||||
|
||||
for (int pri = bgPri; pri >= 0; pri--)
|
||||
{
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderMode5Line()
|
||||
{
|
||||
ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT);
|
||||
|
||||
byte[] palette = this.memory.PaletteRam;
|
||||
byte[] vram = this.memory.VideoRam;
|
||||
|
||||
this.DrawBackdrop();
|
||||
|
||||
byte blendMaskType = (byte)(1 << 2);
|
||||
|
||||
int bgPri = bg2Cnt & 0x3;
|
||||
for (int pri = 3; pri > bgPri; pri--)
|
||||
{
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
|
||||
if ((this.dispCnt & (1 << 10)) != 0)
|
||||
{
|
||||
// Background enabled, render it
|
||||
int baseIdx = 0;
|
||||
if ((this.dispCnt & (1 << 4)) == 1 << 4) baseIdx += 160 * 128 * 2;
|
||||
|
||||
int x = this.memory.Bgx[0];
|
||||
int y = this.memory.Bgy[0];
|
||||
|
||||
short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA);
|
||||
short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC);
|
||||
|
||||
for (int i = 0; i < 240; i++)
|
||||
{
|
||||
int ax = ((int)x) >> 8;
|
||||
int ay = ((int)y) >> 8;
|
||||
|
||||
if (ax >= 0 && ax < 160 && ay >= 0 && ay < 128)
|
||||
{
|
||||
int curIdx = (int)(ay * 160 + ax) * 2;
|
||||
|
||||
this.scanline[i] = Renderer.GbaTo32((ushort)(vram[baseIdx + curIdx] | (vram[baseIdx + curIdx + 1] << 8)));
|
||||
this.blend[i] = blendMaskType;
|
||||
}
|
||||
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
}
|
||||
|
||||
for (int pri = bgPri; pri >= 0; pri--)
|
||||
{
|
||||
this.DrawSprites(pri);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSpriteWindows()
|
||||
{
|
||||
byte[] palette = this.memory.PaletteRam;
|
||||
byte[] vram = this.memory.VideoRam;
|
||||
|
||||
// OBJ must be enabled in this.dispCnt
|
||||
if ((this.dispCnt & (1 << 12)) == 0) return;
|
||||
|
||||
for (int oamNum = 127; oamNum >= 0; oamNum--)
|
||||
{
|
||||
ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0);
|
||||
|
||||
// Not an object window, so continue
|
||||
if (((attr0 >> 10) & 3) != 2) continue;
|
||||
|
||||
ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2);
|
||||
ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4);
|
||||
|
||||
int x = attr1 & 0x1FF;
|
||||
int y = attr0 & 0xFF;
|
||||
|
||||
int width = -1, height = -1;
|
||||
switch ((attr0 >> 14) & 3)
|
||||
{
|
||||
case 0:
|
||||
// Square
|
||||
switch ((attr1 >> 14) & 3)
|
||||
{
|
||||
case 0: width = 8; height = 8; break;
|
||||
case 1: width = 16; height = 16; break;
|
||||
case 2: width = 32; height = 32; break;
|
||||
case 3: width = 64; height = 64; break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// Horizontal Rectangle
|
||||
switch ((attr1 >> 14) & 3)
|
||||
{
|
||||
case 0: width = 16; height = 8; break;
|
||||
case 1: width = 32; height = 8; break;
|
||||
case 2: width = 32; height = 16; break;
|
||||
case 3: width = 64; height = 32; break;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// Vertical Rectangle
|
||||
switch ((attr1 >> 14) & 3)
|
||||
{
|
||||
case 0: width = 8; height = 16; break;
|
||||
case 1: width = 8; height = 32; break;
|
||||
case 2: width = 16; height = 32; break;
|
||||
case 3: width = 32; height = 64; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Check double size flag here
|
||||
|
||||
int rwidth = width, rheight = height;
|
||||
if ((attr0 & (1 << 8)) != 0)
|
||||
{
|
||||
// Rot-scale on
|
||||
if ((attr0 & (1 << 9)) != 0)
|
||||
{
|
||||
rwidth *= 2;
|
||||
rheight *= 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid sprite
|
||||
if ((attr0 & (1 << 9)) != 0)
|
||||
width = -1;
|
||||
}
|
||||
|
||||
if (width == -1)
|
||||
{
|
||||
// Invalid sprite
|
||||
continue;
|
||||
}
|
||||
|
||||
// Y clipping
|
||||
if (y > ((y + rheight) & 0xff))
|
||||
{
|
||||
if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue;
|
||||
}
|
||||
|
||||
int scale = 1;
|
||||
if ((attr0 & (1 << 13)) != 0) scale = 2;
|
||||
|
||||
int spritey = this.curLine - y;
|
||||
if (spritey < 0) spritey += 256;
|
||||
|
||||
if ((attr0 & (1 << 8)) == 0)
|
||||
{
|
||||
if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey;
|
||||
|
||||
int baseSprite;
|
||||
if ((this.dispCnt & (1 << 6)) != 0)
|
||||
{
|
||||
// 1 dimensional
|
||||
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 2 dimensional
|
||||
baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20);
|
||||
}
|
||||
|
||||
int baseInc = scale;
|
||||
if ((attr1 & (1 << 12)) != 0)
|
||||
{
|
||||
baseSprite += ((width / 8) * scale) - scale;
|
||||
baseInc = -baseInc;
|
||||
}
|
||||
|
||||
if ((attr0 & (1 << 13)) != 0)
|
||||
{
|
||||
// 256 colors
|
||||
for (int i = x; i < x + width; i++)
|
||||
{
|
||||
if ((i & 0x1ff) < 240)
|
||||
{
|
||||
int tx = (i - x) & 7;
|
||||
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
||||
int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx;
|
||||
int lookup = vram[0x10000 + curIdx];
|
||||
if (lookup != 0)
|
||||
{
|
||||
this.windowCover[i & 0x1ff] = this.winObjEnabled;
|
||||
}
|
||||
}
|
||||
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 16 colors
|
||||
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
||||
for (int i = x; i < x + width; i++)
|
||||
{
|
||||
if ((i & 0x1ff) < 240)
|
||||
{
|
||||
int tx = (i - x) & 7;
|
||||
if ((attr1 & (1 << 12)) != 0) tx = 7 - tx;
|
||||
int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2);
|
||||
int lookup = vram[0x10000 + curIdx];
|
||||
if ((tx & 1) == 0)
|
||||
{
|
||||
lookup &= 0xf;
|
||||
}
|
||||
else
|
||||
{
|
||||
lookup >>= 4;
|
||||
}
|
||||
if (lookup != 0)
|
||||
{
|
||||
this.windowCover[i & 0x1ff] = this.winObjEnabled;
|
||||
}
|
||||
}
|
||||
if (((i - x) & 7) == 7) baseSprite += baseInc;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int rotScaleParam = (attr1 >> 9) & 0x1F;
|
||||
|
||||
short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6);
|
||||
short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE);
|
||||
short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16);
|
||||
short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E);
|
||||
|
||||
int cx = rwidth / 2;
|
||||
int cy = rheight / 2;
|
||||
|
||||
int baseSprite = attr2 & 0x3FF;
|
||||
int pitch;
|
||||
|
||||
if ((this.dispCnt & (1 << 6)) != 0)
|
||||
{
|
||||
// 1 dimensional
|
||||
pitch = (width / 8) * scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 2 dimensional
|
||||
pitch = 0x20;
|
||||
}
|
||||
|
||||
short rx = (short)((dmx * (spritey - cy)) - (cx * dx) + (width << 7));
|
||||
short ry = (short)((dmy * (spritey - cy)) - (cx * dy) + (height << 7));
|
||||
|
||||
// Draw a rot/scale sprite
|
||||
if ((attr0 & (1 << 13)) != 0)
|
||||
{
|
||||
// 256 colors
|
||||
for (int i = x; i < x + rwidth; i++)
|
||||
{
|
||||
int tx = rx >> 8;
|
||||
int ty = ry >> 8;
|
||||
|
||||
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height)
|
||||
{
|
||||
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7);
|
||||
int lookup = vram[0x10000 + curIdx];
|
||||
if (lookup != 0)
|
||||
{
|
||||
this.windowCover[i & 0x1ff] = this.winObjEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
rx += dx;
|
||||
ry += dy;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 16 colors
|
||||
int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2);
|
||||
for (int i = x; i < x + rwidth; i++)
|
||||
{
|
||||
int tx = rx >> 8;
|
||||
int ty = ry >> 8;
|
||||
|
||||
if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height)
|
||||
{
|
||||
int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2);
|
||||
int lookup = vram[0x10000 + curIdx];
|
||||
if ((tx & 1) == 0)
|
||||
{
|
||||
lookup &= 0xf;
|
||||
}
|
||||
else
|
||||
{
|
||||
lookup >>= 4;
|
||||
}
|
||||
if (lookup != 0)
|
||||
{
|
||||
this.windowCover[i & 0x1ff] = this.winObjEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
rx += dx;
|
||||
ry += dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static uint GbaTo32(ushort color)
|
||||
{
|
||||
// more accurate, but slower :(
|
||||
// return colorLUT[color];
|
||||
return ((color & 0x1FU) << 19) | ((color & 0x3E0U) << 6) | ((color & 0x7C00U) >> 7);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,191 @@
|
|||
namespace GarboDev
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
public class SoundManager
|
||||
{
|
||||
private Memory memory = null;
|
||||
private Queue<byte>[] soundQueue = new Queue<byte>[2];
|
||||
private byte latchedA, latchedB;
|
||||
private int frequency, cyclesPerSample;
|
||||
private int leftover = 0;
|
||||
|
||||
private short[] soundBuffer = new short[40000];
|
||||
private int soundBufferPos = 0;
|
||||
private int lastSoundBufferPos = 0;
|
||||
|
||||
public SoundManager(Memory memory, int frequency)
|
||||
{
|
||||
this.Frequency = frequency;
|
||||
|
||||
this.memory = memory;
|
||||
this.memory.SoundManager = this;
|
||||
|
||||
this.soundQueue[0] = new Queue<byte>(32);
|
||||
this.soundQueue[1] = new Queue<byte>(32);
|
||||
}
|
||||
|
||||
#region Public Properties
|
||||
public int Frequency
|
||||
{
|
||||
get { return this.frequency; }
|
||||
set
|
||||
{
|
||||
this.frequency = value;
|
||||
this.cyclesPerSample = (GbaManager.cpuFreq << 5) / this.frequency;
|
||||
}
|
||||
}
|
||||
|
||||
public int QueueSizeA
|
||||
{
|
||||
get { return this.soundQueue[0].Count; }
|
||||
}
|
||||
|
||||
public int QueueSizeB
|
||||
{
|
||||
get { return this.soundQueue[1].Count; }
|
||||
}
|
||||
|
||||
public int SamplesMixed
|
||||
{
|
||||
get
|
||||
{
|
||||
int value = this.soundBufferPos - this.lastSoundBufferPos;
|
||||
if (value < 0) value += this.soundBuffer.Length;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
public void GetSamples(short[] buffer, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (this.lastSoundBufferPos == this.soundBuffer.Length)
|
||||
{
|
||||
this.lastSoundBufferPos = 0;
|
||||
}
|
||||
buffer[i] = this.soundBuffer[this.lastSoundBufferPos++];
|
||||
}
|
||||
}
|
||||
|
||||
public void Mix(int cycles)
|
||||
{
|
||||
ushort soundCntH = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_H);
|
||||
ushort soundCntX = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_X);
|
||||
|
||||
cycles <<= 5;
|
||||
cycles += this.leftover;
|
||||
|
||||
if (cycles > 0)
|
||||
{
|
||||
// Precompute loop invariants
|
||||
short directA = (short)(sbyte)(this.latchedA);
|
||||
short directB = (short)(sbyte)(this.latchedB);
|
||||
|
||||
if ((soundCntH & (1 << 2)) == 0)
|
||||
{
|
||||
directA >>= 1;
|
||||
}
|
||||
if ((soundCntH & (1 << 3)) == 0)
|
||||
{
|
||||
directB >>= 1;
|
||||
}
|
||||
|
||||
while (cycles > 0)
|
||||
{
|
||||
short l = 0, r = 0;
|
||||
|
||||
cycles -= this.cyclesPerSample;
|
||||
|
||||
// Mixing
|
||||
if ((soundCntX & (1 << 7)) != 0)
|
||||
{
|
||||
if ((soundCntH & (1 << 8)) != 0)
|
||||
{
|
||||
r += directA;
|
||||
}
|
||||
if ((soundCntH & (1 << 9)) != 0)
|
||||
{
|
||||
l += directA;
|
||||
}
|
||||
if ((soundCntH & (1 << 12)) != 0)
|
||||
{
|
||||
r += directB;
|
||||
}
|
||||
if ((soundCntH & (1 << 13)) != 0)
|
||||
{
|
||||
l += directB;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.soundBufferPos == this.soundBuffer.Length)
|
||||
{
|
||||
this.soundBufferPos = 0;
|
||||
}
|
||||
|
||||
this.soundBuffer[this.soundBufferPos++] = (short)(l << 6);
|
||||
this.soundBuffer[this.soundBufferPos++] = (short)(r << 6);
|
||||
}
|
||||
}
|
||||
|
||||
this.leftover = cycles;
|
||||
}
|
||||
|
||||
public void ResetFifoA()
|
||||
{
|
||||
this.soundQueue[0].Clear();
|
||||
this.latchedA = 0;
|
||||
}
|
||||
|
||||
public void ResetFifoB()
|
||||
{
|
||||
this.soundQueue[1].Clear();
|
||||
this.latchedB = 0;
|
||||
}
|
||||
|
||||
public void IncrementFifoA()
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
this.EnqueueDSoundSample(0, this.memory.IORam[Memory.FIFO_A_L + i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void IncrementFifoB()
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
this.EnqueueDSoundSample(1, this.memory.IORam[Memory.FIFO_B_L + i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void DequeueA()
|
||||
{
|
||||
if (this.soundQueue[0].Count > 0)
|
||||
{
|
||||
this.latchedA = this.soundQueue[0].Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
public void DequeueB()
|
||||
{
|
||||
if (this.soundQueue[1].Count > 0)
|
||||
{
|
||||
this.latchedB = this.soundQueue[1].Dequeue();
|
||||
}
|
||||
}
|
||||
#endregion Public Methods
|
||||
|
||||
private void EnqueueDSoundSample(int channel, byte sample)
|
||||
{
|
||||
if (this.soundQueue[channel].Count < 32)
|
||||
{
|
||||
this.soundQueue[channel].Enqueue(sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,981 @@
|
|||
//#define ARM_DEBUG
|
||||
|
||||
namespace GarboDev
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
public class ThumbCore
|
||||
{
|
||||
private const int COND_EQ = 0; // Z set
|
||||
private const int COND_NE = 1; // Z clear
|
||||
private const int COND_CS = 2; // C set
|
||||
private const int COND_CC = 3; // C clear
|
||||
private const int COND_MI = 4; // N set
|
||||
private const int COND_PL = 5; // N clear
|
||||
private const int COND_VS = 6; // V set
|
||||
private const int COND_VC = 7; // V clear
|
||||
private const int COND_HI = 8; // C set and Z clear
|
||||
private const int COND_LS = 9; // C clear or Z set
|
||||
private const int COND_GE = 10; // N equals V
|
||||
private const int COND_LT = 11; // N not equal to V
|
||||
private const int COND_GT = 12; // Z clear AND (N equals V)
|
||||
private const int COND_LE = 13; // Z set OR (N not equal to V)
|
||||
private const int COND_AL = 14; // Always
|
||||
private const int COND_NV = 15; // Never execute
|
||||
|
||||
private const int OP_AND = 0x0;
|
||||
private const int OP_EOR = 0x1;
|
||||
private const int OP_LSL = 0x2;
|
||||
private const int OP_LSR = 0x3;
|
||||
private const int OP_ASR = 0x4;
|
||||
private const int OP_ADC = 0x5;
|
||||
private const int OP_SBC = 0x6;
|
||||
private const int OP_ROR = 0x7;
|
||||
private const int OP_TST = 0x8;
|
||||
private const int OP_NEG = 0x9;
|
||||
private const int OP_CMP = 0xA;
|
||||
private const int OP_CMN = 0xB;
|
||||
private const int OP_ORR = 0xC;
|
||||
private const int OP_MUL = 0xD;
|
||||
private const int OP_BIC = 0xE;
|
||||
private const int OP_MVN = 0xF;
|
||||
|
||||
private Arm7Processor parent;
|
||||
private Memory memory;
|
||||
private uint[] registers;
|
||||
|
||||
// CPU flags
|
||||
private uint zero, carry, negative, overflow;
|
||||
private ushort curInstruction, instructionQueue;
|
||||
|
||||
private delegate void ExecuteInstruction();
|
||||
private ExecuteInstruction[] NormalOps = null;
|
||||
|
||||
public ThumbCore(Arm7Processor parent, Memory memory)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.memory = memory;
|
||||
this.registers = this.parent.Registers;
|
||||
|
||||
this.NormalOps = new ExecuteInstruction[256]
|
||||
{
|
||||
OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm,
|
||||
OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm,
|
||||
OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm,
|
||||
OpAddRegReg, OpAddRegReg, OpSubRegReg, OpSubRegReg, OpAddRegImm, OpAddRegImm, OpSubRegImm, OpSubRegImm,
|
||||
OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm,
|
||||
OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm,
|
||||
OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm,
|
||||
OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm,
|
||||
OpArith, OpArith, OpArith, OpArith, OpAddHi, OpCmpHi, OpMovHi, OpBx,
|
||||
OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc,
|
||||
OpStrReg, OpStrReg, OpStrhReg, OpStrhReg, OpStrbReg, OpStrbReg, OpLdrsbReg, OpLdrsbReg,
|
||||
OpLdrReg, OpLdrReg, OpLdrhReg, OpLdrhReg, OpLdrbReg, OpLdrbReg, OpLdrshReg, OpLdrshReg,
|
||||
OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm,
|
||||
OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm,
|
||||
OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm,
|
||||
OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm,
|
||||
OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm,
|
||||
OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm,
|
||||
OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp,
|
||||
OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp,
|
||||
OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc,
|
||||
OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp,
|
||||
OpSubSp, OpUnd, OpUnd, OpUnd, OpPush, OpPushLr, OpUnd, OpUnd,
|
||||
OpUnd, OpUnd, OpUnd, OpUnd, OpPop, OpPopPc, OpUnd, OpUnd,
|
||||
OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia,
|
||||
OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia,
|
||||
OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond,
|
||||
OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpUnd, OpSwi,
|
||||
OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB,
|
||||
OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd,
|
||||
OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1,
|
||||
OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2
|
||||
};
|
||||
}
|
||||
|
||||
public void BeginExecution()
|
||||
{
|
||||
this.FlushQueue();
|
||||
}
|
||||
|
||||
public void Step()
|
||||
{
|
||||
this.UnpackFlags();
|
||||
|
||||
this.curInstruction = this.instructionQueue;
|
||||
this.instructionQueue = this.memory.ReadU16(registers[15]);
|
||||
registers[15] += 2;
|
||||
|
||||
// Execute the instruction
|
||||
this.NormalOps[this.curInstruction >> 8]();
|
||||
|
||||
this.parent.Cycles -= this.memory.WaitCycles;
|
||||
|
||||
if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK)
|
||||
{
|
||||
if ((this.curInstruction >> 8) != 0xDF) this.parent.ReloadQueue();
|
||||
}
|
||||
|
||||
this.PackFlags();
|
||||
}
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
this.UnpackFlags();
|
||||
|
||||
while (this.parent.Cycles > 0)
|
||||
{
|
||||
this.curInstruction = this.instructionQueue;
|
||||
this.instructionQueue = this.memory.ReadU16(registers[15]);
|
||||
registers[15] += 2;
|
||||
|
||||
// Execute the instruction
|
||||
this.NormalOps[this.curInstruction >> 8]();
|
||||
|
||||
this.parent.Cycles -= this.memory.WaitCycles;
|
||||
|
||||
if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK)
|
||||
{
|
||||
if ((this.curInstruction >> 8) != 0xDF) this.parent.ReloadQueue();
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the current PC
|
||||
#if ARM_DEBUG
|
||||
if (this.parent.Breakpoints.ContainsKey(registers[15] - 2U))
|
||||
{
|
||||
this.parent.BreakpointHit = true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
this.PackFlags();
|
||||
}
|
||||
|
||||
#region Flag helpers
|
||||
public void OverflowCarryAdd(uint a, uint b, uint r)
|
||||
{
|
||||
overflow = ((a & b & ~r) | (~a & ~b & r)) >> 31;
|
||||
carry = ((a & b) | (a & ~r) | (b & ~r)) >> 31;
|
||||
}
|
||||
|
||||
public void OverflowCarrySub(uint a, uint b, uint r)
|
||||
{
|
||||
overflow = ((a & ~b & ~r) | (~a & b & r)) >> 31;
|
||||
carry = ((a & ~b) | (a & ~r) | (~b & ~r)) >> 31;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Opcodes
|
||||
private void OpLslImm()
|
||||
{
|
||||
// 0x00 - 0x07
|
||||
// lsl rd, rm, #immed
|
||||
int rd = this.curInstruction & 0x7;
|
||||
int rm = (this.curInstruction >> 3) & 0x7;
|
||||
int immed = (this.curInstruction >> 6) & 0x1F;
|
||||
|
||||
if (immed == 0)
|
||||
{
|
||||
registers[rd] = registers[rm];
|
||||
} else
|
||||
{
|
||||
carry = (registers[rm] >> (32 - immed)) & 0x1;
|
||||
registers[rd] = registers[rm] << immed;
|
||||
}
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpLsrImm()
|
||||
{
|
||||
// 0x08 - 0x0F
|
||||
// lsr rd, rm, #immed
|
||||
int rd = this.curInstruction & 0x7;
|
||||
int rm = (this.curInstruction >> 3) & 0x7;
|
||||
int immed = (this.curInstruction >> 6) & 0x1F;
|
||||
|
||||
if (immed == 0)
|
||||
{
|
||||
carry = registers[rm] >> 31;
|
||||
registers[rd] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
carry = (registers[rm] >> (immed - 1)) & 0x1;
|
||||
registers[rd] = registers[rm] >> immed;
|
||||
}
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpAsrImm()
|
||||
{
|
||||
// asr rd, rm, #immed
|
||||
int rd = this.curInstruction & 0x7;
|
||||
int rm = (this.curInstruction >> 3) & 0x7;
|
||||
int immed = (this.curInstruction >> 6) & 0x1F;
|
||||
|
||||
if (immed == 0)
|
||||
{
|
||||
carry = registers[rm] >> 31;
|
||||
if (carry == 1) registers[rd] = 0xFFFFFFFF;
|
||||
else registers[rd] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
carry = (registers[rm] >> (immed - 1)) & 0x1;
|
||||
registers[rd] = (uint)(((int)registers[rm]) >> immed);
|
||||
}
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpAddRegReg()
|
||||
{
|
||||
// add rd, rn, rm
|
||||
int rd = this.curInstruction & 0x7;
|
||||
int rn = (this.curInstruction >> 3) & 0x7;
|
||||
int rm = (this.curInstruction >> 6) & 0x7;
|
||||
|
||||
uint orn = registers[rn];
|
||||
uint orm = registers[rm];
|
||||
|
||||
registers[rd] = orn + orm;
|
||||
|
||||
this.OverflowCarryAdd(orn, orm, registers[rd]);
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpSubRegReg()
|
||||
{
|
||||
// sub rd, rn, rm
|
||||
int rd = this.curInstruction & 0x7;
|
||||
int rn = (this.curInstruction >> 3) & 0x7;
|
||||
int rm = (this.curInstruction >> 6) & 0x7;
|
||||
|
||||
uint orn = registers[rn];
|
||||
uint orm = registers[rm];
|
||||
|
||||
registers[rd] = orn - orm;
|
||||
|
||||
this.OverflowCarrySub(orn, orm, registers[rd]);
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpAddRegImm()
|
||||
{
|
||||
// add rd, rn, #immed
|
||||
int rd = this.curInstruction & 0x7;
|
||||
int rn = (this.curInstruction >> 3) & 0x7;
|
||||
uint immed = (uint)((this.curInstruction >> 6) & 0x7);
|
||||
|
||||
uint orn = registers[rn];
|
||||
|
||||
registers[rd] = orn + immed;
|
||||
|
||||
this.OverflowCarryAdd(orn, immed, registers[rd]);
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpSubRegImm()
|
||||
{
|
||||
// sub rd, rn, #immed
|
||||
int rd = this.curInstruction & 0x7;
|
||||
int rn = (this.curInstruction >> 3) & 0x7;
|
||||
uint immed = (uint)((this.curInstruction >> 6) & 0x7);
|
||||
|
||||
uint orn = registers[rn];
|
||||
|
||||
registers[rd] = orn - immed;
|
||||
|
||||
this.OverflowCarrySub(orn, immed, registers[rd]);
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpMovImm()
|
||||
{
|
||||
// mov rd, #immed
|
||||
int rd = (this.curInstruction >> 8) & 0x7;
|
||||
|
||||
registers[rd] = (uint)(this.curInstruction & 0xFF);
|
||||
|
||||
negative = 0;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpCmpImm()
|
||||
{
|
||||
// cmp rn, #immed
|
||||
int rn = (this.curInstruction >> 8) & 0x7;
|
||||
|
||||
uint alu = registers[rn] - (uint)(this.curInstruction & 0xFF);
|
||||
|
||||
this.OverflowCarrySub(registers[rn], (uint)(this.curInstruction & 0xFF), alu);
|
||||
negative = alu >> 31;
|
||||
zero = alu == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpAddImm()
|
||||
{
|
||||
// add rd, #immed
|
||||
int rd = (this.curInstruction >> 8) & 0x7;
|
||||
|
||||
uint ord = registers[rd];
|
||||
|
||||
registers[rd] += (uint)(this.curInstruction & 0xFF);
|
||||
|
||||
this.OverflowCarryAdd(ord, (uint)(this.curInstruction & 0xFF), registers[rd]);
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpSubImm()
|
||||
{
|
||||
// sub rd, #immed
|
||||
int rd = (this.curInstruction >> 8) & 0x7;
|
||||
|
||||
uint ord = registers[rd];
|
||||
|
||||
registers[rd] -= (uint)(this.curInstruction & 0xFF);
|
||||
|
||||
this.OverflowCarrySub(ord, (uint)(this.curInstruction & 0xFF), registers[rd]);
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
}
|
||||
|
||||
private void OpArith()
|
||||
{
|
||||
int rd = this.curInstruction & 0x7;
|
||||
uint rn = registers[(this.curInstruction >> 3) & 0x7];
|
||||
|
||||
uint orig, alu;
|
||||
int shiftAmt;
|
||||
|
||||
switch ((this.curInstruction >> 6) & 0xF)
|
||||
{
|
||||
case OP_ADC:
|
||||
orig = registers[rd];
|
||||
registers[rd] += rn + carry;
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
this.OverflowCarryAdd(orig, rn, registers[rd]);
|
||||
break;
|
||||
|
||||
case OP_AND:
|
||||
registers[rd] &= rn;
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_ASR:
|
||||
shiftAmt = (int)(rn & 0xFF);
|
||||
if (shiftAmt == 0)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
else if (shiftAmt < 32)
|
||||
{
|
||||
carry = (registers[rd] >> (shiftAmt - 1)) & 0x1;
|
||||
registers[rd] = (uint)(((int)registers[rd]) >> shiftAmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
carry = (registers[rd] >> 31) & 1;
|
||||
if (carry == 1) registers[rd] = 0xFFFFFFFF;
|
||||
else registers[rd] = 0;
|
||||
}
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_BIC:
|
||||
registers[rd] &= ~rn;
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_CMN:
|
||||
alu = registers[rd] + rn;
|
||||
|
||||
negative = alu >> 31;
|
||||
zero = alu == 0 ? 1U : 0U;
|
||||
this.OverflowCarryAdd(registers[rd], rn, alu);
|
||||
break;
|
||||
|
||||
case OP_CMP:
|
||||
alu = registers[rd] - rn;
|
||||
|
||||
negative = alu >> 31;
|
||||
zero = alu == 0 ? 1U : 0U;
|
||||
this.OverflowCarrySub(registers[rd], rn, alu);
|
||||
break;
|
||||
|
||||
case OP_EOR:
|
||||
registers[rd] ^= rn;
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_LSL:
|
||||
shiftAmt = (int)(rn & 0xFF);
|
||||
if (shiftAmt == 0)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
else if (shiftAmt < 32)
|
||||
{
|
||||
carry = (registers[rd] >> (32 - shiftAmt)) & 0x1;
|
||||
registers[rd] <<= shiftAmt;
|
||||
}
|
||||
else if (shiftAmt == 32)
|
||||
{
|
||||
carry = registers[rd] & 0x1;
|
||||
registers[rd] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
carry = 0;
|
||||
registers[rd] = 0;
|
||||
}
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_LSR:
|
||||
shiftAmt = (int)(rn & 0xFF);
|
||||
if (shiftAmt == 0)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
else if (shiftAmt < 32)
|
||||
{
|
||||
carry = (registers[rd] >> (shiftAmt - 1)) & 0x1;
|
||||
registers[rd] >>= shiftAmt;
|
||||
}
|
||||
else if (shiftAmt == 32)
|
||||
{
|
||||
carry = (registers[rd] >> 31) & 0x1;
|
||||
registers[rd] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
carry = 0;
|
||||
registers[rd] = 0;
|
||||
}
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_MUL:
|
||||
int mulCycles = 4;
|
||||
// Multiply cycle calculations
|
||||
if ((rn & 0xFFFFFF00) == 0 || (rn & 0xFFFFFF00) == 0xFFFFFF00)
|
||||
{
|
||||
mulCycles = 1;
|
||||
}
|
||||
else if ((rn & 0xFFFF0000) == 0 || (rn & 0xFFFF0000) == 0xFFFF0000)
|
||||
{
|
||||
mulCycles = 2;
|
||||
}
|
||||
else if ((rn & 0xFF000000) == 0 || (rn & 0xFF000000) == 0xFF000000)
|
||||
{
|
||||
mulCycles = 3;
|
||||
}
|
||||
|
||||
this.parent.Cycles -= mulCycles;
|
||||
|
||||
registers[rd] *= rn;
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_MVN:
|
||||
registers[rd] = ~rn;
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_NEG:
|
||||
registers[rd] = 0 - rn;
|
||||
|
||||
this.OverflowCarrySub(0, rn, registers[rd]);
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_ORR:
|
||||
registers[rd] |= rn;
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_ROR:
|
||||
shiftAmt = (int)(rn & 0xFF);
|
||||
if (shiftAmt == 0)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
else if ((shiftAmt & 0x1F) == 0)
|
||||
{
|
||||
carry = registers[rd] >> 31;
|
||||
}
|
||||
else
|
||||
{
|
||||
shiftAmt &= 0x1F;
|
||||
carry = (registers[rd] >> (shiftAmt - 1)) & 0x1;
|
||||
registers[rd] = (registers[rd] >> shiftAmt) | (registers[rd] << (32 - shiftAmt));
|
||||
}
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
case OP_SBC:
|
||||
orig = registers[rd];
|
||||
registers[rd] = (registers[rd] - rn) - (1U - carry);
|
||||
|
||||
negative = registers[rd] >> 31;
|
||||
zero = registers[rd] == 0 ? 1U : 0U;
|
||||
this.OverflowCarrySub(orig, rn, registers[rd]);
|
||||
break;
|
||||
|
||||
case OP_TST:
|
||||
alu = registers[rd] & rn;
|
||||
|
||||
negative = alu >> 31;
|
||||
zero = alu == 0 ? 1U : 0U;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception("The coder screwed up on the thumb alu op...");
|
||||
}
|
||||
}
|
||||
|
||||
private void OpAddHi()
|
||||
{
|
||||
int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7);
|
||||
int rm = (this.curInstruction >> 3) & 0xF;
|
||||
|
||||
registers[rd] += registers[rm];
|
||||
|
||||
if (rd == 15)
|
||||
{
|
||||
registers[rd] &= ~1U;
|
||||
this.FlushQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private void OpCmpHi()
|
||||
{
|
||||
int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7);
|
||||
int rm = (this.curInstruction >> 3) & 0xF;
|
||||
|
||||
uint alu = registers[rd] - registers[rm];
|
||||
|
||||
negative = alu >> 31;
|
||||
zero = alu == 0 ? 1U : 0U;
|
||||
this.OverflowCarrySub(registers[rd], registers[rm], alu);
|
||||
}
|
||||
|
||||
private void OpMovHi()
|
||||
{
|
||||
int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7);
|
||||
int rm = (this.curInstruction >> 3) & 0xF;
|
||||
|
||||
registers[rd] = registers[rm];
|
||||
|
||||
if (rd == 15)
|
||||
{
|
||||
registers[rd] &= ~1U;
|
||||
this.FlushQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private void OpBx()
|
||||
{
|
||||
int rm = (this.curInstruction >> 3) & 0xf;
|
||||
|
||||
this.PackFlags();
|
||||
|
||||
this.parent.CPSR &= ~Arm7Processor.T_MASK;
|
||||
this.parent.CPSR |= (registers[rm] & 1) << Arm7Processor.T_BIT;
|
||||
|
||||
registers[15] = registers[rm] & (~1U);
|
||||
|
||||
this.UnpackFlags();
|
||||
|
||||
// Check for branch back to Arm Mode
|
||||
if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.FlushQueue();
|
||||
}
|
||||
|
||||
private void OpLdrPc()
|
||||
{
|
||||
int rd = (this.curInstruction >> 8) & 0x7;
|
||||
|
||||
registers[rd] = this.memory.ReadU32((registers[15] & ~2U) + (uint)((this.curInstruction & 0xFF) * 4));
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpStrReg()
|
||||
{
|
||||
this.memory.WriteU32(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7],
|
||||
registers[this.curInstruction & 0x7]);
|
||||
}
|
||||
|
||||
private void OpStrhReg()
|
||||
{
|
||||
this.memory.WriteU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7],
|
||||
(ushort)(registers[this.curInstruction & 0x7] & 0xFFFF));
|
||||
}
|
||||
|
||||
private void OpStrbReg()
|
||||
{
|
||||
this.memory.WriteU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7],
|
||||
(byte)(registers[this.curInstruction & 0x7] & 0xFF));
|
||||
}
|
||||
|
||||
private void OpLdrsbReg()
|
||||
{
|
||||
registers[this.curInstruction & 0x7] =
|
||||
this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
|
||||
|
||||
if ((registers[this.curInstruction & 0x7] & (1 << 7)) != 0)
|
||||
{
|
||||
registers[this.curInstruction & 0x7] |= 0xFFFFFF00;
|
||||
}
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpLdrReg()
|
||||
{
|
||||
registers[this.curInstruction & 0x7] =
|
||||
this.memory.ReadU32(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpLdrhReg()
|
||||
{
|
||||
registers[this.curInstruction & 0x7] =
|
||||
this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpLdrbReg()
|
||||
{
|
||||
registers[this.curInstruction & 0x7] =
|
||||
this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpLdrshReg()
|
||||
{
|
||||
registers[this.curInstruction & 0x7] =
|
||||
this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]);
|
||||
|
||||
if ((registers[this.curInstruction & 0x7] & (1 << 15)) != 0)
|
||||
{
|
||||
registers[this.curInstruction & 0x7] |= 0xFFFF0000;
|
||||
}
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpStrImm()
|
||||
{
|
||||
this.memory.WriteU32(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 4),
|
||||
registers[this.curInstruction & 0x7]);
|
||||
}
|
||||
|
||||
private void OpLdrImm()
|
||||
{
|
||||
registers[this.curInstruction & 0x7] =
|
||||
this.memory.ReadU32(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 4));
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpStrbImm()
|
||||
{
|
||||
this.memory.WriteU8(registers[(this.curInstruction >> 3) & 0x7] + (uint)((this.curInstruction >> 6) & 0x1F),
|
||||
(byte)(registers[this.curInstruction & 0x7] & 0xFF));
|
||||
}
|
||||
|
||||
private void OpLdrbImm()
|
||||
{
|
||||
registers[this.curInstruction & 0x7] =
|
||||
this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + (uint)((this.curInstruction >> 6) & 0x1F));
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpStrhImm()
|
||||
{
|
||||
this.memory.WriteU16(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 2),
|
||||
(ushort)(registers[this.curInstruction & 0x7] & 0xFFFF));
|
||||
}
|
||||
|
||||
private void OpLdrhImm()
|
||||
{
|
||||
registers[this.curInstruction & 0x7] =
|
||||
this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 2));
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpStrSp()
|
||||
{
|
||||
this.memory.WriteU32(registers[13] + (uint)((this.curInstruction & 0xFF) * 4),
|
||||
registers[(this.curInstruction >> 8) & 0x7]);
|
||||
}
|
||||
|
||||
private void OpLdrSp()
|
||||
{
|
||||
registers[(this.curInstruction >> 8) & 0x7] =
|
||||
this.memory.ReadU32(registers[13] + (uint)((this.curInstruction & 0xFF) * 4));
|
||||
}
|
||||
|
||||
private void OpAddPc()
|
||||
{
|
||||
registers[(this.curInstruction >> 8) & 0x7] =
|
||||
(registers[15] & ~2U) + (uint)((this.curInstruction & 0xFF) * 4);
|
||||
}
|
||||
|
||||
private void OpAddSp()
|
||||
{
|
||||
registers[(this.curInstruction >> 8) & 0x7] =
|
||||
registers[13] + (uint)((this.curInstruction & 0xFF) * 4);
|
||||
}
|
||||
|
||||
private void OpSubSp()
|
||||
{
|
||||
if ((this.curInstruction & (1 << 7)) != 0)
|
||||
registers[13] -= (uint)((this.curInstruction & 0x7F) * 4);
|
||||
else
|
||||
registers[13] += (uint)((this.curInstruction & 0x7F) * 4);
|
||||
}
|
||||
|
||||
private void OpPush()
|
||||
{
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if (((this.curInstruction >> i) & 1) != 0)
|
||||
{
|
||||
registers[13] -= 4;
|
||||
this.memory.WriteU32(registers[13], registers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpPushLr()
|
||||
{
|
||||
registers[13] -= 4;
|
||||
this.memory.WriteU32(registers[13], registers[14]);
|
||||
|
||||
for (int i = 7; i >= 0; i--)
|
||||
{
|
||||
if (((this.curInstruction >> i) & 1) != 0)
|
||||
{
|
||||
registers[13] -= 4;
|
||||
this.memory.WriteU32(registers[13], registers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpPop()
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (((this.curInstruction >> i) & 1) != 0)
|
||||
{
|
||||
registers[i] = this.memory.ReadU32(registers[13]);
|
||||
registers[13] += 4;
|
||||
}
|
||||
}
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpPopPc()
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (((this.curInstruction >> i) & 1) != 0)
|
||||
{
|
||||
registers[i] = this.memory.ReadU32(registers[13]);
|
||||
registers[13] += 4;
|
||||
}
|
||||
}
|
||||
|
||||
registers[15] = this.memory.ReadU32(registers[13]) & (~1U);
|
||||
registers[13] += 4;
|
||||
|
||||
// ARM9 check here
|
||||
|
||||
this.FlushQueue();
|
||||
|
||||
this.parent.Cycles--;
|
||||
}
|
||||
|
||||
private void OpStmia()
|
||||
{
|
||||
int rn = (this.curInstruction >> 8) & 0x7;
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (((this.curInstruction >> i) & 1) != 0)
|
||||
{
|
||||
this.memory.WriteU32(registers[rn] & (~3U), registers[i]);
|
||||
registers[rn] += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpLdmia()
|
||||
{
|
||||
int rn = (this.curInstruction >> 8) & 0x7;
|
||||
|
||||
uint address = registers[rn];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (((this.curInstruction >> i) & 1) != 0)
|
||||
{
|
||||
registers[i] = this.memory.ReadU32Aligned(address & (~3U));
|
||||
address += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (((this.curInstruction >> rn) & 1) == 0)
|
||||
{
|
||||
registers[rn] = address;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpBCond()
|
||||
{
|
||||
uint cond = 0;
|
||||
switch ((this.curInstruction >> 8) & 0xF)
|
||||
{
|
||||
case COND_AL: cond = 1; break;
|
||||
case COND_EQ: cond = zero; break;
|
||||
case COND_NE: cond = 1 - zero; break;
|
||||
case COND_CS: cond = carry; break;
|
||||
case COND_CC: cond = 1 - carry; break;
|
||||
case COND_MI: cond = negative; break;
|
||||
case COND_PL: cond = 1 - negative; break;
|
||||
case COND_VS: cond = overflow; break;
|
||||
case COND_VC: cond = 1 - overflow; break;
|
||||
case COND_HI: cond = carry & (1 - zero); break;
|
||||
case COND_LS: cond = (1 - carry) | zero; break;
|
||||
case COND_GE: cond = (1 - negative) ^ overflow; break;
|
||||
case COND_LT: cond = negative ^ overflow; break;
|
||||
case COND_GT: cond = (1 - zero) & (negative ^ (1 - overflow)); break;
|
||||
case COND_LE: cond = (negative ^ overflow) | zero; break;
|
||||
}
|
||||
|
||||
if (cond == 1)
|
||||
{
|
||||
uint offset = (uint)(this.curInstruction & 0xFF);
|
||||
if ((offset & (1 << 7)) != 0) offset |= 0xFFFFFF00;
|
||||
|
||||
registers[15] += offset << 1;
|
||||
|
||||
this.FlushQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private void OpSwi()
|
||||
{
|
||||
registers[15] -= 4U;
|
||||
this.parent.EnterException(Arm7Processor.SVC, 0x8, false, false);
|
||||
}
|
||||
|
||||
private void OpB()
|
||||
{
|
||||
uint offset = (uint)(this.curInstruction & 0x7FF);
|
||||
if ((offset & (1 << 10)) != 0) offset |= 0xFFFFF800;
|
||||
|
||||
registers[15] += offset << 1;
|
||||
|
||||
this.FlushQueue();
|
||||
}
|
||||
|
||||
private void OpBl1()
|
||||
{
|
||||
uint offset = (uint)(this.curInstruction & 0x7FF);
|
||||
if ((offset & (1 << 10)) != 0) offset |= 0xFFFFF800;
|
||||
|
||||
registers[14] = registers[15] + (offset << 12);
|
||||
}
|
||||
|
||||
private void OpBl2()
|
||||
{
|
||||
uint tmp = registers[15];
|
||||
registers[15] = registers[14] + (uint)((this.curInstruction & 0x7FF) << 1);
|
||||
registers[14] = (tmp - 2U) | 1;
|
||||
|
||||
this.FlushQueue();
|
||||
}
|
||||
|
||||
private void OpUnd()
|
||||
{
|
||||
throw new Exception("Unknown opcode");
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void PackFlags()
|
||||
{
|
||||
this.parent.CPSR &= 0x0FFFFFFF;
|
||||
this.parent.CPSR |= this.negative << Arm7Processor.N_BIT;
|
||||
this.parent.CPSR |= this.zero << Arm7Processor.Z_BIT;
|
||||
this.parent.CPSR |= this.carry << Arm7Processor.C_BIT;
|
||||
this.parent.CPSR |= this.overflow << Arm7Processor.V_BIT;
|
||||
}
|
||||
|
||||
private void UnpackFlags()
|
||||
{
|
||||
this.negative = (this.parent.CPSR >> Arm7Processor.N_BIT) & 1;
|
||||
this.zero = (this.parent.CPSR >> Arm7Processor.Z_BIT) & 1;
|
||||
this.carry = (this.parent.CPSR >> Arm7Processor.C_BIT) & 1;
|
||||
this.overflow = (this.parent.CPSR >> Arm7Processor.V_BIT) & 1;
|
||||
}
|
||||
|
||||
private void FlushQueue()
|
||||
{
|
||||
this.instructionQueue = this.memory.ReadU16(registers[15]);
|
||||
registers[15] += 2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
namespace GarboDev
|
||||
{
|
||||
public class VideoManager
|
||||
{
|
||||
public delegate void OnPresent(uint[] data);
|
||||
|
||||
private Memory memory = null;
|
||||
private IRenderer renderer = null;
|
||||
private OnPresent presenter;
|
||||
private GbaManager gbaManager;
|
||||
private int curLine;
|
||||
|
||||
public Memory Memory
|
||||
{
|
||||
set{ this.memory = value; }
|
||||
}
|
||||
|
||||
public IRenderer Renderer
|
||||
{
|
||||
set
|
||||
{
|
||||
this.renderer = value;
|
||||
this.renderer.Memory = this.memory;
|
||||
}
|
||||
}
|
||||
|
||||
public OnPresent Presenter
|
||||
{
|
||||
set { this.presenter = value; }
|
||||
}
|
||||
|
||||
public VideoManager(GbaManager gbaManager)
|
||||
{
|
||||
this.gbaManager = gbaManager;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this.curLine = 0;
|
||||
|
||||
this.renderer.Memory = memory;
|
||||
this.renderer.Reset();
|
||||
}
|
||||
|
||||
private void EnterVBlank(Arm7Processor processor)
|
||||
{
|
||||
ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT);
|
||||
dispstat |= 1;
|
||||
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
|
||||
|
||||
// Render the frame
|
||||
this.gbaManager.FramesRendered++;
|
||||
this.presenter(this.renderer.ShowFrame());
|
||||
|
||||
if ((dispstat & (1 << 3)) != 0)
|
||||
{
|
||||
// Fire the vblank irq
|
||||
processor.RequestIrq(0);
|
||||
}
|
||||
|
||||
// Check for DMA triggers
|
||||
this.memory.VBlankDma();
|
||||
}
|
||||
|
||||
private void LeaveVBlank(Arm7Processor processor)
|
||||
{
|
||||
ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT);
|
||||
dispstat &= 0xFFFE;
|
||||
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
|
||||
|
||||
processor.UpdateKeyState();
|
||||
|
||||
// Update the rot/scale values
|
||||
this.memory.Bgx[0] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG2X_L);
|
||||
this.memory.Bgx[1] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG3X_L);
|
||||
this.memory.Bgy[0] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG2Y_L);
|
||||
this.memory.Bgy[1] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG3Y_L);
|
||||
}
|
||||
|
||||
public void EnterHBlank(Arm7Processor processor)
|
||||
{
|
||||
ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT);
|
||||
dispstat |= 1 << 1;
|
||||
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
|
||||
|
||||
// Advance the bgx registers
|
||||
for (int bg = 0; bg <= 1; bg++)
|
||||
{
|
||||
short dmx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PB + (uint)bg * 0x10);
|
||||
short dmy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PD + (uint)bg * 0x10);
|
||||
this.memory.Bgx[bg] += dmx;
|
||||
this.memory.Bgy[bg] += dmy;
|
||||
}
|
||||
|
||||
if (this.curLine < 160)
|
||||
{
|
||||
this.memory.HBlankDma();
|
||||
|
||||
// Trigger hblank irq
|
||||
if ((dispstat & (1 << 4)) != 0)
|
||||
{
|
||||
processor.RequestIrq(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="processor"></param>
|
||||
/// <returns>true if end of frame</returns>
|
||||
public bool LeaveHBlank(Arm7Processor processor)
|
||||
{
|
||||
bool ret = false;
|
||||
ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT);
|
||||
dispstat &= 0xFFF9;
|
||||
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
|
||||
|
||||
// Move to the next line
|
||||
this.curLine++;
|
||||
|
||||
if (this.curLine >= 228)
|
||||
{
|
||||
// Start again at the beginning
|
||||
this.curLine = 0;
|
||||
}
|
||||
|
||||
// Update registers
|
||||
Memory.WriteU16(this.memory.IORam, Memory.VCOUNT, (ushort)this.curLine);
|
||||
|
||||
// Check for vblank
|
||||
if (this.curLine == 160)
|
||||
{
|
||||
this.EnterVBlank(processor);
|
||||
ret = true;
|
||||
}
|
||||
else if (this.curLine == 0)
|
||||
{
|
||||
this.LeaveVBlank(processor);
|
||||
}
|
||||
|
||||
// Check y-line trigger
|
||||
if (((dispstat >> 8) & 0xff) == this.curLine)
|
||||
{
|
||||
dispstat = (ushort)(Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT) | (1 << 2));
|
||||
Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat);
|
||||
|
||||
if ((dispstat & (1 << 5)) != 0)
|
||||
{
|
||||
processor.RequestIrq(2);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void RenderLine()
|
||||
{
|
||||
if (this.curLine < 160)
|
||||
{
|
||||
this.renderer.RenderLine(this.curLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1928,7 +1928,8 @@ namespace BizHawk.MultiClient
|
|||
MessageBox.Show("Unable to find the required GBA BIOS file - \n" + gbabios, "Unable to load BIOS", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
throw new Exception();
|
||||
}
|
||||
GBA gba = new GBA(nextComm);
|
||||
//GBA gba = new GBA(nextComm);
|
||||
var gba = new GarboDev.GbaManager(nextComm);
|
||||
gba.Load(rom.RomData, gbabios);
|
||||
nextEmulator = gba;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue