BizHawk/BizHawk.Emulation/CPUs/ARM/ARM.cs

239 lines
5.9 KiB
C#

using System;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.CPUs.ARM
{
//reference: ARM manual DDI0406B
public enum AT
{
PEEK, POKE, READ, WRITE, FETCH
}
//this may need to be rearchitectured to pass the entire instruction, as specified in the docs...
public interface ICoprocessorSet
{
//read
uint MRC(uint cp, uint opc1, uint t, uint crn, uint crm, uint opc2);
string Describe(uint cp, uint opc1, uint t, uint crn, uint crm, uint opc2);
}
public interface ARM_SYS
{
uint svc(uint num);
string svc_name(uint num);
ICoprocessorSet coprocessors { get; }
}
public interface ARM_BUS
{
byte Read08(AT at, uint addr);
ushort Read16(AT at, uint addr);
uint Read32(AT at, uint addr);
void Write08(AT at, uint addr, byte val);
void Write16(AT at, uint addr, ushort val);
void Write32(AT at, uint addr, uint val);
void Write64(AT at, uint addr, ulong val);
}
public unsafe partial class ARM
{
public ARM_SYS sys;
public ARM_BUS bus;
public enum Mode
{
USR = 0x10,
FIQ = 0x11,
IRQ = 0x12,
SVC = 0x13,
ABT = 0x17,
UND = 0x1B,
SYS = 0x1F
}
public struct Status_Reg
{
//public uint val;
//public uint mode { get { return val & 0x1F; } set { val = (uint)((val & ~0x1F) | (value & 0x1F)); } }
//public uint T { get { return val & 0x20; } set { val = (uint)((val & ~0x20) | (value & 0x20)); } }
//public uint F { get { return val & 0x40; } set { val = (uint)((val & ~0x40) | (value & 0x40)); } }
//public uint I { get { return val & 0x80; } set { val = (uint)((val & ~0x80) | (value & 0x80)); } }
//public uint N { get { return val & 0x80000000; } set { val = (uint)((val & ~0x80000000) | (value & 0x80000000)); } }
public Mode mode;
public Bit T, F, I, Q, V, C, Z, N;
//todo - consider combining the condition flags together for various speedups
}
public uint next_instruct_adr;
public uint instruct_adr;
public uint instruction;
uint thumb_32bit_extra;
bool thumb_32bit;
public class Registers
{
ARM cpu;
public Registers(ARM cpu) { this.cpu = cpu; }
public uint this[int n] { get { return this[(uint)n]; } set { this[(uint)n] = value; } }
public uint this[uint n]
{
//this is a bit different than the docs, in the interest of performance
get
{
if (n == 15)
{
uint offset = (cpu._CurrentInstrSet() == EInstrSet.ARM ? 8U : 4U);
return cpu._R[15] + offset;
}
else
{
//TODO - junk about security and monitor mode and FIQ and whatnot
return cpu._R[n];
}
}
set
{
Debug.Assert(n >= 0 && n <= 14);
//TODO - junk about security and monitor mode and FIQ and whatnot
if (n == 13 && (value & 3) != 0 && cpu._CurrentInstrSet() != EInstrSet.ARM) cpu._FlagUnpredictable();
cpu._R[n] = value;
}
}
}
public Registers r;
uint[] _R = new uint[16];
public Status_Reg APSR;
public Status_Reg SPSR;
float[] S = new float[32];
double[] D = new double[16];
unsafe static uint float_downcast(float f)
{
float* fp = &f;
return *(uint*)fp;
}
unsafe static float float_upcast(uint f)
{
uint* fp = &f;
return *(float*)fp;
}
unsafe static ulong double_downcast(double f)
{
double* fp = &f;
return *(ulong*)fp;
}
unsafe static double double_upcast(ulong f)
{
ulong* fp = &f;
return *(double*)fp;
}
public uint SP { get { return r[13]; } set { r[13] = value; } }
public uint LR { get { return r[14]; } set { r[14] = value; } }
public uint PC { get { return r[15]; } set { r[15] = value; } }
uint R13_usr, R14_usr;
uint R13_svc, R14_svc;
uint R13_abt, R14_abt;
uint R13_und, R14_und;
uint R13_irq, R14_irq;
uint R8_fiq, R9_fiq, R10_fiq, R11_fiq, R12_fiq, R13_fiq, R14_fiq;
Status_Reg SPSR_svc, SPSR_abt, SPSR_und, SPSR_irq, SPSR_fiq;
uint FPSCR;
uint _FPSCR_LEN() { return 0; }
uint _FPSCR_STRIDE() { return 0; }
public ARM(ARM_SYS sys, ARM_BUS bus)
{
this.sys = sys;
this.bus = bus;
r = new Registers(this);
}
//disassembly state
public bool disassemble;
public bool nstyle;
public string disassembly;
enum Encoding
{
T1, T2, T3, T4,
A1, A2, A3, A4
}
bool EncodingT(Encoding e)
{
return e == Encoding.T1 || e == Encoding.T2 || e == Encoding.T3 || e == Encoding.T4;
}
bool EncodingA(Encoding e)
{
return e == Encoding.A1 || e == Encoding.A2 || e == Encoding.A3 || e == Encoding.A4;
}
public enum TraceType
{
Full, Short
}
ulong tracetr = 0;
public string Trace(TraceType tt)
{
disassemble = true;
Execute();
StringBuilder sb = new StringBuilder(256);
sb.AppendFormat("{0}:{1}:{2:X8} ", tracetr, _CurrentInstrSet() == EInstrSet.ARM ? 'A' : 'T', instruct_adr);
tracetr++;
if(thumb_32bit)
sb.AppendFormat("{0:X4}{1:X4} ", instruction,thumb_32bit_extra);
else
sb.AppendFormat("{0:X8} ",instruction);
sb.Append(disassembly.PadRight(30,' '));
if(tt == TraceType.Full)
for(int i=0;i<16;i++)
sb.AppendFormat(" {0:X8}", r[i]);
disassemble = false;
return sb.ToString();
}
public uint Fetch()
{
thumb_32bit = false;
instruct_adr = next_instruct_adr;
if (APSR.T)
{
//THUMB:
Debug.Assert((instruct_adr & 1) == 0);
_R[15] = instruct_adr;
next_instruct_adr += 2;
instruction = bus.Read16(AT.FETCH, instruct_adr);
}
else
{
Debug.Assert((instruct_adr & 3) == 0);
_R[15] = instruct_adr;
next_instruct_adr += 4;
instruction = bus.Read32(AT.FETCH, instruct_adr);
}
return 1;
}
void ThumbFetchExtra()
{
thumb_32bit = true;
thumb_32bit_extra = bus.Read16(AT.FETCH, next_instruct_adr);
next_instruct_adr = instruct_adr + 2;
}
}
}