mirror of https://github.com/Washi1337/Emux.git
339 lines
13 KiB
C#
339 lines
13 KiB
C#
using System;
|
|
|
|
namespace Emux.GameBoy.Cpu
|
|
{
|
|
public class GameBoyAlu
|
|
{
|
|
private readonly RegisterBank _registers;
|
|
|
|
public GameBoyAlu(RegisterBank registers)
|
|
{
|
|
_registers = registers;
|
|
}
|
|
|
|
private ushort Perform(ushort a, ushort b, Func<ushort, ushort, int> operation, RegisterFlags affectedFlags)
|
|
{
|
|
int intermediate = operation(a, b);
|
|
ushort result = (ushort) (intermediate & 0xFFFF);
|
|
_registers.ClearFlags(affectedFlags);
|
|
var setAffected = RegisterFlags.None;
|
|
if ((intermediate & (1 << 16)) == (1 << 16))
|
|
setAffected |= RegisterFlags.C;
|
|
if ((operation((ushort) (a & 0xFFF), (ushort) (b & 0xFFF)) & (1 << 12)) == (1 << 12))
|
|
setAffected |= RegisterFlags.H;
|
|
if (result == 0)
|
|
setAffected |= RegisterFlags.Z;
|
|
_registers.SetFlags(setAffected & affectedFlags);
|
|
return result;
|
|
}
|
|
|
|
private byte Perform(byte a, byte b, Func<byte, byte, int> operation, RegisterFlags affectedFlags)
|
|
{
|
|
int intermediate = operation(a, b);
|
|
byte result = (byte) (intermediate & 0xFF);
|
|
_registers.ClearFlags(affectedFlags);
|
|
var setAffected = RegisterFlags.None;
|
|
if ((intermediate & (1 << 8)) == (1 << 8))
|
|
setAffected |= RegisterFlags.C;
|
|
if ((operation((byte) (a & 0xF), (byte) (b & 0xF)) & (1 << 4)) == (1 << 4))
|
|
setAffected |= RegisterFlags.H;
|
|
if (result == 0)
|
|
setAffected |= RegisterFlags.Z;
|
|
_registers.SetFlags(setAffected & affectedFlags);
|
|
return result;
|
|
}
|
|
|
|
private ushort Perform(ushort a, sbyte b, Func<ushort, sbyte, int> operation, RegisterFlags affectedFlags)
|
|
{
|
|
int intermediate = operation(a, b);
|
|
ushort result = (ushort) (intermediate & 0xFFFF);
|
|
_registers.ClearFlags(affectedFlags);
|
|
var setAffected = RegisterFlags.None;
|
|
if ((intermediate & (1 << 8)) == (1 << 8))
|
|
setAffected |= RegisterFlags.C;
|
|
if ((operation((ushort) (a & 0xF), (sbyte) (b & 0xF)) & (1 << 4)) == (1 << 4))
|
|
setAffected |= RegisterFlags.H;
|
|
if (result == 0)
|
|
setAffected |= RegisterFlags.Z;
|
|
_registers.SetFlags(setAffected & affectedFlags);
|
|
return result;
|
|
}
|
|
|
|
internal ushort Add(ushort a, ushort b, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
_registers.ClearFlags(resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
return Perform(a, b, (x, y) => x + y, affectedFlags);
|
|
}
|
|
|
|
internal ushort Add(ushort a, sbyte b, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
_registers.ClearFlags(resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
return Perform(a, b, (x, y) => x + y, affectedFlags);
|
|
}
|
|
|
|
internal byte Add(byte a, byte b, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
_registers.ClearFlags(resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
return Perform(a, b, (x, y) => x + y, affectedFlags);
|
|
}
|
|
|
|
internal ushort Sub(ushort a, ushort b, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
_registers.ClearFlags(resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
return Perform(a, b, (x, y) => x - y, affectedFlags);
|
|
}
|
|
|
|
internal byte Sub(byte a, byte b, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
_registers.ClearFlags(resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
return Perform(a, b, (x, y) => x - y, affectedFlags);
|
|
}
|
|
|
|
internal byte Adc(byte a, byte b, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
_registers.ClearFlags(resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
return Perform(a, b, (x, y) => x + y + (_registers.GetFlags(RegisterFlags.C) ? 1 : 0), affectedFlags);
|
|
}
|
|
|
|
internal byte Sbc(byte a, byte b, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
_registers.ClearFlags(resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
return Perform(a, b, (x, y) => x - (y + (_registers.GetFlags(RegisterFlags.C) ? 1 : 0)), affectedFlags);
|
|
}
|
|
|
|
internal byte And(byte b)
|
|
{
|
|
unchecked
|
|
{
|
|
int intermediate = _registers.A & b;
|
|
byte result = (byte) (intermediate & 0xFF);
|
|
_registers.OverwriteFlags(RegisterFlags.H | (result == 0 ? RegisterFlags.Z : RegisterFlags.None));
|
|
return result;
|
|
}
|
|
}
|
|
|
|
internal byte Xor(byte b)
|
|
{
|
|
unchecked
|
|
{
|
|
int intermediate = _registers.A ^ b;
|
|
byte result = (byte) (intermediate & 0xFF);
|
|
_registers.OverwriteFlags(result == 0 ? RegisterFlags.Z : RegisterFlags.None);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
internal byte Or(byte b)
|
|
{
|
|
unchecked
|
|
{
|
|
int intermediate = _registers.A | b;
|
|
byte result = (byte) (intermediate & 0xFF);
|
|
_registers.OverwriteFlags(result == 0 ? RegisterFlags.Z : RegisterFlags.None);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
internal void Cp(byte b)
|
|
{
|
|
Sub(_registers.A, b, RegisterFlags.Z | RegisterFlags.H | RegisterFlags.C, RegisterFlags.N);
|
|
}
|
|
|
|
internal byte Rl(byte value, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
byte newValue = (byte) ((value << 1) | (_registers.GetFlags(RegisterFlags.C) ? 1 : 0));
|
|
|
|
_registers.ClearFlags(affectedFlags | resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
|
|
var flags = RegisterFlags.None;
|
|
if (newValue == 0)
|
|
flags |= RegisterFlags.Z;
|
|
if ((value & (1 << 7)) == (1 << 7))
|
|
flags |= RegisterFlags.C;
|
|
_registers.SetFlags(flags);
|
|
|
|
return newValue;
|
|
}
|
|
|
|
internal byte Rlc(byte value, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
byte newValue = (byte) ((value << 1) | (value >> 7));
|
|
|
|
_registers.ClearFlags(affectedFlags | resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
var flags = RegisterFlags.None;
|
|
if (newValue == 0)
|
|
flags |= RegisterFlags.Z;
|
|
if ((value & (1 << 7)) == (1 << 7))
|
|
flags |= RegisterFlags.C;
|
|
_registers.SetFlags(flags);
|
|
|
|
return newValue;
|
|
}
|
|
|
|
internal byte Rr(byte value, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
byte newValue = (byte) ((value >> 1) | (_registers.GetFlags(RegisterFlags.C) ? 1 : 0));
|
|
|
|
_registers.ClearFlags(affectedFlags | resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
var flags = RegisterFlags.None;
|
|
if (newValue == 0)
|
|
flags |= RegisterFlags.Z;
|
|
if ((value & 1) == 1)
|
|
flags |= RegisterFlags.C;
|
|
_registers.SetFlags(flags);
|
|
|
|
return newValue;
|
|
}
|
|
|
|
internal byte Rrc(byte value, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
byte newValue = (byte) ((value >> 1) | ((value & 1) << 7));
|
|
|
|
_registers.ClearFlags(affectedFlags | resetFlags);
|
|
_registers.SetFlags(setFlags);
|
|
var flags = RegisterFlags.None;
|
|
if (newValue == 0)
|
|
flags |= RegisterFlags.Z;
|
|
if ((value & 1) == 1)
|
|
flags |= RegisterFlags.C;
|
|
_registers.SetFlags(flags);
|
|
|
|
return newValue;
|
|
}
|
|
|
|
internal byte Sla(byte value)
|
|
{
|
|
byte newValue = (byte) (value << 1);
|
|
var flags = RegisterFlags.None;
|
|
if (newValue == 0)
|
|
flags |= RegisterFlags.Z;
|
|
if ((value & (1 << 7)) == (1 << 7))
|
|
flags |= RegisterFlags.C;
|
|
_registers.OverwriteFlags(flags);
|
|
return newValue;
|
|
}
|
|
|
|
internal byte Sr(byte value, bool resetMsb)
|
|
{
|
|
byte newValue = (byte) (value >> 1);
|
|
if (!resetMsb)
|
|
newValue |= (byte) (value & (1 << 7));
|
|
var flags = RegisterFlags.None;
|
|
if (newValue == 0)
|
|
flags |= RegisterFlags.Z;
|
|
if ((value & 1) == 1)
|
|
flags |= RegisterFlags.C;
|
|
_registers.OverwriteFlags(flags);
|
|
return newValue;
|
|
}
|
|
|
|
internal byte Swap(byte value)
|
|
{
|
|
byte newValue = (byte) (((value & 0xF) << 4) | ((value & 0xF0) >> 4));
|
|
_registers.OverwriteFlags(newValue == 0 ? RegisterFlags.Z : RegisterFlags.None);
|
|
return newValue;
|
|
}
|
|
|
|
internal byte Set(byte value, int position)
|
|
{
|
|
return (byte) (value | (1 << position));
|
|
}
|
|
|
|
internal byte Res(byte value, int position)
|
|
{
|
|
return (byte) (value & ~(1 << position));
|
|
}
|
|
|
|
internal void Bit(byte value, int position)
|
|
{
|
|
var c = _registers.GetFlags(RegisterFlags.C) ? RegisterFlags.C : RegisterFlags.None;
|
|
var z = ((value >> position) & 1) == 0 ? RegisterFlags.Z : RegisterFlags.None;
|
|
_registers.OverwriteFlags(z | RegisterFlags.H | c);
|
|
}
|
|
|
|
internal ushort Increment(ushort value, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
return Add(value, 1, affectedFlags, setFlags, resetFlags);
|
|
}
|
|
|
|
internal ushort Decrement(ushort value, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
return Sub(value, 1, affectedFlags, setFlags, resetFlags);
|
|
}
|
|
|
|
internal byte Increment(byte value, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
return Add(value, (byte) 1, affectedFlags, setFlags, resetFlags);
|
|
}
|
|
|
|
internal byte Decrement(byte value, RegisterFlags affectedFlags = RegisterFlags.None,
|
|
RegisterFlags setFlags = RegisterFlags.None, RegisterFlags resetFlags = RegisterFlags.None)
|
|
{
|
|
return Sub(value, 1, affectedFlags, setFlags, resetFlags);
|
|
}
|
|
|
|
internal byte Cpl(byte value)
|
|
{
|
|
unchecked
|
|
{
|
|
_registers.SetFlags(RegisterFlags.N | RegisterFlags.H);
|
|
return (byte) ~value;
|
|
}
|
|
}
|
|
|
|
internal void Ccf()
|
|
{
|
|
if (_registers.GetFlags(RegisterFlags.C))
|
|
_registers.ClearFlags(RegisterFlags.C);
|
|
else
|
|
_registers.SetFlags(RegisterFlags.C);
|
|
}
|
|
|
|
internal void Daa()
|
|
{
|
|
var flags = RegisterFlags.None;
|
|
|
|
ushort value = _registers.A;
|
|
if ((value & 0xF) > 0x9 || _registers.GetFlags(RegisterFlags.H))
|
|
value += 6;
|
|
|
|
if ((value & 0xF0) > 0x90)
|
|
value += 0x60;
|
|
|
|
if ((value & (1 << 8)) == (1 << 8))
|
|
flags |= RegisterFlags.C;
|
|
if (_registers.A == 0)
|
|
flags |= RegisterFlags.Z;
|
|
|
|
_registers.A = (byte) (value & 0xFF);
|
|
|
|
_registers.ClearFlags(RegisterFlags.Z | RegisterFlags.H | RegisterFlags.C);
|
|
_registers.SetFlags(flags);
|
|
}
|
|
}
|
|
}
|