add project files
This commit is contained in:
93
MCEmuCore.GBMonolith/Cpu.cs
Normal file
93
MCEmuCore.GBMonolith/Cpu.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MCEmuCore.GBMonolith
|
||||
{
|
||||
public struct OpCode
|
||||
{
|
||||
public readonly byte Instruction;
|
||||
public byte? Arg1;
|
||||
public byte? Arg2;
|
||||
}
|
||||
class Cpu
|
||||
{
|
||||
const byte ZERO_FLAG = 0b10000000;
|
||||
const byte SUB_FLAG = 0b01000000;
|
||||
const byte HALF_CARRY = 0b00100000;
|
||||
const byte FULL_CARRY = 0b00010000;
|
||||
private readonly CpuRegisters registers;
|
||||
#region OpCodes
|
||||
|
||||
private readonly Dictionary<byte, Func<OpCode, bool>> OpCodeTable;
|
||||
#endregion
|
||||
|
||||
public Cpu()
|
||||
{
|
||||
registers = new CpuRegisters();
|
||||
|
||||
OpCodeTable = new Dictionary<byte, Func<OpCode, bool>>()
|
||||
{
|
||||
{0x00, (op) => {
|
||||
registers.PC += 1;
|
||||
return true;
|
||||
} },
|
||||
{0x01, (op) =>
|
||||
{
|
||||
// LD BC,d16
|
||||
if (op.Arg1.HasValue && op.Arg2.HasValue)
|
||||
{
|
||||
ushort operand = (ushort)(op.Arg2 + (op.Arg1 << 8));
|
||||
registers.BC = operand;
|
||||
registers.PC += 3;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(op, "Missing arguments for LD BC");
|
||||
}
|
||||
} },
|
||||
{0x02, (op) =>
|
||||
{
|
||||
// LD (BC), A
|
||||
throw new NotImplementedException("LD (BC), A");
|
||||
} },
|
||||
{0x03, (op) =>
|
||||
{
|
||||
registers.BC += 1;
|
||||
registers.PC += 1;
|
||||
return true;
|
||||
} },
|
||||
{0x04, (op) =>
|
||||
{
|
||||
// INC B
|
||||
byte reg = (byte)(registers.B + 1);
|
||||
registers.B = reg;
|
||||
|
||||
registers.Flags.Subtract = false;
|
||||
registers.Flags.Zero = reg == 0;
|
||||
registers.Flags.HalfCarry = reg == 16;
|
||||
|
||||
registers.PC += 1;
|
||||
return true;
|
||||
} }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class InvalidOperationException : Exception
|
||||
{
|
||||
public OpCode OpCode { get; set; }
|
||||
public InvalidOperationException() { }
|
||||
public InvalidOperationException(OpCode opCode, string message) : base(message)
|
||||
{
|
||||
OpCode = opCode;
|
||||
}
|
||||
public InvalidOperationException(string message) : base(message) { }
|
||||
public InvalidOperationException(string message, Exception inner) : base(message, inner) { }
|
||||
protected InvalidOperationException(
|
||||
System.Runtime.Serialization.SerializationInfo info,
|
||||
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
|
||||
}
|
||||
}
|
||||
308
MCEmuCore.GBMonolith/CpuRegisters.cs
Normal file
308
MCEmuCore.GBMonolith/CpuRegisters.cs
Normal file
@@ -0,0 +1,308 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MCEmuCore.GBMonolith
|
||||
{
|
||||
class CpuRegisters
|
||||
{
|
||||
enum Register { A, B, C, D, E, H, L };
|
||||
private readonly byte[] Registers = new byte[7];
|
||||
public readonly FlagRegister Flags = new FlagRegister();
|
||||
|
||||
#region Register Accessors
|
||||
public byte A
|
||||
{
|
||||
get
|
||||
{
|
||||
return Registers[(int)Register.A];
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.A] = value;
|
||||
}
|
||||
}
|
||||
public byte B
|
||||
{
|
||||
get
|
||||
{
|
||||
return Registers[(int)Register.B];
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.B] = value;
|
||||
}
|
||||
}
|
||||
public byte D
|
||||
{
|
||||
get
|
||||
{
|
||||
return Registers[(int)Register.D];
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.D] = value;
|
||||
}
|
||||
}
|
||||
public byte H
|
||||
{
|
||||
get
|
||||
{
|
||||
return Registers[(int)Register.H];
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.H] = value;
|
||||
}
|
||||
}
|
||||
public byte F
|
||||
{
|
||||
get
|
||||
{
|
||||
return Flags.Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
Flags.Value = value;
|
||||
}
|
||||
}
|
||||
public byte C
|
||||
{
|
||||
get
|
||||
{
|
||||
return Registers[(int)Register.C];
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.C] = value;
|
||||
}
|
||||
}
|
||||
public byte E
|
||||
{
|
||||
get
|
||||
{
|
||||
return Registers[(int)Register.E];
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.E] = value;
|
||||
}
|
||||
}
|
||||
public byte L
|
||||
{
|
||||
get
|
||||
{
|
||||
return Registers[(int)Register.L];
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.L] = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Wide Register Accessors
|
||||
public ushort AF
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort retVal = Flags.Value;
|
||||
retVal += (ushort)((Registers[(int)Register.A]) << 8);
|
||||
return retVal;
|
||||
}
|
||||
set
|
||||
{
|
||||
Flags.Value = (byte)(value & 0x00FF);
|
||||
Registers[(int)Register.A] = (byte)((value & 0xFF00) >> 8);
|
||||
}
|
||||
}
|
||||
public ushort BC
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort retVal = Registers[(int)Register.C];
|
||||
retVal += (ushort)((Registers[(int)Register.B]) << 8);
|
||||
return retVal;
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.C] = (byte)(value & 0x00FF);
|
||||
Registers[(int)Register.B] = (byte)((value & 0xFF00) >> 8);
|
||||
}
|
||||
}
|
||||
public ushort DE
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort retVal = Registers[(int)Register.E];
|
||||
retVal += (ushort)((Registers[(int)Register.D]) << 8);
|
||||
return retVal;
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.E] = (byte)(value & 0x00FF);
|
||||
Registers[(int)Register.D] = (byte)((value & 0xFF00) >> 8);
|
||||
}
|
||||
}
|
||||
public ushort HL
|
||||
{
|
||||
get
|
||||
{
|
||||
ushort retVal = Registers[(int)Register.L];
|
||||
retVal += (ushort)((Registers[(int)Register.H]) << 8);
|
||||
return retVal;
|
||||
}
|
||||
set
|
||||
{
|
||||
Registers[(int)Register.L] = (byte)(value & 0x00FF);
|
||||
Registers[(int)Register.H] = (byte)((value & 0xFF00) >> 8);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public ushort SP { get; set; }
|
||||
public ushort PC { get; set; }
|
||||
|
||||
#region Constructors
|
||||
public CpuRegisters()
|
||||
{
|
||||
AF = 0x01B0;
|
||||
BC = 0x0013;
|
||||
DE = 0x00D8;
|
||||
HL = 0x014D;
|
||||
}
|
||||
public CpuRegisters(ushort initValue) : base()
|
||||
{
|
||||
AF = initValue;
|
||||
BC = initValue;
|
||||
DE = initValue;
|
||||
HL = initValue;
|
||||
}
|
||||
public CpuRegisters(byte A, byte F, ushort SP, ushort PC)
|
||||
{
|
||||
this.A = A;
|
||||
this.F = F;
|
||||
this.SP = SP;
|
||||
this.PC = PC;
|
||||
}
|
||||
public CpuRegisters(byte A, byte F, byte B, byte C, byte D, byte E, byte H, byte L, ushort SP, ushort PC) : this(A, F, SP, PC)
|
||||
{
|
||||
this.B = B;
|
||||
this.C = C;
|
||||
this.D = D;
|
||||
this.E = E;
|
||||
this.H = H;
|
||||
this.L = L;
|
||||
}
|
||||
public CpuRegisters(byte A, byte F, ushort BC, ushort DE, ushort HL, ushort SP, ushort PC) : this(A, F, SP, PC)
|
||||
{
|
||||
this.BC = BC;
|
||||
this.DE = DE;
|
||||
this.HL = HL;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public Dictionary<string, ushort> DumpRegisters()
|
||||
{
|
||||
return new Dictionary<string, ushort>
|
||||
{
|
||||
{ "A", A },
|
||||
{ "F", F },
|
||||
{ "B", B },
|
||||
{ "C", C },
|
||||
{ "D", D },
|
||||
{ "E", E },
|
||||
{ "H", H },
|
||||
{ "L", L },
|
||||
{ "AF", AF },
|
||||
{ "BC", BC },
|
||||
{ "DE", DE },
|
||||
{ "HL", HL },
|
||||
{ "PC", PC },
|
||||
{ "SP", SP }
|
||||
};
|
||||
}
|
||||
|
||||
#region Print Functions
|
||||
public void PrintSingleStatus()
|
||||
{
|
||||
Console.WriteLine("Register status:");
|
||||
Console.WriteLine($"A:\t{A}\tF:\t{F}");
|
||||
Console.WriteLine($"B:\t{B}\tC:\t{C}");
|
||||
Console.WriteLine($"D:\t{D}\tE:\t{E}");
|
||||
Console.WriteLine($"H:\t{H}\tL:\t{L}\r\n");
|
||||
}
|
||||
public void PrintWideStatus()
|
||||
{
|
||||
Console.WriteLine("Wide Register status:");
|
||||
Console.WriteLine($"AF:\t{AF}");
|
||||
Console.WriteLine($"BC:\t{BC}");
|
||||
Console.WriteLine($"DE:\t{DE}");
|
||||
Console.WriteLine($"HL:\t{HL}");
|
||||
}
|
||||
public void PrintFlags()
|
||||
{
|
||||
Console.WriteLine("Flag status:");
|
||||
Console.WriteLine($"Zero:\t\t{Flags.Zero}");
|
||||
Console.WriteLine($"Subtract:\t{Flags.Subtract}");
|
||||
Console.WriteLine($"Half Carry:\t{Flags.HalfCarry}");
|
||||
Console.WriteLine($"Carry:\t\t{Flags.Carry}");
|
||||
Console.WriteLine($"Raw Register:\t{Convert.ToString(Flags.Value, 2)}");
|
||||
}
|
||||
#endregion
|
||||
|
||||
public class FlagRegister
|
||||
{
|
||||
protected internal byte Value { get; set; }
|
||||
#region Boolean Flag Properties
|
||||
public bool Zero
|
||||
{
|
||||
get { return (Value & 0b1000_0000) != 0; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
Value |= 0b1000_0000;
|
||||
else
|
||||
Value &= 0b0111_1111;
|
||||
}
|
||||
}
|
||||
public bool Subtract
|
||||
{
|
||||
get { return (Value & 0b0100_0000) != 0; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
Value |= 0b0100_0000;
|
||||
else
|
||||
Value &= 0b1011_1111;
|
||||
}
|
||||
}
|
||||
public bool HalfCarry
|
||||
{
|
||||
get { return (Value & 0b0010_0000) != 0; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
Value |= 0b0010_0000;
|
||||
else
|
||||
Value &= 0b1101_1111;
|
||||
}
|
||||
}
|
||||
public bool Carry
|
||||
{
|
||||
get { return (Value & 0b0001_0000) != 0; }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
Value |= 0b0001_0000;
|
||||
else
|
||||
Value &= 0b1110_1111;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
public bool IsValid
|
||||
{
|
||||
get { return (Value & 0b0000_1111) == 0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
MCEmuCore.GBMonolith/MCEmuCore.GBMonolith.csproj
Normal file
8
MCEmuCore.GBMonolith/MCEmuCore.GBMonolith.csproj
Normal file
@@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
60
MCEmuCore.GBMonolith/Program.cs
Normal file
60
MCEmuCore.GBMonolith/Program.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
|
||||
namespace MCEmuCore.GBMonolith
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
TestRegisters();
|
||||
}
|
||||
|
||||
static void TestRegisters()
|
||||
{
|
||||
CpuRegisters cpuRegisters = new CpuRegisters(true);
|
||||
Console.WriteLine("Initializing registers:");
|
||||
Console.WriteLine("A:\t1\tF:\t16");
|
||||
Console.WriteLine("B:\t2\tC:\t100");
|
||||
Console.WriteLine("D:\t3\tE:\t150");
|
||||
Console.WriteLine("H:\t4\tL:\t200\r\n");
|
||||
cpuRegisters.A += 1;
|
||||
cpuRegisters.B += 2;
|
||||
cpuRegisters.D += 3;
|
||||
cpuRegisters.H += 4;
|
||||
cpuRegisters.F += 16;
|
||||
cpuRegisters.C += 100;
|
||||
cpuRegisters.E += 150;
|
||||
cpuRegisters.L += 200;
|
||||
cpuRegisters.PrintSingleStatus();
|
||||
Console.WriteLine("Expected Wide Register status:");
|
||||
Console.WriteLine("BC:\t612");
|
||||
Console.WriteLine("DE:\t918");
|
||||
Console.WriteLine("HL:\t1224\r\n");
|
||||
Console.Write("Actual ");
|
||||
cpuRegisters.PrintWideStatus();
|
||||
Console.WriteLine("\r\nSetting wide values:");
|
||||
Console.WriteLine("BC:\t26214");
|
||||
Console.WriteLine("DE:\t22016");
|
||||
Console.WriteLine("HL:\t153\r\n");
|
||||
cpuRegisters.BC = 26214;
|
||||
cpuRegisters.DE = 22016;
|
||||
cpuRegisters.HL = 153;
|
||||
cpuRegisters.PrintWideStatus();
|
||||
Console.WriteLine("\r\nExpected Register status:");
|
||||
Console.WriteLine("A:\t1\tF:\t16");
|
||||
Console.WriteLine("B:\t102\tC:\t102");
|
||||
Console.WriteLine("D:\t86\tE:\t0");
|
||||
Console.WriteLine("H:\t0\tL:\t153\r\n");
|
||||
Console.Write("Actual ");
|
||||
cpuRegisters.PrintSingleStatus();
|
||||
Console.WriteLine("Testing Flags:");
|
||||
cpuRegisters.PrintFlags();
|
||||
cpuRegisters.Flags.Zero = true;
|
||||
cpuRegisters.Flags.Subtract = true;
|
||||
cpuRegisters.Flags.HalfCarry = true;
|
||||
cpuRegisters.Flags.Carry = false;
|
||||
cpuRegisters.PrintFlags();
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user