1937 lines
65 KiB
C#
1937 lines
65 KiB
C#
using System.Runtime.CompilerServices;
|
|
using static OptimeGBA.Bits;
|
|
using static Util;
|
|
|
|
namespace OptimeGBA
|
|
{
|
|
public delegate void ArmExecutor(Arm7 arm7, uint ins);
|
|
|
|
public unsafe static class Arm
|
|
{
|
|
public static uint popcount_8(uint n)
|
|
{
|
|
uint m = 0x01010101;
|
|
uint c = n & m;
|
|
int i;
|
|
for (i = 0; i < 7; i++)
|
|
{
|
|
n >>= 1;
|
|
c += n & m;
|
|
}
|
|
c += c >> 8;
|
|
c += c >> 16;
|
|
return c & 0x3f;
|
|
}
|
|
public static void SWI(Arm7 arm7, uint ins)
|
|
{
|
|
arm7.SPSR_svc = arm7.GetCPSR();
|
|
arm7.SetMode(Arm7Mode.SVC); // Go into SVC / Supervisor mode
|
|
arm7.R[14] = arm7.R[15] - 4;
|
|
// arm7.ThumbState = false; // Back to ARM state
|
|
arm7.IRQDisable = true;
|
|
|
|
arm7.R[15] = arm7.VectorSoftwareInterrupt;
|
|
arm7.FlushPipeline();
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _LDMSTM(Arm7 arm7, uint ins, bool L)
|
|
{
|
|
arm7.LineDebug("LDM/STM");
|
|
|
|
bool P = BitTest(ins, 24); // post-indexed / offset addressing
|
|
bool U = BitTest(ins, 23); // invert
|
|
bool S = BitTest(ins, 22);
|
|
bool W = BitTest(ins, 21);
|
|
|
|
bool loadsPc = BitTest(ins, 15);
|
|
|
|
uint oldMode = 0;
|
|
if (S && (!L || !loadsPc))
|
|
{
|
|
oldMode = arm7.GetMode();
|
|
arm7.SetMode(Arm7Mode.USR);
|
|
}
|
|
|
|
// if (U && P && W) Error("U & P & W");
|
|
|
|
arm7.LineDebug(L ? "Load" : "Store");
|
|
arm7.LineDebug(P ? "No Include Base" : "Include Base");
|
|
arm7.LineDebug(U ? "Upwards" : "Downwards");
|
|
|
|
uint rn = (ins >> 16) & 0xF;
|
|
|
|
uint addr = arm7.R[rn];
|
|
|
|
// String regs = "";
|
|
|
|
uint bitsSet = popcount_8(ins & 0xFFFF);
|
|
uint writebackValue;
|
|
if (U)
|
|
{
|
|
if (W)
|
|
{
|
|
writebackValue = addr + bitsSet * 4;
|
|
}
|
|
else
|
|
{
|
|
writebackValue = addr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (W)
|
|
{
|
|
writebackValue = addr - bitsSet * 4;
|
|
}
|
|
else
|
|
{
|
|
writebackValue = addr;
|
|
}
|
|
if (P)
|
|
{
|
|
addr = addr - bitsSet * 4 - 4;
|
|
}
|
|
else
|
|
{
|
|
addr = addr - bitsSet * 4 + 4;
|
|
}
|
|
}
|
|
|
|
if (L)
|
|
{
|
|
if (W)
|
|
{
|
|
arm7.R[rn] = writebackValue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arm7.R[15] += 4;
|
|
}
|
|
|
|
for (byte r = 0; r < 16; r++)
|
|
{
|
|
if (BitTest(ins, r))
|
|
{
|
|
if (L)
|
|
{
|
|
if (P) addr += 4;
|
|
|
|
if (r != 15)
|
|
{
|
|
arm7.R[r] = arm7.Read32(addr & ~3U);
|
|
}
|
|
else
|
|
{
|
|
arm7.R[15] = arm7.Read32(addr & ~3U) & ~3U;
|
|
arm7.FlushPipeline();
|
|
}
|
|
|
|
if (!P) addr += 4;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (P) addr += 4;
|
|
|
|
arm7.Write32(addr & ~3U, arm7.R[r]);
|
|
|
|
if (!P) addr += 4;
|
|
|
|
arm7.R[rn] = writebackValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool emptyRlist = (ins & 0xFFFF) == 0;
|
|
if (emptyRlist)
|
|
{
|
|
if (L)
|
|
{
|
|
arm7.R[15] = arm7.Read32(addr & ~3U);
|
|
arm7.FlushPipeline();
|
|
if (U)
|
|
{
|
|
arm7.R[rn] += 0x40;
|
|
}
|
|
else
|
|
{
|
|
arm7.R[rn] -= 0x40;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug("Empty Rlist!");
|
|
if (P)
|
|
{
|
|
if (U)
|
|
{
|
|
arm7.Write32(arm7.R[rn] + 4, arm7.R[15]);
|
|
arm7.R[rn] += 0x40;
|
|
}
|
|
else
|
|
{
|
|
arm7.R[rn] -= 0x40;
|
|
arm7.Write32(arm7.R[rn], arm7.R[15]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (U)
|
|
{
|
|
arm7.Write32(arm7.R[rn], arm7.R[15]);
|
|
arm7.R[rn] += 0x40;
|
|
}
|
|
else
|
|
{
|
|
arm7.R[rn] -= 0x40;
|
|
arm7.Write32(arm7.R[rn] + 4, arm7.R[15]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!L)
|
|
{
|
|
arm7.R[15] -= 4;
|
|
}
|
|
|
|
if (S)
|
|
{
|
|
if (L && loadsPc)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
}
|
|
else
|
|
{
|
|
arm7.SetMode((Arm7Mode)oldMode);
|
|
}
|
|
}
|
|
|
|
// arm7.LineDebug(regs);
|
|
|
|
arm7.ICycle();
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _LDMSTM_V5(Arm7 arm7, uint ins, bool L)
|
|
{
|
|
arm7.LineDebug("LDM/STM ARMv5");
|
|
|
|
bool P = BitTest(ins, 24); // post-indexed / offset addressing
|
|
bool U = BitTest(ins, 23); // invert
|
|
bool S = BitTest(ins, 22);
|
|
bool W = BitTest(ins, 21);
|
|
|
|
bool loadsPc = BitTest(ins, 15);
|
|
|
|
uint oldMode = 0;
|
|
if (S && (!L || !loadsPc))
|
|
{
|
|
oldMode = arm7.GetMode();
|
|
arm7.SetMode(Arm7Mode.USR);
|
|
}
|
|
|
|
// if (U && P && W) Error("U & P & W");
|
|
|
|
arm7.LineDebug(L ? "Load" : "Store");
|
|
arm7.LineDebug(P ? "No Include Base" : "Include Base");
|
|
arm7.LineDebug(U ? "Upwards" : "Downwards");
|
|
|
|
uint rn = (ins >> 16) & 0xF;
|
|
|
|
uint addr = arm7.R[rn];
|
|
|
|
// String regs = "";
|
|
|
|
uint bitsSet = popcount_8(ins & 0xFFFF);
|
|
uint writebackValue;
|
|
if (U)
|
|
{
|
|
if (W)
|
|
{
|
|
writebackValue = addr + bitsSet * 4;
|
|
}
|
|
else
|
|
{
|
|
writebackValue = addr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (W)
|
|
{
|
|
writebackValue = addr - bitsSet * 4;
|
|
}
|
|
else
|
|
{
|
|
writebackValue = addr;
|
|
}
|
|
if (P)
|
|
{
|
|
addr = addr - bitsSet * 4 - 4;
|
|
}
|
|
else
|
|
{
|
|
addr = addr - bitsSet * 4 + 4;
|
|
}
|
|
}
|
|
|
|
if (!L)
|
|
{
|
|
arm7.R[15] += 4;
|
|
}
|
|
|
|
for (byte r = 0; r < 16; r++)
|
|
{
|
|
if (BitTest(ins, r))
|
|
{
|
|
if (L)
|
|
{
|
|
if (W)
|
|
{
|
|
arm7.R[rn] = writebackValue;
|
|
}
|
|
|
|
if (P) addr += 4;
|
|
|
|
if (r != 15)
|
|
{
|
|
arm7.R[r] = arm7.Read32(addr & ~3U);
|
|
}
|
|
else
|
|
{
|
|
arm7.R[15] = arm7.Read32(addr & ~3U);
|
|
arm7.ThumbState = BitTest(arm7.R[15], 0);
|
|
arm7.FlushPipeline();
|
|
}
|
|
|
|
if (!P) addr += 4;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (P) addr += 4;
|
|
|
|
arm7.Write32(addr & ~3U, arm7.R[r]);
|
|
|
|
if (!P) addr += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!L)
|
|
{
|
|
arm7.R[15] -= 4;
|
|
}
|
|
|
|
// ARMv5: When Rn is in Rlist, writeback happens if Rn is the only register, or not the last
|
|
// I can't figure out the order of operations so I'll just hack the only register case
|
|
if (!L || bitsSet == 1)
|
|
{
|
|
arm7.R[rn] = writebackValue;
|
|
}
|
|
|
|
bool emptyRlist = (ins & 0xFFFF) == 0;
|
|
if (emptyRlist)
|
|
{
|
|
if (U)
|
|
{
|
|
arm7.R[rn] += 0x40;
|
|
}
|
|
else
|
|
{
|
|
arm7.R[rn] -= 0x40;
|
|
}
|
|
}
|
|
|
|
if (S)
|
|
{
|
|
if (L && loadsPc)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
}
|
|
else
|
|
{
|
|
arm7.SetMode((Arm7Mode)oldMode);
|
|
}
|
|
}
|
|
|
|
// arm7.LineDebug(regs);
|
|
|
|
arm7.ICycle();
|
|
}
|
|
|
|
public static void LDM(Arm7 arm7, uint ins) { _LDMSTM(arm7, ins, true); }
|
|
public static void STM(Arm7 arm7, uint ins) { _LDMSTM(arm7, ins, false); }
|
|
public static void LDM_V5(Arm7 arm7, uint ins) { _LDMSTM_V5(arm7, ins, true); }
|
|
public static void STM_V5(Arm7 arm7, uint ins) { _LDMSTM_V5(arm7, ins, false); }
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _B(Arm7 arm7, uint ins, bool link)
|
|
{
|
|
arm7.LineDebug("B | Branch");
|
|
// B
|
|
int offset = (int)(ins & 0b111111111111111111111111) << 2;
|
|
// Signed with Two's Complement
|
|
// Cheap and easy sign-extend
|
|
offset = (offset << 6) >> 6;
|
|
|
|
// BL - store return address in R14
|
|
if (link)
|
|
{
|
|
arm7.R[14] = arm7.R[15] - 4;
|
|
}
|
|
|
|
// BLX immediate
|
|
if ((ins >> 28) == 0b1111)
|
|
{
|
|
int halfwordOffset = (int)(ins >> 24) & 0b1;
|
|
arm7.ThumbState = true;
|
|
offset += halfwordOffset * 2;
|
|
|
|
arm7.R[14] = arm7.R[15] - 4;
|
|
}
|
|
|
|
arm7.R[15] = (uint)(arm7.R[15] + offset);
|
|
arm7.FlushPipeline();
|
|
}
|
|
|
|
public static void B(Arm7 arm7, uint ins) { _B(arm7, ins, false); }
|
|
public static void BL(Arm7 arm7, uint ins) { _B(arm7, ins, true); }
|
|
|
|
public static void BX(Arm7 arm7, uint ins)
|
|
{
|
|
// BX - branch and optional switch to Thumb state
|
|
arm7.LineDebug("BX");
|
|
|
|
uint rm = ins & 0xF;
|
|
uint rmValue = arm7.R[rm];
|
|
|
|
arm7.ThumbState = BitTest(rmValue, 0);
|
|
if (arm7.ThumbState)
|
|
{
|
|
arm7.StateChange();
|
|
arm7.LineDebug("Switch to THUMB State");
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug("Switch to ARM State");
|
|
}
|
|
|
|
// BLX register
|
|
uint opcode = (ins >> 4) & 0xF;
|
|
if (opcode == 0b0011)
|
|
{
|
|
arm7.R[14] = arm7.R[15] - 4;
|
|
}
|
|
|
|
arm7.R[15] = rmValue & ~1U;
|
|
arm7.FlushPipeline();
|
|
}
|
|
|
|
public static void SWP(Arm7 arm7, uint ins)
|
|
{
|
|
uint rm = (ins >> 0) & 0xF;
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint rn = (ins >> 16) & 0xF;
|
|
|
|
uint addr = arm7.R[rn];
|
|
uint storeValue = arm7.R[rm];
|
|
|
|
arm7.LineDebug("SWP");
|
|
uint readVal = RotateRight32(arm7.Read32(addr & ~3u), (byte)((addr & 3u) * 8));
|
|
arm7.Write32(addr & ~3u, storeValue);
|
|
arm7.R[rd] = readVal;
|
|
|
|
arm7.ICycle();
|
|
}
|
|
|
|
public static void SWPB(Arm7 arm7, uint ins)
|
|
{
|
|
uint rm = (ins >> 0) & 0xF;
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint rn = (ins >> 16) & 0xF;
|
|
|
|
uint addr = arm7.R[rn];
|
|
uint storeValue = arm7.R[rm];
|
|
|
|
arm7.LineDebug("SWPB");
|
|
byte readVal = arm7.Read8(addr);
|
|
arm7.Write8(addr, (byte)storeValue);
|
|
arm7.R[rd] = readVal;
|
|
|
|
arm7.ICycle();
|
|
}
|
|
|
|
public static void MSR(Arm7 arm7, uint ins)
|
|
{
|
|
arm7.LineDebug("MSR");
|
|
// MSR
|
|
|
|
bool useSPSR = BitTest(ins, 22);
|
|
|
|
bool setControl = BitTest(ins, 16);
|
|
bool setExtension = BitTest(ins, 17);
|
|
bool setStatus = BitTest(ins, 18);
|
|
bool setFlags = BitTest(ins, 19);
|
|
|
|
bool useImmediate = BitTest(ins, 25);
|
|
|
|
uint operand;
|
|
|
|
if (useImmediate)
|
|
{
|
|
uint rotateBits = ((ins >> 8) & 0xF) * 2;
|
|
uint constant = ins & 0xFF;
|
|
|
|
operand = RotateRight32(constant, (byte)rotateBits);
|
|
}
|
|
else
|
|
{
|
|
operand = arm7.R[ins & 0xF];
|
|
}
|
|
|
|
uint byteMask =
|
|
(setControl ? 0x000000FFu : 0) |
|
|
(setExtension ? 0x0000FF00u : 0) |
|
|
(setStatus ? 0x00FF0000u : 0) |
|
|
(setFlags ? 0xFF000000u : 0);
|
|
|
|
arm7.LineDebug($"Set Control: {setControl}");
|
|
arm7.LineDebug($"Set Extension: {setExtension}");
|
|
arm7.LineDebug($"Set Status: {setStatus}");
|
|
arm7.LineDebug($"Set Flags: {setFlags}");
|
|
|
|
if (!useSPSR)
|
|
{
|
|
// TODO: Fix privileged mode functionality in CPSR MSR
|
|
if (arm7.Mode == Arm7Mode.USR)
|
|
{
|
|
// Privileged
|
|
arm7.LineDebug("Privileged");
|
|
byteMask &= 0xFF000000;
|
|
}
|
|
arm7.SetCPSR((arm7.GetCPSR() & ~byteMask) | (operand & byteMask));
|
|
}
|
|
else
|
|
{
|
|
// TODO: Add SPSR functionality to MSR
|
|
arm7.SetSPSR((arm7.GetSPSR() & ~byteMask) | (operand & byteMask));
|
|
}
|
|
}
|
|
|
|
public static void MRS(Arm7 arm7, uint ins)
|
|
{
|
|
arm7.LineDebug("MRS");
|
|
|
|
bool useSPSR = BitTest(ins, 22);
|
|
|
|
uint rd = (ins >> 12) & 0xF;
|
|
|
|
if (useSPSR)
|
|
{
|
|
arm7.LineDebug("Rd from SPSR");
|
|
arm7.R[rd] = arm7.GetSPSR();
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug("Rd from CPSR");
|
|
arm7.R[rd] = arm7.GetCPSR();
|
|
}
|
|
}
|
|
|
|
public static void MUL(Arm7 arm7, uint ins)
|
|
{
|
|
uint rd = (ins >> 16) & 0xF;
|
|
uint rs = (ins >> 8) & 0xF;
|
|
uint rm = (ins >> 0) & 0xF;
|
|
uint rsValue = arm7.R[rs];
|
|
uint rmValue = arm7.R[rm];
|
|
|
|
arm7.LineDebug($"R{rm} * R{rs}");
|
|
arm7.LineDebug($"${Util.HexN(rmValue, 8)} * ${Util.HexN(rsValue, 8)}");
|
|
|
|
bool setFlags = BitTest(ins, 20);
|
|
|
|
uint final;
|
|
if (BitTest(ins, 21))
|
|
{
|
|
uint rnValue = arm7.R[(ins >> 12) & 0xF];
|
|
arm7.LineDebug("Multiply Accumulate");
|
|
final = (rsValue * rmValue) + rnValue;
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug("Multiply Regular");
|
|
final = rsValue * rmValue;
|
|
}
|
|
arm7.R[rd] = final;
|
|
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(final, 31);
|
|
arm7.Zero = final == 0;
|
|
}
|
|
}
|
|
|
|
public static void MULL(Arm7 arm7, uint ins)
|
|
{
|
|
bool signed = BitTest(ins, 22);
|
|
bool accumulate = BitTest(ins, 21);
|
|
bool setFlags = BitTest(ins, 20);
|
|
|
|
uint rdHi = (ins >> 16) & 0xF;
|
|
uint rdLo = (ins >> 12) & 0xF;
|
|
uint rs = (ins >> 8) & 0xF;
|
|
uint rm = (ins >> 0) & 0xF;
|
|
ulong rsVal = arm7.R[rs];
|
|
ulong rmVal = arm7.R[rm];
|
|
|
|
arm7.LineDebug("Multiply Long");
|
|
|
|
ulong longLo;
|
|
ulong longHi;
|
|
if (accumulate)
|
|
{
|
|
arm7.LineDebug("Accumulate");
|
|
|
|
if (signed)
|
|
{
|
|
// SMLAL
|
|
long rmValExt = (long)(rmVal << 32) >> 32;
|
|
long rsValExt = (long)(rsVal << 32) >> 32;
|
|
|
|
longLo = (ulong)(((rsValExt * rmValExt) & 0xFFFFFFFF) + arm7.R[rdLo]);
|
|
longHi = (ulong)((rsValExt * rmValExt) >> 32) + arm7.R[rdHi] + (longLo > 0xFFFFFFFF ? 1U : 0);
|
|
}
|
|
else
|
|
{
|
|
// UMLAL
|
|
longLo = ((rsVal * rmVal) & 0xFFFFFFFF) + arm7.R[rdLo];
|
|
longHi = ((rsVal * rmVal) >> 32) + arm7.R[rdHi] + (longLo > 0xFFFFFFFF ? 1U : 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug("No Accumulate");
|
|
|
|
if (signed)
|
|
{
|
|
// SMULL
|
|
long rmValExt = (long)(rmVal << 32) >> 32;
|
|
long rsValExt = (long)(rsVal << 32) >> 32;
|
|
|
|
longLo = (ulong)((rsValExt * rmValExt));
|
|
longHi = (ulong)((rsValExt * rmValExt) >> 32);
|
|
}
|
|
else
|
|
{
|
|
// UMULL
|
|
longLo = (rmVal * rsVal);
|
|
longHi = ((rmVal * rsVal) >> 32);
|
|
}
|
|
}
|
|
|
|
arm7.LineDebug($"RdLo: R{rdLo}");
|
|
arm7.LineDebug($"RdHi: R{rdHi}");
|
|
arm7.LineDebug($"Rm: R{rm}");
|
|
arm7.LineDebug($"Rs: R{rs}");
|
|
|
|
arm7.R[rdLo] = (uint)longLo;
|
|
arm7.R[rdHi] = (uint)longHi;
|
|
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest((uint)longHi, 31);
|
|
arm7.Zero = arm7.R[rdLo] == 0 && arm7.R[rdHi] == 0;
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _RegularLDRSTR(Arm7 arm7, uint ins, bool L, bool useRegister)
|
|
{
|
|
// LDR (Load Register)
|
|
arm7.LineDebug("LDR (Load Register)");
|
|
|
|
uint rn = (ins >> 16) & 0xF;
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint rnValue = arm7.R[rn];
|
|
|
|
bool P = BitTest(ins, 24); // post-indexed / offset addressing
|
|
bool U = BitTest(ins, 23); // invert
|
|
bool B = BitTest(ins, 22);
|
|
bool W = BitTest(ins, 21);
|
|
|
|
uint offset = 0;
|
|
|
|
if (useRegister)
|
|
{
|
|
// Register offset
|
|
arm7.LineDebug($"Register Offset");
|
|
uint rmVal = arm7.R[ins & 0xF];
|
|
|
|
if ((ins & 0b111111110000) == 0b000000000000)
|
|
{
|
|
arm7.LineDebug($"Non-scaled");
|
|
offset = rmVal;
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug($"Scaled");
|
|
|
|
uint shiftType = (ins >> 5) & 0b11;
|
|
byte shiftBits = (byte)((ins >> 7) & 0b11111);
|
|
switch (shiftType)
|
|
{
|
|
case 0b00:
|
|
offset = LogicalShiftLeft32(rmVal, shiftBits);
|
|
break;
|
|
case 0b01:
|
|
if (shiftBits == 0)
|
|
{
|
|
offset = 0;
|
|
}
|
|
else
|
|
{
|
|
offset = LogicalShiftRight32(rmVal, shiftBits);
|
|
}
|
|
break;
|
|
case 0b10:
|
|
if (shiftBits == 0)
|
|
{
|
|
// if (BitTest(rmVal, 31))
|
|
// {
|
|
// return 0xFFFFFFFF;
|
|
// }
|
|
// else
|
|
// {
|
|
// return 0;
|
|
// }
|
|
offset = (uint)((int)rmVal >> 31);
|
|
}
|
|
else
|
|
{
|
|
offset = ArithmeticShiftRight32(rmVal, shiftBits);
|
|
}
|
|
break;
|
|
default:
|
|
case 0b11:
|
|
if (shiftBits == 0)
|
|
{
|
|
offset = LogicalShiftLeft32(arm7.Carry ? 1U : 0, 31) | (LogicalShiftRight32(rmVal, 1));
|
|
}
|
|
else
|
|
{
|
|
offset = RotateRight32(rmVal, shiftBits);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// Immediate offset
|
|
arm7.LineDebug($"Immediate Offset");
|
|
|
|
// if (L && U && !registerOffset && rd == 0 && (ins & 0b111111111111) == 0) Error("sdfsdf");
|
|
|
|
// This IS NOT A SHIFTED 32-BIT IMMEDIATE, IT'S PLAIN 12-BIT!
|
|
offset = ins & 0b111111111111;
|
|
}
|
|
|
|
uint addr = rnValue;
|
|
if (P)
|
|
{
|
|
if (U)
|
|
{
|
|
addr += offset;
|
|
}
|
|
else
|
|
{
|
|
addr -= offset;
|
|
}
|
|
}
|
|
|
|
if (L)
|
|
{
|
|
arm7.LineDebug($"Rn: R{rn}");
|
|
arm7.LineDebug($"Rd: R{rd}");
|
|
|
|
uint loadVal = 0;
|
|
if (B)
|
|
{
|
|
loadVal = arm7.Read8(addr);
|
|
}
|
|
else
|
|
{
|
|
|
|
if ((addr & 0b11) != 0)
|
|
{
|
|
|
|
// If the address isn't word-aligned
|
|
uint data = arm7.Read32(addr & ~3U);
|
|
loadVal = RotateRight32(data, (byte)(8 * (addr & 0b11)));
|
|
|
|
// Error("Misaligned LDR");
|
|
}
|
|
else
|
|
{
|
|
loadVal = arm7.Read32(addr);
|
|
}
|
|
}
|
|
|
|
arm7.LineDebug($"LDR Addr: {Util.Hex(addr, 8)}");
|
|
arm7.LineDebug($"LDR Value: {Util.Hex(loadVal, 8)}");
|
|
|
|
if (!P)
|
|
{
|
|
if (U)
|
|
{
|
|
addr += offset;
|
|
}
|
|
else
|
|
{
|
|
addr -= offset;
|
|
}
|
|
|
|
arm7.R[rn] = addr;
|
|
}
|
|
else if (W)
|
|
{
|
|
arm7.R[rn] = addr;
|
|
}
|
|
|
|
// Register loading happens after writeback, so if writeback register and Rd are the same,
|
|
// the writeback value would be overwritten by Rd.
|
|
arm7.R[rd] = loadVal;
|
|
|
|
if (rd == 15)
|
|
{
|
|
if (arm7.Armv5)
|
|
{
|
|
arm7.ThumbState = BitTest(loadVal, 0);
|
|
}
|
|
arm7.FlushPipeline();
|
|
}
|
|
|
|
arm7.ICycle();
|
|
}
|
|
else
|
|
{
|
|
arm7.R[15] += 4;
|
|
|
|
uint storeVal = arm7.R[rd];
|
|
if (B)
|
|
{
|
|
arm7.Write8(addr, (byte)storeVal);
|
|
}
|
|
else
|
|
{
|
|
arm7.Write32(addr & 0xFFFFFFFC, storeVal);
|
|
}
|
|
|
|
arm7.LineDebug($"STR Addr: {Util.Hex(addr, 8)}");
|
|
arm7.LineDebug($"STR Value: {Util.Hex(storeVal, 8)}");
|
|
|
|
arm7.R[15] -= 4;
|
|
|
|
if (!P)
|
|
{
|
|
if (U)
|
|
{
|
|
addr += offset;
|
|
}
|
|
else
|
|
{
|
|
addr -= offset;
|
|
}
|
|
|
|
arm7.R[rn] = addr;
|
|
}
|
|
else if (W)
|
|
{
|
|
arm7.R[rn] = addr;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void RegularLDR_Reg(Arm7 arm7, uint ins) { _RegularLDRSTR(arm7, ins, true, true); }
|
|
public static void RegularSTR_Reg(Arm7 arm7, uint ins) { _RegularLDRSTR(arm7, ins, false, true); }
|
|
public static void RegularLDR_Imm(Arm7 arm7, uint ins) { _RegularLDRSTR(arm7, ins, true, false); }
|
|
public static void RegularSTR_Imm(Arm7 arm7, uint ins) { _RegularLDRSTR(arm7, ins, false, false); }
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _SpecialLDRSTR(Arm7 arm7, uint ins, bool L, bool S, bool H)
|
|
{
|
|
arm7.LineDebug("Halfword, Signed Byte, Doubleword Loads & Stores");
|
|
arm7.LineDebug("LDR|STR H|SH|SB|D");
|
|
|
|
bool W = BitTest(ins, 21); // Writeback to base register
|
|
bool immediateOffset = BitTest(ins, 22);
|
|
bool U = BitTest(ins, 23); // Add / Subtract offset
|
|
bool P = BitTest(ins, 24); // Use post-indexed / offset or pre-indexed
|
|
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint rn = (ins >> 16) & 0xF;
|
|
|
|
uint baseAddr = arm7.R[rn];
|
|
|
|
uint offset;
|
|
if (immediateOffset)
|
|
{
|
|
arm7.LineDebug("Immediate Offset");
|
|
uint immed = (ins & 0xF) | ((ins >> 4) & 0xF0);
|
|
offset = immed;
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug("Register Offset");
|
|
uint rm = ins & 0xF;
|
|
offset = arm7.R[rm];
|
|
}
|
|
|
|
uint addr = baseAddr;
|
|
if (P)
|
|
{
|
|
if (U)
|
|
{
|
|
addr += offset;
|
|
}
|
|
else
|
|
{
|
|
addr -= offset;
|
|
}
|
|
}
|
|
|
|
uint loadVal = 0;
|
|
if (L)
|
|
{
|
|
if (S)
|
|
{
|
|
if (H)
|
|
{
|
|
arm7.LineDebug("Load signed halfword");
|
|
|
|
int readVal;
|
|
if ((addr & 1) != 0)
|
|
{
|
|
// Misaligned, read byte instead.
|
|
// Sign extend
|
|
readVal = (sbyte)arm7.Read8(addr);
|
|
}
|
|
else
|
|
{
|
|
// Sign extend
|
|
readVal = (short)arm7.Read16(addr);
|
|
}
|
|
loadVal = (uint)readVal;
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug("Load signed byte");
|
|
|
|
int val = (sbyte)arm7.Read8(addr);
|
|
|
|
loadVal = (uint)val;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (H)
|
|
{
|
|
arm7.LineDebug("Load unsigned halfword");
|
|
// Force halfword aligned, and rotate if unaligned
|
|
loadVal = RotateRight32(arm7.Read16(addr & ~1u), (byte)((addr & 1) * 8));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (S)
|
|
{
|
|
if (arm7.Armv5)
|
|
{
|
|
if (H)
|
|
{
|
|
arm7.LineDebug("Store doubleword");
|
|
arm7.Error($"UNIMPLEMENTED R15:{Hex(arm7.R[15], 8)} OPCODE:{Hex(ins, 8)} STRD");
|
|
}
|
|
else
|
|
{
|
|
arm7.LineDebug("Load doubleword");
|
|
arm7.Error($"UNIMPLEMENTED R15:{Hex(arm7.R[15], 8)} OPCODE:{Hex(ins, 8)} LDRD");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (H)
|
|
{
|
|
arm7.LineDebug("Store halfword");
|
|
arm7.Write16(addr & ~1u, (ushort)arm7.R[rd]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!P)
|
|
{
|
|
if (U)
|
|
{
|
|
addr = baseAddr + offset;
|
|
}
|
|
else
|
|
{
|
|
addr = baseAddr - offset;
|
|
}
|
|
}
|
|
|
|
if (W || !P)
|
|
{
|
|
arm7.R[rn] = addr;
|
|
}
|
|
|
|
if (L)
|
|
{
|
|
arm7.R[rd] = loadVal;
|
|
arm7.ICycle();
|
|
}
|
|
|
|
arm7.LineDebug($"Writeback: {(W ? "Yes" : "No")}");
|
|
arm7.LineDebug($"Offset / pre-indexed addressing: {(P ? "Yes" : "No")}");
|
|
}
|
|
|
|
// In order mentioned in ARM architecture reference manual for ARMv5
|
|
public static void STRH(Arm7 arm7, uint ins) { _SpecialLDRSTR(arm7, ins, false, false, true); }
|
|
public static void LDRD(Arm7 arm7, uint ins) { _SpecialLDRSTR(arm7, ins, false, true, false); }
|
|
public static void STRD(Arm7 arm7, uint ins) { _SpecialLDRSTR(arm7, ins, false, true, true); }
|
|
public static void LDRH(Arm7 arm7, uint ins) { _SpecialLDRSTR(arm7, ins, true, false, true); }
|
|
public static void LDRSB(Arm7 arm7, uint ins) { _SpecialLDRSTR(arm7, ins, true, true, false); }
|
|
public static void LDRSH(Arm7 arm7, uint ins) { _SpecialLDRSTR(arm7, ins, true, true, true); }
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static (uint shifterOperand, bool shifterCarryOut, uint rnVal, uint rd) DataDecode(Arm7 arm7, uint ins, bool useImmediate32)
|
|
{
|
|
uint rd = (ins >> 12) & 0xF; // Rd, SBZ for CMP
|
|
|
|
// ----- When using register as 2nd operand -----
|
|
// Shift by immediate or shift by register
|
|
uint shifterOperand = 0;
|
|
bool shifterCarryOut = false;
|
|
|
|
if (useImmediate32)
|
|
{
|
|
uint rn = (ins >> 16) & 0xF; // Rn
|
|
// uint rs = (ins >> 8) & 0xF;
|
|
// uint rm = ins & 0xF;
|
|
uint rnVal = arm7.R[rn];
|
|
// uint rsVal = R[rs];
|
|
// uint rmVal = R[rm];
|
|
|
|
uint rotateBits = ((ins >> 8) & 0xF) * 2;
|
|
uint constant = ins & 0xFF;
|
|
|
|
shifterOperand = RotateRight32(constant, (byte)rotateBits);
|
|
if (rotateBits == 0)
|
|
{
|
|
shifterCarryOut = arm7.Carry;
|
|
}
|
|
else
|
|
{
|
|
shifterCarryOut = BitTest(shifterOperand, 31);
|
|
}
|
|
|
|
arm7.LineDebug($"Immediate32: {Util.Hex(shifterOperand, 8)}");
|
|
|
|
return (shifterOperand, shifterCarryOut, rnVal, rd);
|
|
}
|
|
else
|
|
{
|
|
bool regShift = (ins & BIT_4) != 0;
|
|
|
|
byte shiftBits;
|
|
uint shiftType = (ins >> 5) & 0b11;
|
|
|
|
if (!regShift)
|
|
{
|
|
// Immediate Shift
|
|
arm7.LineDebug("Immediate Shift");
|
|
shiftBits = (byte)((ins >> 7) & 0b11111);
|
|
|
|
uint rn = (ins >> 16) & 0xF; // Rn
|
|
// uint rs = (ins >> 8) & 0xF;
|
|
uint rm = ins & 0xF;
|
|
uint rnVal = arm7.R[rn];
|
|
// uint rsVal = R[rs];
|
|
uint rmVal = arm7.R[rm];
|
|
|
|
switch (shiftType)
|
|
{
|
|
case 0b00: // LSL
|
|
if (shiftBits == 0)
|
|
{
|
|
shifterOperand = rmVal;
|
|
shifterCarryOut = arm7.Carry;
|
|
}
|
|
else
|
|
{
|
|
shifterOperand = LogicalShiftLeft32(rmVal, shiftBits);
|
|
shifterCarryOut = BitTest(rmVal, (byte)(32 - shiftBits));
|
|
}
|
|
break;
|
|
case 0b01: // LSR
|
|
if (shiftBits == 0)
|
|
{
|
|
shifterOperand = 0;
|
|
shifterCarryOut = BitTest(rmVal, 31);
|
|
}
|
|
else
|
|
{
|
|
shifterOperand = LogicalShiftRight32(rmVal, shiftBits);
|
|
shifterCarryOut = BitTest(rmVal, (byte)(shiftBits - 1));
|
|
}
|
|
break;
|
|
case 0b10: // ASR
|
|
if (shiftBits == 0)
|
|
{
|
|
shifterOperand = (uint)((int)rmVal >> 31);
|
|
shifterCarryOut = BitTest(rmVal, 31);
|
|
}
|
|
else
|
|
{
|
|
shifterOperand = ArithmeticShiftRight32(rmVal, shiftBits);
|
|
shifterCarryOut = BitTest(rmVal, (byte)(shiftBits - 1));
|
|
}
|
|
break;
|
|
case 0b11: // ROR
|
|
if (shiftBits == 0)
|
|
{
|
|
shifterOperand = LogicalShiftLeft32(arm7.Carry ? 1U : 0, 31) | LogicalShiftRight32(rmVal, 1);
|
|
shifterCarryOut = BitTest(rmVal, 0);
|
|
}
|
|
else
|
|
{
|
|
shifterOperand = RotateRight32(rmVal, shiftBits);
|
|
shifterCarryOut = BitTest(rmVal, (byte)(shiftBits - 1));
|
|
}
|
|
break;
|
|
}
|
|
|
|
return (shifterOperand, shifterCarryOut, rnVal, rd);
|
|
}
|
|
else
|
|
{
|
|
// Register shift
|
|
arm7.LineDebug("Register Shift");
|
|
|
|
uint rn = (ins >> 16) & 0xF; // Rn
|
|
uint rs = (ins >> 8) & 0xF;
|
|
uint rm = ins & 0xF;
|
|
arm7.LineDebug("RS: " + rs);
|
|
|
|
arm7.ICycle();
|
|
|
|
arm7.R[15] += 4;
|
|
uint rnVal = arm7.R[rn];
|
|
uint rsVal = arm7.R[rs];
|
|
uint rmVal = arm7.R[rm];
|
|
arm7.R[15] -= 4;
|
|
|
|
shiftBits = (byte)rsVal;
|
|
|
|
switch (shiftType)
|
|
{
|
|
case 0b00:
|
|
if (shiftBits == 0)
|
|
{
|
|
shifterOperand = rmVal;
|
|
shifterCarryOut = arm7.Carry;
|
|
break;
|
|
}
|
|
|
|
if (shiftBits >= 32)
|
|
{
|
|
if (shiftBits > 32)
|
|
{
|
|
shifterCarryOut = false;
|
|
}
|
|
else
|
|
{
|
|
shifterCarryOut = BitTest(rmVal, 0);
|
|
}
|
|
shifterOperand = 0;
|
|
break;
|
|
}
|
|
|
|
shifterOperand = rmVal << shiftBits;
|
|
shifterCarryOut = BitTest(rmVal, (byte)(32 - shiftBits));
|
|
break;
|
|
case 0b01:
|
|
if (shiftBits == 0)
|
|
{
|
|
shifterOperand = rmVal;
|
|
shifterCarryOut = arm7.Carry;
|
|
}
|
|
else if (shiftBits < 32)
|
|
{
|
|
shifterOperand = LogicalShiftRight32(rmVal, shiftBits);
|
|
shifterCarryOut = BitTest(rmVal, (byte)(shiftBits - 1));
|
|
}
|
|
else if (shiftBits == 32)
|
|
{
|
|
shifterOperand = 0;
|
|
shifterCarryOut = BitTest(rmVal, 31);
|
|
}
|
|
else
|
|
{
|
|
shifterOperand = 0;
|
|
shifterCarryOut = false;
|
|
}
|
|
break;
|
|
case 0b10:
|
|
if (shiftBits == 0)
|
|
{
|
|
shifterOperand = rmVal;
|
|
shifterCarryOut = arm7.Carry;
|
|
}
|
|
else if (shiftBits < 32)
|
|
{
|
|
shifterOperand = ArithmeticShiftRight32(rmVal, shiftBits);
|
|
shifterCarryOut = BitTest(rmVal, (byte)(shiftBits - 1));
|
|
}
|
|
else if (shiftBits >= 32)
|
|
{
|
|
shifterOperand = (uint)((int)rmVal >> 31);
|
|
shifterCarryOut = BitTest(rmVal, 31);
|
|
}
|
|
break;
|
|
case 0b11:
|
|
if (shiftBits == 0)
|
|
{
|
|
shifterOperand = rmVal;
|
|
shifterCarryOut = arm7.Carry;
|
|
}
|
|
else
|
|
{
|
|
shifterOperand = RotateRight32(rmVal, (byte)(shiftBits & 0b11111));
|
|
shifterCarryOut = BitTest(rmVal, (byte)((shiftBits & 0b11111) - 1));
|
|
}
|
|
break;
|
|
}
|
|
|
|
return (shifterOperand, shifterCarryOut, rnVal, rd);
|
|
}
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataAND(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("AND");
|
|
|
|
uint final = rnValue & shifterOperand;
|
|
arm7.R[rd] = final;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(final, 31);
|
|
arm7.Zero = final == 0;
|
|
arm7.Carry = shifterCarryOut;
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataEOR(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("EOR");
|
|
|
|
uint final = rnValue ^ shifterOperand;
|
|
arm7.R[rd] = final;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(final, 31);
|
|
arm7.Zero = final == 0;
|
|
arm7.Carry = shifterCarryOut;
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataSUB(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("SUB");
|
|
|
|
uint aluOut = rnValue - shifterOperand;
|
|
|
|
arm7.R[rd] = aluOut;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(aluOut, 31); // N
|
|
arm7.Zero = aluOut == 0; // Z
|
|
arm7.Carry = shifterOperand <= rnValue; // C
|
|
arm7.Overflow = Arm7.CheckOverflowSub(rnValue, shifterOperand, aluOut); // V
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataRSB(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("RSB");
|
|
|
|
uint aluOut = shifterOperand - rnValue;
|
|
|
|
arm7.R[rd] = aluOut;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(aluOut, 31); // N
|
|
arm7.Zero = aluOut == 0; // Z
|
|
arm7.Carry = rnValue <= shifterOperand; // C
|
|
arm7.Overflow = Arm7.CheckOverflowSub(shifterOperand, rnValue, aluOut); // V
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataADD(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("ADD");
|
|
|
|
uint final = rnValue + shifterOperand;
|
|
arm7.R[rd] = final;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(final, 31); // N
|
|
arm7.Zero = final == 0; // Z
|
|
arm7.Carry = (long)rnValue + (long)shifterOperand > 0xFFFFFFFFL; // C
|
|
arm7.Overflow = Arm7.CheckOverflowAdd(rnValue, shifterOperand, final); // C
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataADC(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("ADC");
|
|
|
|
uint final = rnValue + shifterOperand + (arm7.Carry ? 1U : 0);
|
|
arm7.R[rd] = final;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(final, 31); // N
|
|
arm7.Zero = final == 0; // Z
|
|
arm7.Carry = (long)rnValue + (long)shifterOperand + (arm7.Carry ? 1U : 0) > 0xFFFFFFFFL; // C
|
|
arm7.Overflow = Arm7.CheckOverflowAdd(rnValue, shifterOperand, final); // V
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataSBC(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("SBC");
|
|
|
|
uint aluOut = rnValue - shifterOperand - (!arm7.Carry ? 1U : 0U);
|
|
|
|
arm7.R[rd] = aluOut;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(aluOut, 31); // N
|
|
arm7.Zero = aluOut == 0; // Z
|
|
arm7.Carry = (long)shifterOperand + (arm7.Carry ? 0 : 1L) <= rnValue; // C
|
|
arm7.Overflow = Arm7.CheckOverflowSub(rnValue, shifterOperand, aluOut); // V
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataRSC(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("RSC");
|
|
|
|
uint aluOut = shifterOperand - rnValue - (!arm7.Carry ? 1U : 0U);
|
|
|
|
arm7.R[rd] = aluOut;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(aluOut, 31); // N
|
|
arm7.Zero = aluOut == 0; // Z
|
|
arm7.Carry = (long)rnValue + (long)(!arm7.Carry ? 1U : 0) <= shifterOperand; // C
|
|
arm7.Overflow = Arm7.CheckOverflowSub(shifterOperand, rnValue, aluOut); // V
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataTST(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("TST");
|
|
|
|
uint final = rnValue & shifterOperand;
|
|
|
|
arm7.Negative = BitTest(final, 31);
|
|
arm7.Zero = final == 0;
|
|
arm7.Carry = shifterCarryOut;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataTEQ(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("TEQ");
|
|
|
|
uint aluOut = rnValue ^ shifterOperand;
|
|
|
|
arm7.Negative = BitTest(aluOut, 31); // N
|
|
arm7.Zero = aluOut == 0; // Z
|
|
arm7.Carry = shifterCarryOut; // C
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataCMP(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
// SBZ means should be zero, not relevant to the current code, just so you know
|
|
arm7.LineDebug("CMP");
|
|
|
|
uint aluOut = rnValue - shifterOperand;
|
|
|
|
arm7.Negative = BitTest(aluOut, 31); // N
|
|
arm7.Zero = aluOut == 0; // Z
|
|
arm7.Carry = rnValue >= shifterOperand; // C
|
|
arm7.Overflow = Arm7.CheckOverflowSub(rnValue, shifterOperand, aluOut); // V
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataCMN(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("CMN");
|
|
|
|
uint aluOut = rnValue + shifterOperand;
|
|
|
|
arm7.Negative = BitTest(aluOut, 31); // N
|
|
arm7.Zero = aluOut == 0; // Z
|
|
arm7.Carry = (long)rnValue + (long)shifterOperand > 0xFFFFFFFF; // C
|
|
arm7.Overflow = Arm7.CheckOverflowAdd(rnValue, shifterOperand, aluOut); // V
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataORR(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("ORR");
|
|
|
|
uint final = rnValue | shifterOperand;
|
|
arm7.R[rd] = final;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(final, 31);
|
|
arm7.Zero = final == 0;
|
|
arm7.Carry = shifterCarryOut;
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataMOV(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("MOV");
|
|
|
|
arm7.R[rd] /*Rd*/ = shifterOperand;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(shifterOperand, 31); // N
|
|
arm7.Zero = shifterOperand == 0; // Z
|
|
arm7.Carry = shifterCarryOut; // C
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataBIC(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("BIC");
|
|
|
|
uint final = rnValue & ~shifterOperand;
|
|
arm7.R[rd] = final;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(final, 31); // N
|
|
arm7.Zero = final == 0; // Z
|
|
arm7.Carry = shifterCarryOut; // C
|
|
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static void _DataMVN(Arm7 arm7, uint ins, bool useImmediate32, bool setFlags)
|
|
{
|
|
(uint shifterOperand, bool shifterCarryOut, uint rnValue, uint rd) = DataDecode(arm7, ins, useImmediate32);
|
|
|
|
arm7.LineDebug("MVN");
|
|
|
|
arm7.R[rd] /*Rd*/ = ~shifterOperand;
|
|
if (setFlags)
|
|
{
|
|
arm7.Negative = BitTest(~shifterOperand, 31); // N
|
|
arm7.Zero = ~shifterOperand == 0; // Z
|
|
arm7.Carry = shifterCarryOut; ; // C
|
|
if (rd == 15)
|
|
{
|
|
arm7.SetCPSR(arm7.GetSPSR());
|
|
arm7.FlushPipeline();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rd == 15) arm7.FlushPipeline();
|
|
}
|
|
}
|
|
|
|
// Reducing branches during runtime, the private functions beginning
|
|
// with an underline are marked for inlining
|
|
public static void DataAND_Reg(Arm7 arm7, uint ins) { _DataAND(arm7, ins, false, false); }
|
|
public static void DataEOR_Reg(Arm7 arm7, uint ins) { _DataEOR(arm7, ins, false, false); }
|
|
public static void DataSUB_Reg(Arm7 arm7, uint ins) { _DataSUB(arm7, ins, false, false); }
|
|
public static void DataRSB_Reg(Arm7 arm7, uint ins) { _DataRSB(arm7, ins, false, false); }
|
|
public static void DataADD_Reg(Arm7 arm7, uint ins) { _DataADD(arm7, ins, false, false); }
|
|
public static void DataADC_Reg(Arm7 arm7, uint ins) { _DataADC(arm7, ins, false, false); }
|
|
public static void DataSBC_Reg(Arm7 arm7, uint ins) { _DataSBC(arm7, ins, false, false); }
|
|
public static void DataRSC_Reg(Arm7 arm7, uint ins) { _DataRSC(arm7, ins, false, false); }
|
|
public static void DataTST_Reg(Arm7 arm7, uint ins) { _DataTST(arm7, ins, false, false); }
|
|
public static void DataTEQ_Reg(Arm7 arm7, uint ins) { _DataTEQ(arm7, ins, false, false); }
|
|
public static void DataCMP_Reg(Arm7 arm7, uint ins) { _DataCMP(arm7, ins, false, false); }
|
|
public static void DataCMN_Reg(Arm7 arm7, uint ins) { _DataCMN(arm7, ins, false, false); }
|
|
public static void DataORR_Reg(Arm7 arm7, uint ins) { _DataORR(arm7, ins, false, false); }
|
|
public static void DataMOV_Reg(Arm7 arm7, uint ins) { _DataMOV(arm7, ins, false, false); }
|
|
public static void DataBIC_Reg(Arm7 arm7, uint ins) { _DataBIC(arm7, ins, false, false); }
|
|
public static void DataMVN_Reg(Arm7 arm7, uint ins) { _DataMVN(arm7, ins, false, false); }
|
|
|
|
public static void DataAND_Imm(Arm7 arm7, uint ins) { _DataAND(arm7, ins, true, false); }
|
|
public static void DataEOR_Imm(Arm7 arm7, uint ins) { _DataEOR(arm7, ins, true, false); }
|
|
public static void DataSUB_Imm(Arm7 arm7, uint ins) { _DataSUB(arm7, ins, true, false); }
|
|
public static void DataRSB_Imm(Arm7 arm7, uint ins) { _DataRSB(arm7, ins, true, false); }
|
|
public static void DataADD_Imm(Arm7 arm7, uint ins) { _DataADD(arm7, ins, true, false); }
|
|
public static void DataADC_Imm(Arm7 arm7, uint ins) { _DataADC(arm7, ins, true, false); }
|
|
public static void DataSBC_Imm(Arm7 arm7, uint ins) { _DataSBC(arm7, ins, true, false); }
|
|
public static void DataRSC_Imm(Arm7 arm7, uint ins) { _DataRSC(arm7, ins, true, false); }
|
|
public static void DataTST_Imm(Arm7 arm7, uint ins) { _DataTST(arm7, ins, true, false); }
|
|
public static void DataTEQ_Imm(Arm7 arm7, uint ins) { _DataTEQ(arm7, ins, true, false); }
|
|
public static void DataCMP_Imm(Arm7 arm7, uint ins) { _DataCMP(arm7, ins, true, false); }
|
|
public static void DataCMN_Imm(Arm7 arm7, uint ins) { _DataCMN(arm7, ins, true, false); }
|
|
public static void DataORR_Imm(Arm7 arm7, uint ins) { _DataORR(arm7, ins, true, false); }
|
|
public static void DataMOV_Imm(Arm7 arm7, uint ins) { _DataMOV(arm7, ins, true, false); }
|
|
public static void DataBIC_Imm(Arm7 arm7, uint ins) { _DataBIC(arm7, ins, true, false); }
|
|
public static void DataMVN_Imm(Arm7 arm7, uint ins) { _DataMVN(arm7, ins, true, false); }
|
|
|
|
public static void DataANDS_Reg(Arm7 arm7, uint ins) { _DataAND(arm7, ins, false, true); }
|
|
public static void DataEORS_Reg(Arm7 arm7, uint ins) { _DataEOR(arm7, ins, false, true); }
|
|
public static void DataSUBS_Reg(Arm7 arm7, uint ins) { _DataSUB(arm7, ins, false, true); }
|
|
public static void DataRSBS_Reg(Arm7 arm7, uint ins) { _DataRSB(arm7, ins, false, true); }
|
|
public static void DataADDS_Reg(Arm7 arm7, uint ins) { _DataADD(arm7, ins, false, true); }
|
|
public static void DataADCS_Reg(Arm7 arm7, uint ins) { _DataADC(arm7, ins, false, true); }
|
|
public static void DataSBCS_Reg(Arm7 arm7, uint ins) { _DataSBC(arm7, ins, false, true); }
|
|
public static void DataRSCS_Reg(Arm7 arm7, uint ins) { _DataRSC(arm7, ins, false, true); }
|
|
public static void DataTSTS_Reg(Arm7 arm7, uint ins) { _DataTST(arm7, ins, false, true); }
|
|
public static void DataTEQS_Reg(Arm7 arm7, uint ins) { _DataTEQ(arm7, ins, false, true); }
|
|
public static void DataCMPS_Reg(Arm7 arm7, uint ins) { _DataCMP(arm7, ins, false, true); }
|
|
public static void DataCMNS_Reg(Arm7 arm7, uint ins) { _DataCMN(arm7, ins, false, true); }
|
|
public static void DataORRS_Reg(Arm7 arm7, uint ins) { _DataORR(arm7, ins, false, true); }
|
|
public static void DataMOVS_Reg(Arm7 arm7, uint ins) { _DataMOV(arm7, ins, false, true); }
|
|
public static void DataBICS_Reg(Arm7 arm7, uint ins) { _DataBIC(arm7, ins, false, true); }
|
|
public static void DataMVNS_Reg(Arm7 arm7, uint ins) { _DataMVN(arm7, ins, false, true); }
|
|
|
|
public static void DataANDS_Imm(Arm7 arm7, uint ins) { _DataAND(arm7, ins, true, true); }
|
|
public static void DataEORS_Imm(Arm7 arm7, uint ins) { _DataEOR(arm7, ins, true, true); }
|
|
public static void DataSUBS_Imm(Arm7 arm7, uint ins) { _DataSUB(arm7, ins, true, true); }
|
|
public static void DataRSBS_Imm(Arm7 arm7, uint ins) { _DataRSB(arm7, ins, true, true); }
|
|
public static void DataADDS_Imm(Arm7 arm7, uint ins) { _DataADD(arm7, ins, true, true); }
|
|
public static void DataADCS_Imm(Arm7 arm7, uint ins) { _DataADC(arm7, ins, true, true); }
|
|
public static void DataSBCS_Imm(Arm7 arm7, uint ins) { _DataSBC(arm7, ins, true, true); }
|
|
public static void DataRSCS_Imm(Arm7 arm7, uint ins) { _DataRSC(arm7, ins, true, true); }
|
|
public static void DataTSTS_Imm(Arm7 arm7, uint ins) { _DataTST(arm7, ins, true, true); }
|
|
public static void DataTEQS_Imm(Arm7 arm7, uint ins) { _DataTEQ(arm7, ins, true, true); }
|
|
public static void DataCMPS_Imm(Arm7 arm7, uint ins) { _DataCMP(arm7, ins, true, true); }
|
|
public static void DataCMNS_Imm(Arm7 arm7, uint ins) { _DataCMN(arm7, ins, true, true); }
|
|
public static void DataORRS_Imm(Arm7 arm7, uint ins) { _DataORR(arm7, ins, true, true); }
|
|
public static void DataMOVS_Imm(Arm7 arm7, uint ins) { _DataMOV(arm7, ins, true, true); }
|
|
public static void DataBICS_Imm(Arm7 arm7, uint ins) { _DataBIC(arm7, ins, true, true); }
|
|
public static void DataMVNS_Imm(Arm7 arm7, uint ins) { _DataMVN(arm7, ins, true, true); }
|
|
|
|
public static void MCR(Arm7 arm7, uint ins)
|
|
{
|
|
uint opcode1 = (ins >> 21) & 0x7;
|
|
uint cRn = (ins >> 16) & 0xF;
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint coproc = (ins >> 8) & 0xF;
|
|
uint opcode2 = (ins >> 5) & 0x7;
|
|
uint cRm = (ins >> 0) & 0xF;
|
|
|
|
if (coproc == 15)
|
|
{
|
|
uint rdVal = arm7.R[rd];
|
|
arm7.Cp15.TransferTo(opcode1, rdVal, cRn, cRm, opcode2);
|
|
}
|
|
}
|
|
|
|
public static void MRC(Arm7 arm7, uint ins)
|
|
{
|
|
uint opcode1 = (ins >> 21) & 0x7;
|
|
uint cRn = (ins >> 16) & 0xF;
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint coproc = (ins >> 8) & 0xF;
|
|
uint opcode2 = (ins >> 5) & 0x7;
|
|
uint cRm = (ins >> 0) & 0xF;
|
|
|
|
if (coproc == 15)
|
|
{
|
|
uint data = arm7.Cp15.TransferFrom(opcode1, cRn, cRm, opcode2);
|
|
if (rd == 15)
|
|
{
|
|
arm7.Negative = BitTest(data, 31);
|
|
arm7.Zero = BitTest(data, 30);
|
|
arm7.Carry = BitTest(data, 29);
|
|
arm7.Overflow = BitTest(data, 28);
|
|
}
|
|
else
|
|
{
|
|
arm7.R[rd] = data;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void CLZ(Arm7 arm7, uint ins)
|
|
{
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint rm = (ins >> 0) & 0xF;
|
|
|
|
uint rmVal = arm7.R[rm];
|
|
|
|
if (rmVal == 0)
|
|
{
|
|
arm7.R[rd] = 32;
|
|
}
|
|
else
|
|
{
|
|
arm7.R[rd] = 32 - popcount_8(rmVal);//zero_count
|
|
}
|
|
}
|
|
|
|
public static void QADD(Arm7 arm7, uint ins)
|
|
{
|
|
uint rm = (ins >> 0) & 0xF;
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint rn = (ins >> 16) & 0xF;
|
|
|
|
long rmVal = (int)arm7.R[rm];
|
|
long rnVal = (int)arm7.R[rn];
|
|
|
|
bool doubling = BitTest(ins, 22);
|
|
if (doubling)
|
|
{
|
|
rnVal *= 2;
|
|
|
|
if (rnVal < int.MinValue)
|
|
{
|
|
rnVal = int.MinValue;
|
|
arm7.Sticky = true;
|
|
}
|
|
if (rnVal > int.MaxValue)
|
|
{
|
|
rnVal = int.MaxValue;
|
|
arm7.Sticky = true;
|
|
}
|
|
}
|
|
|
|
long result = rmVal + rnVal;
|
|
|
|
if (result < int.MinValue)
|
|
{
|
|
result = int.MinValue;
|
|
arm7.Sticky = true;
|
|
}
|
|
if (result > int.MaxValue)
|
|
{
|
|
result = int.MaxValue;
|
|
arm7.Sticky = true;
|
|
}
|
|
|
|
arm7.R[rd] = (uint)result;
|
|
}
|
|
|
|
public static void QSUB(Arm7 arm7, uint ins)
|
|
{
|
|
uint rm = (ins >> 0) & 0xF;
|
|
uint rd = (ins >> 12) & 0xF;
|
|
uint rn = (ins >> 16) & 0xF;
|
|
|
|
long rmVal = (int)arm7.R[rm];
|
|
long rnVal = (int)arm7.R[rn];
|
|
|
|
bool doubling = BitTest(ins, 22);
|
|
if (doubling)
|
|
{
|
|
rnVal *= 2;
|
|
|
|
if (rnVal < int.MinValue)
|
|
{
|
|
rnVal = int.MinValue;
|
|
arm7.Sticky = true;
|
|
}
|
|
if (rnVal > int.MaxValue)
|
|
{
|
|
rnVal = int.MaxValue;
|
|
arm7.Sticky = true;
|
|
}
|
|
}
|
|
|
|
long result = rmVal - rnVal;
|
|
|
|
if (result < int.MinValue)
|
|
{
|
|
result = int.MinValue;
|
|
arm7.Sticky = true;
|
|
}
|
|
if (result > int.MaxValue)
|
|
{
|
|
result = int.MaxValue;
|
|
arm7.Sticky = true;
|
|
}
|
|
|
|
arm7.R[rd] = (uint)result;
|
|
}
|
|
|
|
public static void SMLALxy(Arm7 arm7, uint ins)
|
|
{
|
|
bool x = BitTest(ins, 5);
|
|
bool y = BitTest(ins, 6);
|
|
|
|
uint rm = (ins >> 0) & 0xFU;
|
|
uint rs = (ins >> 8) & 0xFU;
|
|
uint rmVal = arm7.R[rm];
|
|
uint rsVal = arm7.R[rs];
|
|
|
|
uint rdLo = (ins >> 12) & 0xFU;
|
|
uint rdHi = (ins >> 16) & 0xFU;
|
|
uint rdLoVal = arm7.R[rdLo];
|
|
uint rdHiVal = arm7.R[rdHi];
|
|
|
|
short op1;
|
|
if (!x)
|
|
op1 = (short)rmVal;
|
|
else
|
|
op1 = (short)(rmVal >> 16);
|
|
|
|
short op2;
|
|
if (!y)
|
|
op2 = (short)rsVal;
|
|
else
|
|
op2 = (short)(rsVal >> 16);
|
|
|
|
long finalVal = (((long)rdHiVal << 32) | rdLoVal) + op1 * op2;
|
|
arm7.R[rdLo] = (uint)finalVal;
|
|
arm7.R[rdHi] = (uint)(finalVal >> 32);
|
|
}
|
|
|
|
public static void SMULxy(Arm7 arm7, uint ins)
|
|
{
|
|
bool x = BitTest(ins, 5);
|
|
bool y = BitTest(ins, 6);
|
|
|
|
uint rm = (ins >> 0) & 0xFU;
|
|
uint rs = (ins >> 8) & 0xFU;
|
|
uint rmVal = arm7.R[rm];
|
|
uint rsVal = arm7.R[rs];
|
|
|
|
uint rd = (ins >> 16) & 0xFU;
|
|
|
|
short op1;
|
|
if (!x)
|
|
op1 = (short)rmVal;
|
|
else
|
|
op1 = (short)(rmVal >> 16);
|
|
|
|
short op2;
|
|
if (!y)
|
|
op2 = (short)rsVal;
|
|
else
|
|
op2 = (short)(rsVal >> 16);
|
|
|
|
arm7.R[rd] = (uint)(op1 * op2);
|
|
}
|
|
|
|
public static void SMLAxy(Arm7 arm7, uint ins)
|
|
{
|
|
bool x = BitTest(ins, 5);
|
|
bool y = BitTest(ins, 6);
|
|
|
|
uint rm = (ins >> 0) & 0xFU;
|
|
uint rs = (ins >> 8) & 0xFU;
|
|
uint rn = (ins >> 12) & 0xFU;
|
|
uint rmVal = arm7.R[rm];
|
|
uint rsVal = arm7.R[rs];
|
|
uint rnVal = arm7.R[rn];
|
|
|
|
uint rd = (ins >> 16) & 0xFU;
|
|
|
|
short op1;
|
|
if (!x)
|
|
op1 = (short)rmVal;
|
|
else
|
|
op1 = (short)(rmVal >> 16);
|
|
|
|
short op2;
|
|
if (!y)
|
|
op2 = (short)rsVal;
|
|
else
|
|
op2 = (short)(rsVal >> 16);
|
|
|
|
uint mulVal = (uint)(op1 * op2);
|
|
uint finalVal = mulVal + rnVal;
|
|
arm7.R[rd] = finalVal;
|
|
|
|
if (Arm7.CheckOverflowAdd(mulVal, rnVal, finalVal)) arm7.Sticky = true;
|
|
|
|
// arm7.Error("test");
|
|
}
|
|
|
|
|
|
public static void Invalid(Arm7 arm7, uint ins)
|
|
{
|
|
arm7.Error($"Invalid ARM Instruction: {Hex(ins, 8)}");
|
|
}
|
|
}
|
|
} |