870 lines
25 KiB
C#
870 lines
25 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
|
|
namespace MC68000
|
|
{
|
|
public partial class MC68K
|
|
{
|
|
#region Add Helper Functions
|
|
private sbyte Add(sbyte a, sbyte b, bool updateConditions, bool useX)
|
|
{
|
|
int result = useX ? (int)a + (int)b + (this.X ? 1 : 0) : (int)a + (int)b;
|
|
|
|
if (updateConditions)
|
|
{
|
|
this.C = this.X = (result & 0x100) > 0;
|
|
this.V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
|
this.N = result < 0;
|
|
if (!useX) { this.Z = result == 0; }
|
|
}
|
|
|
|
return (sbyte)result;
|
|
}
|
|
|
|
private short Add(short a, short b, bool updateConditions, bool useX)
|
|
{
|
|
int result = useX ? (int)a + (int)b + (this.X ? 1 : 0) : (int)a + (int)b;
|
|
|
|
if (updateConditions)
|
|
{
|
|
this.C = this.X = (result & 0x10000) > 0;
|
|
this.V = result > short.MaxValue || result < short.MinValue;
|
|
this.N = result < 0;
|
|
if (!useX) { this.Z = result == 0; }
|
|
}
|
|
|
|
return (short)result;
|
|
}
|
|
|
|
private int Add(int a, int b, bool updateConditions, bool useX)
|
|
{
|
|
long result = useX ? (long)a + (long)b + (this.X ? 1 : 0) : (long)a + (long)b;
|
|
|
|
if (updateConditions)
|
|
{
|
|
this.C = this.X = (result & 0x100000000) > 0;
|
|
this.V = result > int.MaxValue || result < int.MinValue;
|
|
this.N = result < 0;
|
|
if (!useX) { this.Z = result == 0; }
|
|
}
|
|
|
|
return (int)result;
|
|
}
|
|
#endregion Add Helper Functions
|
|
|
|
#region Sub Helper Functions
|
|
private sbyte Sub(sbyte a, sbyte b, bool updateConditions, bool setX, bool useX)
|
|
{
|
|
int result = useX ? (int)b - (int)a - (this.X ? 1 : 0) : (int)b - (int)a;
|
|
|
|
if (updateConditions)
|
|
{
|
|
this.C = (result & 0x100) > 0;
|
|
this.V = result > sbyte.MaxValue || result < sbyte.MinValue;
|
|
this.N = result < 0;
|
|
if (!useX) { this.Z = result == 0; }
|
|
if (setX) { this.X = this.C; }
|
|
}
|
|
|
|
return (sbyte)result;
|
|
}
|
|
|
|
private short Sub(short a, short b, bool updateConditions, bool setX, bool useX)
|
|
{
|
|
int result = useX ? (int)b - (int)a - (this.X ? 1 : 0) : (int)b - (int)a;
|
|
|
|
if (updateConditions)
|
|
{
|
|
this.C = (result & 0x10000) > 0;
|
|
this.V = result > short.MaxValue || result < short.MinValue;
|
|
this.N = result < 0;
|
|
if (!useX) { this.Z = result == 0; }
|
|
if (setX) { this.X = this.C; }
|
|
}
|
|
|
|
return (short)result;
|
|
}
|
|
|
|
private int Sub(int a, int b, bool updateConditions, bool setX, bool useX)
|
|
{
|
|
long result = useX ? (long)b - (long)a - (this.X ? 1 : 0) : (long)b - (long)a;
|
|
|
|
if (updateConditions)
|
|
{
|
|
this.C = (result & 0x100000000) > 0;
|
|
this.V = result > int.MaxValue || result < int.MinValue;
|
|
this.N = result < 0;
|
|
if (!useX) { this.Z = result == 0; }
|
|
if (setX) { this.X = this.C; }
|
|
}
|
|
|
|
return (int)result;
|
|
}
|
|
#endregion Sub Helper Functions
|
|
|
|
private void ADD_Dest()
|
|
{
|
|
int src_register = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
sbyte result = Add((sbyte)this.m_D[src_register], PeekOperandB(mode, register), true, false);
|
|
SetOperandB(mode, register, result);
|
|
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
}
|
|
case 1:
|
|
{
|
|
short result = Add((short)this.m_D[src_register], PeekOperandW(mode, register), true, false);
|
|
SetOperandW(mode, register, result);
|
|
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
}
|
|
case 2:
|
|
{
|
|
int result = Add(this.m_D[src_register], PeekOperandL(mode, register), true, false);
|
|
SetOperandL(mode, register, result);
|
|
this.m_Cycles += 12 + Helpers.EACalcTimeL(mode, register);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ADD_Source()
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
sbyte result = Add((sbyte)this.m_D[dest_register], FetchOperandB(src_mode, src_register), true, false);
|
|
Helpers.Inject(ref this.m_D[dest_register], result);
|
|
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
}
|
|
case 1:
|
|
{
|
|
short result = Add((short)this.m_D[dest_register], FetchOperandW(src_mode, src_register), true, false);
|
|
Helpers.Inject(ref this.m_D[dest_register], result);
|
|
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
}
|
|
case 2:
|
|
{
|
|
int result = Add(this.m_D[dest_register], FetchOperandL(src_mode, src_register), true, false);
|
|
this.m_D[dest_register] = result;
|
|
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ADDA()
|
|
{
|
|
int register = (this.m_IR >> 9) & 0x7;
|
|
int opmode = (this.m_IR >> 6) & 0x7;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
switch (opmode)
|
|
{
|
|
case 3: // W
|
|
this.m_A[register] += FetchOperandW(src_mode, src_register);
|
|
this.m_Cycles += 8 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
case 7: // L
|
|
this.m_A[register] += FetchOperandL(src_mode, src_register);
|
|
this.m_Cycles += 6 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void ADDI()
|
|
{
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: // B
|
|
{
|
|
sbyte result = Add((sbyte)FetchW(), PeekOperandB(mode, register), true, false);
|
|
SetOperandB(mode, register, result);
|
|
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
}
|
|
case 1: // W
|
|
{
|
|
short result = Add(FetchW(), PeekOperandW(mode, register), true, false);
|
|
SetOperandW(mode, register, result);
|
|
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
}
|
|
case 2: // L
|
|
{
|
|
int result = Add(FetchL(), PeekOperandL(mode, register), true, false);
|
|
SetOperandL(mode, register, result);
|
|
this.m_Cycles += (mode == 0) ? 16 : 20 + Helpers.EACalcTimeL(mode, register);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ADDQ() // Add Quick
|
|
{
|
|
int data = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
// With data, 0 means 8
|
|
data = (data == 0) ? 8 : data;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: // B
|
|
{
|
|
if (mode == 1)
|
|
{
|
|
throw new ArgumentException("Byte operation not allowed on address registers");
|
|
}
|
|
sbyte result = Add(PeekOperandB(mode, register), (sbyte)data, (mode != 1), false);
|
|
SetOperandB(mode, register, result);
|
|
switch (mode) {
|
|
case 0: this.m_Cycles += 4; break;
|
|
case 1: this.m_Cycles += 4; break;
|
|
default: this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register); break;
|
|
}
|
|
return;
|
|
}
|
|
case 1: // W
|
|
{
|
|
if (mode == 1)
|
|
{
|
|
int result = Add(PeekOperandL(mode, register), data, false, false);
|
|
SetOperandL(mode, register, result);
|
|
}
|
|
else
|
|
{
|
|
short result = Add(PeekOperandW(mode, register), (short)data, true, false);
|
|
SetOperandW(mode, register, result);
|
|
}
|
|
switch (mode)
|
|
{
|
|
case 0: this.m_Cycles += 4; break;
|
|
case 1: this.m_Cycles += 4; break;
|
|
default: this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register); break;
|
|
}
|
|
return;
|
|
}
|
|
case 2: // L
|
|
{
|
|
int result = Add(PeekOperandL(mode, register), data, (mode != 1), false);
|
|
SetOperandL(mode, register, result);
|
|
switch (mode)
|
|
{
|
|
case 0: this.m_Cycles += 8; break;
|
|
case 1: this.m_Cycles += 8; break;
|
|
default: this.m_Cycles += 12 + Helpers.EACalcTimeL(mode, register); break;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ADDX()
|
|
{
|
|
int regRx = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int rm = (this.m_IR >> 3) & 0x1;
|
|
int regRy = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
if (rm == 0)
|
|
{
|
|
this.m_D[regRy] = Add((sbyte)this.m_D[regRx], (sbyte)this.m_D[regRy], true, true);
|
|
this.m_Cycles += 4;
|
|
}
|
|
else
|
|
{
|
|
WriteB(this.m_A[regRy], Add(ReadB(this.m_A[regRx]), ReadB(this.m_A[regRy]), true, true));
|
|
this.m_Cycles += 18;
|
|
}
|
|
return;
|
|
}
|
|
case 1:
|
|
{
|
|
if (rm == 0)
|
|
{
|
|
this.m_D[regRy] = Add((short)this.m_D[regRx], (short)this.m_D[regRy], true, true);
|
|
this.m_Cycles += 4;
|
|
}
|
|
else
|
|
{
|
|
WriteW(this.m_A[regRy], Add(ReadW(this.m_A[regRx]), ReadW(this.m_A[regRy]), true, true));
|
|
this.m_Cycles += 18;
|
|
}
|
|
return;
|
|
}
|
|
case 2:
|
|
{
|
|
if (rm == 0)
|
|
{
|
|
this.m_D[regRy] = Add(this.m_D[regRx], this.m_D[regRy], true, true);
|
|
this.m_Cycles += 8;
|
|
}
|
|
else
|
|
{
|
|
WriteL(this.m_A[regRy], Add(ReadB(this.m_A[regRx]), ReadB(this.m_A[regRy]), true, true));
|
|
this.m_Cycles += 30;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CLR() // Clear an operand
|
|
{
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
this.C = this.N = this.V = false;
|
|
this.Z = true;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: // B
|
|
SetOperandB(mode, register, 0);
|
|
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
case 1: // W
|
|
SetOperandW(mode, register, 0);
|
|
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
case 2: // L
|
|
SetOperandL(mode, register, 0);
|
|
this.m_Cycles += (mode == 0) ? 6 : 12 + Helpers.EACalcTimeL(mode, register);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void CMP() // Compare
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int opmode = (this.m_IR >> 6) & 0x7;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
switch (opmode)
|
|
{
|
|
case 0: // B
|
|
Sub(FetchOperandB(src_mode, src_register), (sbyte)this.m_D[dest_register], true, false, false);
|
|
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
case 1: // W
|
|
Sub(FetchOperandW(src_mode, src_register), (short)this.m_D[dest_register], true, false, false);
|
|
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
case 2: // L
|
|
Sub(FetchOperandL(src_mode, src_register), this.m_D[dest_register], true, false, false);
|
|
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void CMPA()
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int opmode = (this.m_IR >> 6) & 0x7;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
switch (opmode)
|
|
{
|
|
case 3: // W
|
|
Sub((int)FetchOperandW(src_mode, src_register), this.m_A[dest_register], true, false, false);
|
|
this.m_Cycles += 6 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
case 7: // L
|
|
Sub(FetchOperandL(src_mode, src_register), this.m_A[dest_register], true, false, false);
|
|
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void CMPI()
|
|
{
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: // B
|
|
Sub((sbyte)FetchW(), FetchOperandB(mode, register), true, false, false);
|
|
this.m_Cycles += (mode == 0) ? 8 : 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
case 1: // W
|
|
Sub((short)FetchW(), FetchOperandW(mode, register), true, false, false);
|
|
this.m_Cycles += (mode == 0) ? 8 : 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
case 2: // L
|
|
Sub(FetchL(), FetchOperandL(mode, register), true, false, false);
|
|
this.m_Cycles += (mode == 0) ? 14 : 12 + Helpers.EACalcTimeL(mode, register);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void CMPM()
|
|
{
|
|
int registerAx = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int registerAy = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: // B
|
|
Sub((sbyte)this.m_A[registerAy], (sbyte)this.m_A[registerAy], true, false, false);
|
|
this.m_Cycles += 12;
|
|
return;
|
|
case 1: // W
|
|
Sub((short)this.m_A[registerAy], (short)this.m_A[registerAy], true, false, false);
|
|
this.m_Cycles += 12;
|
|
return;
|
|
case 2: // L
|
|
Sub(this.m_A[registerAy], this.m_A[registerAy], true, false, false);
|
|
this.m_Cycles += 20;
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void DIVS() // Unsigned multiply
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
// On 68000, only allowable size is Word
|
|
int source = (short)FetchOperandW(src_mode, src_register);
|
|
int dest = this.m_D[dest_register];
|
|
|
|
this.C = false;
|
|
if (source == 0)
|
|
{
|
|
throw new ArgumentException("Divide by zero...");
|
|
}
|
|
|
|
int quotient = dest / source;
|
|
int remainder = dest % source;
|
|
|
|
// Detect overflow
|
|
if (quotient < short.MinValue || quotient > short.MaxValue)
|
|
{
|
|
this.V = true;
|
|
throw new ArgumentException("Division overflow");
|
|
}
|
|
this.m_D[dest_register] = (remainder << 16) | quotient;
|
|
|
|
this.m_Cycles += 158 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
|
|
this.N = quotient < 0;
|
|
this.Z = quotient == 0;
|
|
this.V = false;
|
|
}
|
|
|
|
private void DIVU() // Unsigned multiply
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
// On 68000, only allowable size is Word
|
|
uint source = (uint)FetchOperandW(src_mode, src_register);
|
|
uint dest = (uint)this.m_D[dest_register];
|
|
|
|
this.C = false;
|
|
if (source == 0)
|
|
{
|
|
throw new ArgumentException("Divide by zero...");
|
|
}
|
|
|
|
uint quotient = dest / source;
|
|
uint remainder = dest % source;
|
|
|
|
// Detect overflow
|
|
if (quotient < ushort.MinValue || quotient > ushort.MaxValue ||
|
|
remainder < ushort.MinValue || remainder > ushort.MaxValue)
|
|
{
|
|
this.V = true;
|
|
throw new ArgumentException("Division overflow");
|
|
}
|
|
this.m_D[dest_register] = (int)((remainder << 16) | quotient);
|
|
|
|
this.m_Cycles += 140 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
|
|
this.N = quotient < 0;
|
|
this.Z = quotient == 0;
|
|
this.V = false;
|
|
}
|
|
|
|
private void EXT() // Sign extend
|
|
{
|
|
this.m_Cycles += 4;
|
|
|
|
switch ((this.m_IR >> 6) & 0x7)
|
|
{
|
|
case 2: // Byte to word
|
|
Helpers.Inject(ref this.m_D[this.m_IR & 0x7], (short)((sbyte)this.m_D[this.m_IR & 0x7]));
|
|
break;
|
|
case 3: // Word to long
|
|
this.m_D[this.m_IR & 0x7] = (short)this.m_D[this.m_IR & 0x7];
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void MULS() // Unsigned multiply
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
// On 68000, only allowable size is Word
|
|
short operand = FetchOperandW(src_mode, src_register);
|
|
short currentValue = (short)this.m_D[dest_register];
|
|
|
|
int newValue = operand * currentValue;
|
|
this.m_D[dest_register] = newValue;
|
|
|
|
this.m_Cycles += 70 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
|
|
this.N = newValue < 0;
|
|
this.Z = newValue == 0;
|
|
this.V = false; // Can't get an overflow
|
|
this.C = false;
|
|
}
|
|
|
|
private void MULU() // Unsigned multiply
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
// On 68000, only allowable size is Word
|
|
ushort operand = (ushort)FetchOperandW(src_mode, src_register);
|
|
ushort currentValue = (ushort)this.m_D[dest_register];
|
|
|
|
uint newValue = (uint)(operand * currentValue);
|
|
this.m_D[dest_register] = (int)newValue;
|
|
|
|
this.m_Cycles += 70 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
|
|
this.N = (int)newValue < 0;
|
|
this.Z = (newValue == 0);
|
|
this.V = false; // Can't get an overflow
|
|
this.C = false;
|
|
}
|
|
|
|
private void NEG()
|
|
{
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
SetOperandB(mode, register, Sub(PeekOperandB(mode, register), (sbyte)0, true, true, false));
|
|
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
case 1:
|
|
SetOperandW(mode, register, Sub(PeekOperandW(mode, register), (short)0, true, true, false));
|
|
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
case 2:
|
|
SetOperandL(mode, register, Sub(PeekOperandL(mode, register), (int)0, true, true, false));
|
|
this.m_Cycles += (mode == 0) ? 6 : 12 + Helpers.EACalcTimeL(mode, register);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void NEGX()
|
|
{
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
SetOperandB(mode, register, Sub(PeekOperandB(mode, register), (sbyte)0, true, true, false));
|
|
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
case 1:
|
|
SetOperandW(mode, register, Sub(PeekOperandW(mode, register), (short)0, true, true, false));
|
|
this.m_Cycles += (mode == 0) ? 4 : 8 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
case 2:
|
|
SetOperandL(mode, register, Sub(PeekOperandL(mode, register), (int)0, true, true, false));
|
|
this.m_Cycles += (mode == 0) ? 6 : 12 + Helpers.EACalcTimeL(mode, register);
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void SUB_Dest()
|
|
{
|
|
int src_register = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
sbyte result = Sub((sbyte)this.m_D[src_register], PeekOperandB(mode, register), true, true, false);
|
|
SetOperandB(mode, register, result);
|
|
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, src_register);
|
|
return;
|
|
}
|
|
case 1:
|
|
{
|
|
short result = Sub((short)this.m_D[src_register], PeekOperandW(mode, register), true, true, false);
|
|
SetOperandW(mode, register, result);
|
|
this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, src_register);
|
|
return;
|
|
}
|
|
case 2:
|
|
{
|
|
int result = Sub(this.m_D[src_register], PeekOperandL(mode, register), true, true, false);
|
|
SetOperandL(mode, register, result);
|
|
this.m_Cycles += 12 + Helpers.EACalcTimeL(mode, src_register);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SUB_Source()
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
sbyte result = Sub(FetchOperandB(src_mode, src_register), (sbyte)this.m_D[dest_register], true, true, false);
|
|
Helpers.Inject(ref this.m_D[dest_register], result);
|
|
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
}
|
|
case 1:
|
|
{
|
|
short result = Sub(FetchOperandW(src_mode, src_register), (short)this.m_D[dest_register], true, true, false);
|
|
Helpers.Inject(ref this.m_D[dest_register], result);
|
|
this.m_Cycles += 4 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
return;
|
|
}
|
|
case 2:
|
|
{
|
|
int result = Sub(FetchOperandL(src_mode, src_register), this.m_D[dest_register], true, true, false);
|
|
this.m_D[dest_register] = (int)result;
|
|
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SUBA()
|
|
{
|
|
int dest_register = (this.m_IR >> 9) & 0x7;
|
|
int opmode = (this.m_IR >> 6) & 0x7;
|
|
int src_mode = (this.m_IR >> 3) & 0x7;
|
|
int src_register = this.m_IR & 0x7;
|
|
|
|
switch (opmode)
|
|
{
|
|
case 3: // W
|
|
{
|
|
int operand = FetchOperandW(src_mode, src_register); // Sign-extended
|
|
this.m_A[dest_register] -= operand;
|
|
this.m_Cycles += 8 + Helpers.EACalcTimeBW(src_mode, src_register);
|
|
break;
|
|
}
|
|
case 7: // L
|
|
{
|
|
int operand = FetchOperandL(src_mode, src_register);
|
|
this.m_A[dest_register] -= operand;
|
|
this.m_Cycles += 6 + Helpers.EACalcTimeL(src_mode, src_register);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SUBI()
|
|
{
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: // B
|
|
{
|
|
sbyte result = Sub((sbyte)FetchW(), PeekOperandB(mode, register), true, true, false);
|
|
SetOperandB(mode, register, result);
|
|
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
}
|
|
case 1: // W
|
|
{
|
|
short result = Sub(FetchW(), PeekOperandW(mode, register), true, true, false);
|
|
SetOperandW(mode, register, result);
|
|
this.m_Cycles += (mode == 0) ? 8 : 12 + Helpers.EACalcTimeBW(mode, register);
|
|
return;
|
|
}
|
|
case 2: // L
|
|
{
|
|
int result = Sub(FetchL(), PeekOperandL(mode, register), true, true, false);
|
|
SetOperandL(mode, register, result);
|
|
this.m_Cycles += (mode == 0) ? 16 : 20 + Helpers.EACalcTimeL(mode, register);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SUBQ() // Add Quick
|
|
{
|
|
int data = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int mode = (this.m_IR >> 3) & 0x7;
|
|
int register = this.m_IR & 0x7;
|
|
|
|
// With data, 0 means 8
|
|
data = (data == 0) ? 8 : data;
|
|
|
|
switch (size)
|
|
{
|
|
case 0: // B
|
|
{
|
|
if (mode == 1)
|
|
{
|
|
throw new ArgumentException("Byte operation not allowed on address registers");
|
|
}
|
|
sbyte result = Sub((sbyte)data, PeekOperandB(mode, register), (mode != 1), true, false);
|
|
SetOperandB(mode, register, result);
|
|
switch (mode)
|
|
{
|
|
case 0: this.m_Cycles += 4; break;
|
|
case 1: this.m_Cycles += 8; break;
|
|
default: this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register); break;
|
|
}
|
|
return;
|
|
}
|
|
case 1: // W
|
|
{
|
|
if (mode == 1)
|
|
{
|
|
int result = Sub(data, PeekOperandL(mode, register), false, true, false);
|
|
SetOperandL(mode, register, result);
|
|
}
|
|
else
|
|
{
|
|
short result = Sub((short)data, PeekOperandW(mode, register), true, true, false);
|
|
SetOperandW(mode, register, result);
|
|
}
|
|
switch (mode)
|
|
{
|
|
case 0: this.m_Cycles += 4; break;
|
|
case 1: this.m_Cycles += 8; break;
|
|
default: this.m_Cycles += 8 + Helpers.EACalcTimeBW(mode, register); break;
|
|
}
|
|
return;
|
|
}
|
|
case 2: // L
|
|
{
|
|
int result = Sub(data, PeekOperandL(mode, register), (mode != 1), true, false);
|
|
SetOperandL(mode, register, result);
|
|
switch (mode)
|
|
{
|
|
case 0: this.m_Cycles += 8; break;
|
|
case 1: this.m_Cycles += 8; break;
|
|
default: this.m_Cycles += 12 + Helpers.EACalcTimeL(mode, register); break;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SUBX()
|
|
{
|
|
int regRx = (this.m_IR >> 9) & 0x7;
|
|
int size = (this.m_IR >> 6) & 0x3;
|
|
int rm = (this.m_IR >> 3) & 0x1;
|
|
int regRy = this.m_IR & 0x7;
|
|
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
{
|
|
if (rm == 0)
|
|
{
|
|
this.m_D[regRy] = Sub((sbyte)this.m_D[regRx], (sbyte)this.m_D[regRy], true, true, true);
|
|
this.m_Cycles += 4;
|
|
}
|
|
else
|
|
{
|
|
WriteB(this.m_A[regRy], Sub(ReadB(this.m_A[regRx]), ReadB(this.m_A[regRy]), true, true, true));
|
|
this.m_Cycles += 18;
|
|
}
|
|
return;
|
|
}
|
|
case 1:
|
|
{
|
|
if (rm == 0)
|
|
{
|
|
this.m_D[regRy] = Sub((short)this.m_D[regRx], (short)this.m_D[regRy], true, true, true);
|
|
this.m_Cycles += 4;
|
|
}
|
|
else
|
|
{
|
|
WriteW(this.m_A[regRy], Sub(ReadW(this.m_A[regRx]), ReadW(this.m_A[regRy]), true, true, true));
|
|
this.m_Cycles += 18;
|
|
}
|
|
return;
|
|
}
|
|
case 2:
|
|
{
|
|
if (rm == 0)
|
|
{
|
|
this.m_D[regRy] = Sub(this.m_D[regRx], this.m_D[regRy], true, true, true);
|
|
this.m_Cycles += 8;
|
|
}
|
|
else
|
|
{
|
|
WriteL(this.m_A[regRy], Sub(ReadB(this.m_A[regRx]), ReadB(this.m_A[regRy]), true, true, true));
|
|
this.m_Cycles += 30;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|