BizHawk/attic/GarboDev/Arm7Processor.cs

501 lines
15 KiB
C#

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;
}
}
}
}
}