GBA.Unity/Assets/emulator/cpu/Thumb.cs

1346 lines
41 KiB
C#
Raw Permalink Normal View History

2024-08-16 11:06:40 +08:00
using static OptimeGBA.Bits;
using static Util;
using System.Numerics;
namespace OptimeGBA
{
public delegate void ThumbExecutor(Arm7 arm7, ushort ins);
public unsafe sealed class Thumb
{
public static void MovImmediate(Arm7 arm7, ushort ins)
{
uint rd = (uint)((ins >> 8) & 0b111);
uint immed8 = ins & 0xFFu;
arm7.LineDebug("MOV | Move large immediate to register");
arm7.R[rd] = immed8;
arm7.Negative = false;
arm7.Zero = immed8 == 0;
}
public static void CmpImmediate(Arm7 arm7, ushort ins)
{
uint rd = (uint)((ins >> 8) & 0b111);
uint immed8 = ins & 0xFFu;
arm7.LineDebug("CMP (1)");
uint rnVal = arm7.R[rd];
uint alu_out = rnVal - immed8;
arm7.Negative = BitTest(alu_out, 31);
arm7.Zero = alu_out == 0;
arm7.Carry = !(immed8 > rnVal);
arm7.Overflow = Arm7.CheckOverflowSub(rnVal, immed8, alu_out);
}
public static void AddImmediate(Arm7 arm7, ushort ins)
{
uint rd = (uint)((ins >> 8) & 0b111);
uint immed8 = ins & 0xFFu;
arm7.LineDebug("ADD (2)");
uint rdVal = arm7.R[rd];
uint final = rdVal + immed8;
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
arm7.Carry = (long)rdVal + (long)immed8 > 0xFFFFFFFF;
arm7.Overflow = Arm7.CheckOverflowAdd(rdVal, immed8, final);
}
public static void SubImmediate(Arm7 arm7, ushort ins)
{
uint rd = (uint)((ins >> 8) & 0b111);
uint immed8 = ins & 0xFFu;
arm7.LineDebug("SUB (2)");
uint rdVal = arm7.R[rd];
uint final = rdVal - immed8;
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
arm7.Carry = !(immed8 > rdVal);
arm7.Overflow = Arm7.CheckOverflowSub(rdVal, immed8, final);
}
public static void DataAND(Arm7 arm7, ushort ins) // AND
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rm = (uint)((ins >> 3) & 0b111);
arm7.LineDebug("AND");
uint rdVal = arm7.R[rd];
uint rmVal = arm7.R[rm];
uint final = rdVal & rmVal;
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
}
public static void DataEOR(Arm7 arm7, ushort ins) // EOR
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rm = (uint)((ins >> 3) & 0b111);
arm7.LineDebug("EOR");
uint rdVal = arm7.R[rd];
uint rmVal = arm7.R[rm];
rdVal = rdVal ^ rmVal;
arm7.R[rd] = rdVal;
arm7.Negative = BitTest(rdVal, 31);
arm7.Zero = rdVal == 0;
}
public static void DataLSL(Arm7 arm7, ushort ins) // LSL (2)
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rm = (uint)((ins >> 3) & 0b111);
arm7.LineDebug("LSL (2) | Logical Shift Left");
uint rdValue = arm7.R[rd];
uint rsValue = arm7.R[rm];
if ((rsValue & 0xFF) == 0)
{
// Do nothing
}
else if ((rsValue & 0xFF) < 32)
{
arm7.Carry = BitTest(rdValue, (byte)(32 - (rsValue & 0xFF)));
rdValue = LogicalShiftLeft32(rdValue, (byte)(rsValue & 0xFF));
}
else if ((rsValue & 0xFF) == 32)
{
arm7.Carry = BitTest(rdValue, 0);
rdValue = 0;
}
else
{
arm7.Carry = false;
rdValue = 0;
}
arm7.R[rd] = rdValue;
arm7.Negative = BitTest(rdValue, 31);
arm7.Zero = rdValue == 0;
}
public static void DataLSR(Arm7 arm7, ushort ins) // LSR (2)
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("LSR (2)");
uint rdVal = arm7.R[rd];
uint rsVal = arm7.R[rs];
if ((rsVal & 0xFF) == 0)
{
// everything unaffected
}
else if ((rsVal & 0xFF) < 32)
{
arm7.Carry = BitTest(rdVal, (byte)((rsVal & 0xFF) - 1));
arm7.R[rd] = LogicalShiftRight32(rdVal, (byte)(rsVal & 0xFF));
}
else if ((rsVal & 0xFF) == 32)
{
arm7.Carry = BitTest(rdVal, 31);
arm7.R[rd] = 0;
}
else
{
arm7.Carry = false;
arm7.R[rd] = 0;
}
rdVal = arm7.R[rd];
arm7.Negative = BitTest(rdVal, 31);
arm7.Zero = rdVal == 0;
}
public static void DataASR(Arm7 arm7, ushort ins) // ASR (2)
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("ASR (2)");
uint rdVal = arm7.R[rd];
uint rsVal = arm7.R[rs];
if ((rsVal & 0xFF) == 0)
{
// Do nothing
}
else if ((rsVal & 0xFF) < 32)
{
arm7.Carry = BitTest(rdVal, (byte)((rsVal & 0xFF) - 1));
rdVal = ArithmeticShiftRight32(rdVal, (byte)(rsVal & 0xFF));
}
else
{
arm7.Carry = BitTest(rdVal, 31);
if (!arm7.Carry)
{
rdVal = 0;
}
else
{
rdVal = 0xFFFFFFFF;
}
}
arm7.R[rd] = rdVal;
arm7.Negative = BitTest(rdVal, 31);
arm7.Zero = rdVal == 0;
}
public static void DataADC(Arm7 arm7, ushort ins) // ADC
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
uint rdVal = arm7.R[rd];
uint rmVal = arm7.R[rm];
uint final = rdVal + rmVal + (arm7.Carry ? 1U : 0);
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = rdVal == 0;
arm7.Carry = (long)rdVal + (long)rmVal + (arm7.Carry ? 1U : 0) > 0xFFFFFFFF;
arm7.Overflow = Arm7.CheckOverflowAdd(rdVal, rmVal, final);
}
public static void DataSBC(Arm7 arm7, ushort ins) // SBC
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("SBC");
uint rdVal = arm7.R[rd];
uint rmVal = arm7.R[rm];
uint final = rdVal - rmVal - (!arm7.Carry ? 1U : 0);
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
arm7.Carry = !((long)rmVal + (!arm7.Carry ? 1U : 0) > rdVal);
arm7.Overflow = Arm7.CheckOverflowSub(rdVal, rmVal, final);
}
public static void DataROR(Arm7 arm7, ushort ins) // ROR
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("ROR");
uint rdVal = arm7.R[rd];
uint rsVal = arm7.R[rs];
if ((rsVal & 0xFF) == 0)
{
// Do nothing
}
else if ((rsVal & 0b11111) == 0)
{
arm7.Carry = BitTest(rdVal, 31);
}
else
{
arm7.Carry = BitTest(rdVal, (byte)((rsVal & 0b11111) - 1));
rdVal = RotateRight32(rdVal, (byte)(rsVal & 0b11111));
arm7.R[rd] = rdVal;
}
arm7.Negative = BitTest(rdVal, 31);
arm7.Zero = rdVal == 0;
}
public static void DataTST(Arm7 arm7, ushort ins) // TST
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("TST");
uint rnValue = arm7.R[rn];
uint rmValue = arm7.R[rm];
uint final = rnValue & rmValue;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
}
public static void DataNEG(Arm7 arm7, ushort ins) // NEG / RSB
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("NEG / RSB");
uint rdVal = arm7.R[rd];
uint rmVal = arm7.R[rm];
uint final = 0 - rmVal;
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
arm7.Carry = !(rmVal > 0);
arm7.Overflow = Arm7.CheckOverflowSub(0, rmVal, final);
}
public static void DataCMP(Arm7 arm7, ushort ins) // CMP (2)
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("CMP (2)");
uint rnVal = arm7.R[(uint)((ins >> 0) & 0b111)];
uint rmVal = arm7.R[(uint)((ins >> 3) & 0b111)];
uint alu_out = rnVal - rmVal;
arm7.Negative = BitTest(alu_out, 31);
arm7.Zero = alu_out == 0;
arm7.Carry = !(rmVal > rnVal);
arm7.Overflow = Arm7.CheckOverflowSub(rnVal, rmVal, alu_out);
}
public static void DataCMN(Arm7 arm7, ushort ins) // CMN
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("CMN");
uint rnVal = arm7.R[(uint)((ins >> 0) & 0b111)];
uint rmVal = arm7.R[(uint)((ins >> 3) & 0b111)];
uint alu_out = rnVal + rmVal;
arm7.Negative = BitTest(alu_out, 31);
arm7.Zero = alu_out == 0;
arm7.Carry = (long)rmVal + (long)rnVal > 0xFFFFFFFF;
arm7.Overflow = Arm7.CheckOverflowAdd(rnVal, rmVal, alu_out);
}
public static void DataORR(Arm7 arm7, ushort ins) // ORR
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("ORR");
arm7.R[rd] = arm7.R[rd] | arm7.R[rm];
arm7.Negative = BitTest(arm7.R[rd], 31);
arm7.Zero = arm7.R[rd] == 0;
}
public static void DataMUL(Arm7 arm7, ushort ins) // MUL
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("MUL");
uint rdVal = arm7.R[rd];
uint rmVal = arm7.R[rm];
rdVal = (rmVal * rdVal);
arm7.R[rd] = rdVal;
arm7.Negative = BitTest(rdVal, 31);
arm7.Zero = rdVal == 0;
}
public static void DataBIC(Arm7 arm7, ushort ins) // BIC
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("BIC");
uint rdValue = arm7.R[rd];
uint rmValue = arm7.R[rm];
uint final = rdValue & (~rmValue);
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
}
public static void DataMVN(Arm7 arm7, ushort ins) // MVN
{
// Rm/Rs and Rd/Rn are the same, just different names for opcodes in this encoding
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = rd;
uint rm = (uint)((ins >> 3) & 0b111);
uint rs = rm;
arm7.LineDebug("MVN");
arm7.R[rd] = ~arm7.R[rm];
arm7.Negative = BitTest(arm7.R[rd], 31);
arm7.Zero = arm7.R[rd] == 0;
}
public static void SpecialDataADD(Arm7 arm7, ushort ins) // ADD (4)
{
arm7.LineDebug("ADD (4)");
uint rd = (uint)((ins >> 0) & 0b111) | ((ins & BIT_7) >> 4);
uint rm = (uint)((ins >> 3) & 0b111) | ((ins & BIT_6) >> 3);
uint rdVal = arm7.R[rd];
uint rmVal = arm7.R[rm];
uint final = rdVal + rmVal;
arm7.R[rd] = final;
if (rd == 15)
{
arm7.FlushPipeline();
}
}
public static void SpecialDataCMP(Arm7 arm7, ushort ins) // CMP (3)
{
arm7.LineDebug("CMP (3)");
uint rn = (uint)((ins >> 0) & 0b111) | ((ins & BIT_7) >> 4);
uint rm = (uint)((ins >> 3) & 0b111) | ((ins & BIT_6) >> 3);
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint alu_out = rnVal - rmVal;
arm7.Negative = BitTest(alu_out, 31);
arm7.Zero = alu_out == 0;
arm7.Carry = !(rmVal > rnVal);
arm7.Overflow = Arm7.CheckOverflowSub(rnVal, rmVal, alu_out);
}
public static void SpecialDataMOV(Arm7 arm7, ushort ins)// MOV (3)
{
arm7.LineDebug("MOV (3)");
uint rd = (uint)((ins >> 0) & 0b111) | ((ins & BIT_7) >> 4);
uint rm = (uint)((ins >> 3) & 0b111) | ((ins & BIT_6) >> 3);
arm7.R[rd] = arm7.R[rm];
if (rd == 15)
{
arm7.R[15] &= 0xFFFFFFFE;
arm7.FlushPipeline();
}
}
public static void SpecialDataBX(Arm7 arm7, ushort ins) // BX
{
arm7.LineDebug("BX | Optionally switch back to ARM state");
uint rm = (uint)((ins >> 3) & 0xF); // High bit is technically an H bit, but can be ignored here
uint val = arm7.R[rm];
arm7.LineDebug($"R{rm}");
// BLX (2)
if (BitTest(ins, 7))
{
arm7.R[14] = (arm7.R[15] - 2) | 1;
}
arm7.ThumbState = BitTest(val, 0);
arm7.R[15] = val & 0xFFFFFFFE;
arm7.FlushPipeline();
if (!arm7.ThumbState)
{
arm7.StateChange();
}
}
public static void LDRLiteralPool(Arm7 arm7, ushort ins)
{
arm7.LineDebug("LDR (3) | PC Relative, 8-bit Immediate");
uint rd = (uint)((ins >> 8) & 0b111);
uint immed8 = (uint)((ins >> 0) & 0xFF);
uint addr = (arm7.R[15] & 0xFFFFFFFC) + (immed8 * 4);
uint readAddr = addr & ~0b11U;
uint readVal = arm7.Read32(readAddr);
arm7.R[rd] = RotateRight32(readVal, (byte)((addr & 0b11) * 8));
}
public static void ImmShiftLSL(Arm7 arm7, ushort ins)
{
arm7.LineDebug("LSL (1) | Logical Shift Left");
uint immed5 = (uint)((ins >> 6) & 0b11111);
uint rd = (uint)((ins >> 0) & 0b111);
uint rmValue = arm7.R[(uint)((ins >> 3) & 0b111)];
if (immed5 == 0)
{
arm7.R[rd] = rmValue;
}
else
{
arm7.Carry = BitTest(rmValue, (byte)(32 - immed5));
arm7.R[rd] = LogicalShiftLeft32(rmValue, (byte)immed5);
}
arm7.Negative = BitTest(arm7.R[rd], 31);
arm7.Zero = arm7.R[rd] == 0;
}
public static void ImmShiftLSR(Arm7 arm7, ushort ins)
{
arm7.LineDebug("LSR (1)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rm = (uint)((ins >> 3) & 0b111);
uint immed5 = (uint)((ins >> 6) & 0b11111);
uint rmVal = arm7.R[rm];
uint final;
if (immed5 == 0)
{
arm7.Carry = BitTest(rmVal, 31);
final = 0;
}
else
{
arm7.Carry = BitTest(rmVal, (byte)(immed5 - 1));
final = LogicalShiftRight32(rmVal, (byte)immed5);
}
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
}
public static void ImmShiftASR(Arm7 arm7, ushort ins)
{
arm7.LineDebug("ASR (1)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rmValue = arm7.R[(uint)((ins >> 3) & 0b111)];
uint immed5 = (uint)((ins >> 6) & 0b11111);
if (immed5 == 0)
{
arm7.Carry = BitTest(rmValue, 31);
if (BitTest(rmValue, 31))
{
arm7.R[rd] = 0xFFFFFFFF;
}
else
{
arm7.R[rd] = 0;
}
}
else
{
arm7.Carry = BitTest(rmValue, (byte)(immed5 - 1));
arm7.R[rd] = ArithmeticShiftRight32(rmValue, (byte)immed5);
}
arm7.Negative = BitTest(arm7.R[rd], 31);
arm7.Zero = arm7.R[rd] == 0;
}
public static void ImmAluADD1(Arm7 arm7, ushort ins)
{
arm7.LineDebug("ADD (3)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rnVal = arm7.R[(uint)((ins >> 3) & 0b111)];
uint rmVal = arm7.R[(uint)((ins >> 6) & 0b111)];
uint final = rnVal + rmVal;
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
arm7.Carry = (long)rnVal + (long)rmVal > 0xFFFFFFFF;
arm7.Overflow = Arm7.CheckOverflowAdd(rnVal, rmVal, final);
}
public static void ImmAluSUB1(Arm7 arm7, ushort ins)
{
arm7.LineDebug("SUB (3)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rnValue = arm7.R[(uint)((ins >> 3) & 0b111)];
uint rmValue = arm7.R[(uint)((ins >> 6) & 0b111)];
uint final = rnValue - rmValue;
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
arm7.Carry = !(rmValue > rnValue);
arm7.Overflow = Arm7.CheckOverflowSub(rnValue, rmValue, final);
}
public static void ImmAluADD2(Arm7 arm7, ushort ins)
{
arm7.LineDebug("ADD (1)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rnVal = arm7.R[(uint)((ins >> 3) & 0b111)];
uint immed3 = (uint)((ins >> 6) & 0b111);
uint final = rnVal + immed3;
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
arm7.Carry = (long)rnVal + (long)immed3 > 0xFFFFFFFF;
arm7.Overflow = Arm7.CheckOverflowAdd(rnVal, immed3, final);
if (rd == 15) arm7.FlushPipeline();
}
public static void ImmAluSUB2(Arm7 arm7, ushort ins)
{
arm7.LineDebug("SUB (1)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint immed3 = (uint)((ins >> 6) & 0b111);
uint rdVal = arm7.R[rd];
uint rnVal = arm7.R[rn];
uint final = rnVal - immed3;
arm7.R[rd] = final;
arm7.Negative = BitTest(final, 31);
arm7.Zero = final == 0;
arm7.Carry = !(immed3 > rnVal);
arm7.Overflow = Arm7.CheckOverflowSub(rnVal, immed3, final);
}
public static void ImmOffsLDR(Arm7 arm7, ushort ins)
{
arm7.LineDebug("LDR (1) | Base + Immediate");
uint rd = (uint)((ins >> 0) & 0b111);
uint rnValue = arm7.R[(uint)((ins >> 3) & 0b111)];
uint immed5 = (uint)((ins >> 6) & 0b11111);
uint addr = rnValue + (immed5 * 4);
// Misaligned
uint readAddr = addr & ~0b11U;
uint readVal = arm7.Read32(readAddr);
arm7.R[rd] = RotateRight32(readVal, (byte)((addr & 0b11) * 8));
arm7.LineDebug($"Addr: {Util.HexN(addr, 8)}");
arm7.ICycle();
}
public static void ImmOffsSTR(Arm7 arm7, ushort ins)
{
arm7.LineDebug("STR (1)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rnValue = arm7.R[(uint)((ins >> 3) & 0b111)];
uint immed5 = (uint)((ins >> 6) & 0b11111);
uint addr = rnValue + (immed5 * 4);
arm7.LineDebug($"Addr: {Util.HexN(addr, 8)}");
arm7.Write32(addr & ~3U, arm7.R[rd]);
}
public static void ImmOffsSTRB(Arm7 arm7, ushort ins)
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rdVal = arm7.R[rd];
uint rn = (uint)((ins >> 3) & 0b111);
uint rnVal = arm7.R[rn];
uint immed5 = (uint)((ins >> 6) & 0b11111);
uint addr = rnVal + immed5;
arm7.LineDebug("STRB (1)");
arm7.Write8(addr, (byte)rdVal);
}
public static void ImmOffsLDRB(Arm7 arm7, ushort ins)
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rdVal = arm7.R[rd];
uint rn = (uint)((ins >> 3) & 0b111);
uint rnVal = arm7.R[rn];
uint immed5 = (uint)((ins >> 6) & 0b11111);
uint addr = rnVal + immed5;
arm7.LineDebug("LDRB (1)");
arm7.R[rd] = arm7.Read8(addr);
arm7.ICycle();
}
public static void RegOffsSTR(Arm7 arm7, ushort ins) // STR (2)
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rm = (uint)((ins >> 6) & 0b111);
arm7.LineDebug("STR (2)");
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint addr = rnVal + rmVal;
arm7.Write32(addr & ~0b11U, arm7.R[rd]);
}
public static void RegOffsSTRH(Arm7 arm7, ushort ins) // STRH (2)
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rm = (uint)((ins >> 6) & 0b111);
arm7.LineDebug("STRH (2)");
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint addr = rnVal + rmVal;
arm7.LineDebug("Store");
uint rdVal = arm7.R[rd];
// Forcibly align address to halfwords
arm7.Write16(addr & ~1u, (ushort)rdVal);
}
public static void RegOffsSTRB(Arm7 arm7, ushort ins) // STRB (2)
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rm = (uint)((ins >> 6) & 0b111);
uint rdVal = arm7.R[rd];
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint addr = rnVal + rmVal;
bool load = BitTest(ins, 11);
arm7.LineDebug("STRB (2)");
arm7.Write8(addr, (byte)rdVal);
}
public static void RegOffsLDRSB(Arm7 arm7, ushort ins) // LDRSB
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rm = (uint)((ins >> 6) & 0b111);
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint addr = rnVal + rmVal;
arm7.LineDebug("LDRSB");
// Sign extend
int readVal = (sbyte)arm7.Read8(addr);
arm7.R[rd] = (uint)readVal;
arm7.ICycle();
}
public static void RegOffsLDR(Arm7 arm7, ushort ins) // LDR (2)
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rm = (uint)((ins >> 6) & 0b111);
arm7.LineDebug("LDR (2)");
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint addr = rnVal + rmVal;
// Misaligned
uint readAddr = addr & ~0b11U;
uint readVal = arm7.Read32(readAddr);
arm7.R[rd] = RotateRight32(readVal, (byte)((addr & 0b11) * 8));
arm7.ICycle();
}
public static void RegOffsLDRH(Arm7 arm7, ushort ins) // LDRH (2)
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rm = (uint)((ins >> 6) & 0b111);
arm7.LineDebug("LDRH (2)");
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint addr = rnVal + rmVal;
arm7.LineDebug("Load");
// Take care of alignment
arm7.R[rd] = RotateRight32(arm7.Read16(addr & ~1u), (byte)(8 * (addr & 1)));
arm7.ICycle();
}
public static void RegOffsLDRB(Arm7 arm7, ushort ins) // LDRB (2)
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rm = (uint)((ins >> 6) & 0b111);
uint rdVal = arm7.R[rd];
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint addr = rnVal + rmVal;
bool load = BitTest(ins, 11);
if (load)
{
arm7.LineDebug("LDRB (2)");
arm7.R[rd] = arm7.Read8(addr);
}
else
{
arm7.LineDebug("STRB (2)");
arm7.Write8(addr, (byte)rdVal);
}
arm7.ICycle();
}
public static void RegOffsLDRSH(Arm7 arm7, ushort ins) // LDRSH
{
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rm = (uint)((ins >> 6) & 0b111);
uint rnVal = arm7.R[rn];
uint rmVal = arm7.R[rm];
uint addr = rnVal + rmVal;
arm7.LineDebug("LDRSH");
int readVal;
if ((addr & 1) != 0)
{
// Misaligned, read byte instead.
readVal = (sbyte)arm7.Read8(addr);
}
else
{
readVal = (short)arm7.Read16(addr);
}
arm7.R[rd] = (uint)readVal;
arm7.ICycle();
}
public static void StackLDR(Arm7 arm7, ushort ins)
{
arm7.LineDebug("LDR (4)");
uint immed8 = (uint)((ins >> 0) & 0xFF);
uint rd = (uint)((ins >> 8) & 0b111);
uint addr = arm7.R[13] + (immed8 * 4);
// Misaligned
uint readAddr = addr & ~0b11U;
uint readVal = arm7.Read32(readAddr);
arm7.R[rd] = RotateRight32(readVal, (byte)((addr & 0b11) * 8));
arm7.ICycle();
}
public static void StackSTR(Arm7 arm7, ushort ins)
{
arm7.LineDebug("STR (3)");
uint immed8 = (uint)((ins >> 0) & 0xFF);
uint rd = (uint)((ins >> 8) & 0b111);
uint addr = arm7.R[13] + (immed8 * 4);
arm7.Write32(addr & ~3U, arm7.R[rd]);
arm7.ICycle();
}
public static void ImmLDRH(Arm7 arm7, ushort ins)
{
arm7.LineDebug("LDRH (1)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rnVal = arm7.R[rn];
uint immed5 = (uint)((ins >> 6) & 0b11111);
uint addr = rnVal + (immed5 * 2);
arm7.LineDebug("Load");
arm7.R[rd] = RotateRight32(arm7.Read16(addr & ~1u), (byte)(8 * (addr & 1)));
arm7.ICycle();
}
public static void ImmSTRH(Arm7 arm7, ushort ins)
{
arm7.LineDebug("STRH (1)");
uint rd = (uint)((ins >> 0) & 0b111);
uint rn = (uint)((ins >> 3) & 0b111);
uint rnVal = arm7.R[rn];
uint immed5 = (uint)((ins >> 6) & 0b11111);
uint addr = rnVal + (immed5 * 2);
arm7.LineDebug("Store");
arm7.Write16(addr & ~1u, (ushort)arm7.R[rd]);
}
public static void POP(Arm7 arm7, ushort ins)
{
arm7.LineDebug("POP");
// String regs = "";
uint addr = arm7.R[13];
2024-08-16 14:51:15 +08:00
uint registerCount = Arm.popcount_8(ins & 0x1FFU);
2024-08-16 11:06:40 +08:00
arm7.R[13] = addr + registerCount * 4;
if (BitTest(ins, 0)) { /* regs += "R0 "; */ arm7.R[0] = arm7.Read32(addr & ~3u); addr += 4; }
if (BitTest(ins, 1)) { /* regs += "R1 "; */ arm7.R[1] = arm7.Read32(addr & ~3u); addr += 4; }
if (BitTest(ins, 2)) { /* regs += "R2 "; */ arm7.R[2] = arm7.Read32(addr & ~3u); addr += 4; }
if (BitTest(ins, 3)) { /* regs += "R3 "; */ arm7.R[3] = arm7.Read32(addr & ~3u); addr += 4; }
if (BitTest(ins, 4)) { /* regs += "R4 "; */ arm7.R[4] = arm7.Read32(addr & ~3u); addr += 4; }
if (BitTest(ins, 5)) { /* regs += "R5 "; */ arm7.R[5] = arm7.Read32(addr & ~3u); addr += 4; }
if (BitTest(ins, 6)) { /* regs += "R6 "; */ arm7.R[6] = arm7.Read32(addr & ~3u); addr += 4; }
if (BitTest(ins, 7)) { /* regs += "R7 "; */ arm7.R[7] = arm7.Read32(addr & ~3u); addr += 4; }
if (BitTest(ins, 8))
{
/* regs += "PC "; */
arm7.R[15] = arm7.Read32(addr);
if (arm7.Armv5)
{
arm7.ThumbState = BitTest(arm7.R[15], 0);
}
arm7.FlushPipeline();
arm7.LineDebug(Util.Hex(arm7.R[15], 8));
addr += 4;
}
// Handle empty rlist
if ((ins & 0x1FF) == 0)
{
if (!arm7.Armv5)
{
arm7.R[15] = arm7.Read32(addr & ~3u);
arm7.FlushPipeline();
}
2024-08-16 14:51:15 +08:00
//Debug.Log("POP empty Rlist");
2024-08-16 11:06:40 +08:00
arm7.R[13] += 0x40;
}
// LineDebug(regs);
arm7.ICycle();
}
public static void PUSH(Arm7 arm7, ushort ins)
{
arm7.LineDebug("PUSH");
uint addr = arm7.R[13];
2024-08-16 14:51:15 +08:00
addr -= 4 * Arm.popcount_8((uint)ins & 0x1FF);
2024-08-16 11:06:40 +08:00
if (BitTest(ins, 0)) { /* regs += "R0 "; */ arm7.Write32(addr & ~3u, arm7.R[0]); addr += 4; arm7.R[13] -= 4; }
if (BitTest(ins, 1)) { /* regs += "R1 "; */ arm7.Write32(addr & ~3u, arm7.R[1]); addr += 4; arm7.R[13] -= 4; }
if (BitTest(ins, 2)) { /* regs += "R2 "; */ arm7.Write32(addr & ~3u, arm7.R[2]); addr += 4; arm7.R[13] -= 4; }
if (BitTest(ins, 3)) { /* regs += "R3 "; */ arm7.Write32(addr & ~3u, arm7.R[3]); addr += 4; arm7.R[13] -= 4; }
if (BitTest(ins, 4)) { /* regs += "R4 "; */ arm7.Write32(addr & ~3u, arm7.R[4]); addr += 4; arm7.R[13] -= 4; }
if (BitTest(ins, 5)) { /* regs += "R5 "; */ arm7.Write32(addr & ~3u, arm7.R[5]); addr += 4; arm7.R[13] -= 4; }
if (BitTest(ins, 6)) { /* regs += "R6 "; */ arm7.Write32(addr & ~3u, arm7.R[6]); addr += 4; arm7.R[13] -= 4; }
if (BitTest(ins, 7)) { /* regs += "R7 "; */ arm7.Write32(addr & ~3u, arm7.R[7]); addr += 4; arm7.R[13] -= 4; }
if (BitTest(ins, 8))
{
/* regs += "LR "; */
arm7.Write32(addr, arm7.R[14]);
addr += 4;
arm7.R[13] -= 4;
}
// Handle empty rlist
if ((ins & 0x1FF) == 0)
{
if (!arm7.Armv5)
{
arm7.Write32(addr & ~3u, arm7.R[15]);
}
arm7.R[13] += 0x40;
}
// LineDebug(regs);
}
public static void MiscImmADD(Arm7 arm7, ushort ins)
{
arm7.LineDebug("ADD (7)");
uint immed7 = (uint)(ins & 0b1111111);
arm7.R[13] = arm7.R[13] + (immed7 << 2);
}
public static void MiscImmSUB(Arm7 arm7, ushort ins)
{
arm7.LineDebug("SUB (4)");
uint immed7 = (uint)(ins & 0b1111111);
arm7.R[13] = arm7.R[13] - (immed7 << 2);
}
public static void MiscREVSH(Arm7 arm7, ushort ins)
{
arm7.LineDebug("REVSH");
uint rd = (uint)((ins >> 0) & 0b111);
uint rdVal = arm7.R[rd];
uint rn = (uint)((ins >> 3) & 0b111);
uint rnVal = arm7.R[rn];
uint rnValHalfLower = ((rnVal >> 0) & 0xFFFF);
uint rnValHalfUpper = ((rnVal >> 8) & 0xFFFF);
rdVal &= 0xFFFF0000;
rdVal |= (rnValHalfUpper << 0);
rdVal |= (rnValHalfLower << 8);
// Sign Extend
if (BitTest(rn, 7))
{
rdVal |= 0xFFFF0000;
}
else
{
rdVal &= 0x0000FFFF;
}
arm7.R[rd] = rdVal;
}
public static void MiscPcADD(Arm7 arm7, ushort ins)
{
arm7.LineDebug("ADD (5)");
uint immed8 = (uint)(ins & 0xFF);
uint rd = (uint)((ins >> 8) & 0b111);
arm7.R[rd] = (arm7.R[15] & 0xFFFFFFFC) + (immed8 * 4);
}
public static void MiscSpADD(Arm7 arm7, ushort ins)
{
arm7.LineDebug("ADD (6)");
uint immed8 = (uint)(ins & 0xFF);
uint rd = (uint)((ins >> 8) & 0b111);
arm7.R[rd] = arm7.R[13] + (immed8 << 2);
}
public static void LDMIA(Arm7 arm7, ushort ins)
{
arm7.LineDebug("LDMIA | Load Multiple Increment After");
uint rn = (uint)((ins >> 8) & 0b111);
uint addr = arm7.R[rn];
// String regs = "";
uint registerList = ins & 0xFFU;
2024-08-16 14:51:15 +08:00
uint registerCount = Arm.popcount_8(registerList);
2024-08-16 11:06:40 +08:00
uint writebackVal = arm7.R[rn] + registerCount * 4;
arm7.R[rn] = writebackVal;
uint register = 0;
for (; registerList != 0; registerList >>= 1)
{
if (BitTest(registerList, 0))
{
arm7.R[register] = arm7.Read32(addr & ~3u);
addr += 4;
}
register++;
}
// Handle empty rlist
if ((ins & 0xFF) == 0)
{
if (!arm7.Armv5)
{
arm7.R[15] = arm7.Read32(addr & ~3u);
arm7.FlushPipeline();
}
arm7.R[rn] += 0x40;
}
// LineDebug(regs);
arm7.ICycle();
}
public static void STMIA(Arm7 arm7, ushort ins)
{
arm7.LineDebug("STMIA | Store Multiple Increment After");
uint rn = (uint)((ins >> 8) & 0b111);
uint addr = arm7.R[rn];
// String regs = "";
arm7.R[15] += 2;
uint registerList = ins & 0xFFU;
2024-08-16 14:51:15 +08:00
uint registerCount = Arm.popcount_8(registerList);
2024-08-16 11:06:40 +08:00
uint writebackVal = arm7.R[rn] + registerCount * 4;
uint register = 0;
for (; registerList != 0; registerList >>= 1)
{
if (BitTest(registerList, 0))
{
arm7.Write32(addr & ~3u, arm7.R[register]);
addr += 4;
if (!arm7.Armv5)
{
arm7.R[rn] = writebackVal;
}
}
register++;
}
if (arm7.Armv5)
{
arm7.R[rn] = writebackVal;
}
// Handle empty rlist
if ((ins & 0xFF) == 0)
{
if (!arm7.Armv5)
{
arm7.Write32(addr & ~3u, arm7.R[15]);
}
arm7.R[rn] += 0x40;
}
arm7.R[15] -= 2;
}
// LineDebug(regs);
public static void SWI(Arm7 arm7, ushort ins)
{
arm7.SPSR_svc = arm7.GetCPSR();
arm7.SetMode(Arm7Mode.SVC); // Go into SVC / Supervisor mode
arm7.R[14] = arm7.R[15] - 2;
arm7.ThumbState = false; // Back to ARM state
arm7.IRQDisable = true;
arm7.R[15] = arm7.VectorSoftwareInterrupt;
arm7.FlushPipeline();
arm7.StateChange();
}
public static void ConditionalB(Arm7 arm7, ushort ins)
{
arm7.LineDebug("B | Conditional Branch");
uint cond = (uint)((ins >> 8) & 0xF);
bool condition = arm7.CheckCondition(cond);
if (condition)
{
// B
int offset = (int)(ins & 0xFF) << 1;
// Signed with Two's Complement
offset = (offset << 23) >> 23;
arm7.R[15] = (uint)(arm7.R[15] + offset);
arm7.FlushPipeline();
}
else
{
arm7.LineDebug("Not Taken");
}
}
public static void UnconditionalB(Arm7 arm7, ushort ins)
{
arm7.LineDebug("B | Unconditional Branch");
int signedImmed11 = (int)(ins & 0b11111111111) << 1;
signedImmed11 = (signedImmed11 << 20) >> 20;
arm7.R[15] = (uint)(arm7.R[15] + signedImmed11);
arm7.FlushPipeline();
}
public static void BLUpperFill(Arm7 arm7, ushort ins)
{
arm7.LineDebug("BL, BLX | Branch With Link (And Exchange)");
uint H = (uint)((ins >> 11) & 0b11);
int offset11 = ins & 0b11111111111;
offset11 <<= 12;
// Sign extend
offset11 = ((int)offset11 << 9) >> 9;
arm7.LineDebug($"offset11: {offset11}");
arm7.R[14] = (uint)(arm7.R[15] + offset11);
arm7.LineDebug("Upper fill");
}
public static void BLToThumb(Arm7 arm7, ushort ins)
{
arm7.LineDebug("BL, BLX | Branch With Link (And Exchange)");
int offset11 = ins & 0b11111111111;
uint oldR14 = arm7.R[14];
arm7.R[14] = (arm7.R[15] - 2) | 1;
arm7.R[15] = (uint)(oldR14 + (offset11 << 1));
arm7.R[15] &= 0xFFFFFFFE;
arm7.FlushPipeline();
arm7.LineDebug($"Jump to ${Util.HexN(arm7.R[15], 8)}");
arm7.LineDebug("Stay in THUMB state");
}
public static void BLToArm(Arm7 arm7, ushort ins)
{
arm7.LineDebug("BL, BLX | Branch With Link (And Exchange)");
int offset11 = ins & 0b11111111111;
uint oldR14 = arm7.R[14];
arm7.R[14] = (arm7.R[15] - 2) | 1;
arm7.R[15] = (uint)((oldR14 + (offset11 << 1)) & ~3);
arm7.ThumbState = false;
arm7.FlushPipeline();
arm7.LineDebug($"Jump to ${Util.HexN(arm7.R[15], 8)}");
arm7.LineDebug("Exit THUMB state");
arm7.StateChange();
}
public static void BLXRegister(Arm7 arm7, ushort ins)
{
arm7.StateChange();
}
public static void Invalid(Arm7 arm7, ushort ins)
{
arm7.Error($"Invalid THUMB Instruction: {Hex(ins, 4)} @ {HexN(arm7.R[15], 8)}");
}
}
}