diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 2b4d5347b7..f65074ddd4 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -231,6 +231,18 @@ + + + + + + + + + + + + diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Arm7Processor.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Arm7Processor.cs new file mode 100644 index 0000000000..6b9c83c499 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Arm7Processor.cs @@ -0,0 +1,501 @@ +namespace GarboDev +{ + using System; + using System.Collections.Generic; + using System.Text; + + public class Arm7Processor + { + private Memory memory = null; + private FastArmCore armCore = null; + private ThumbCore thumbCore = null; + private Dictionary breakpoints = null; + + private int cycles = 0; + private int timerCycles = 0; + private int soundCycles = 0; + + // CPU mode definitions + public const uint USR = 0x10; + public const uint FIQ = 0x11; + public const uint IRQ = 0x12; + public const uint SVC = 0x13; + public const uint ABT = 0x17; + public const uint UND = 0x1B; + public const uint SYS = 0x1F; + + // CPSR bit definitions + public const int N_BIT = 31; + public const int Z_BIT = 30; + public const int C_BIT = 29; + public const int V_BIT = 28; + public const int I_BIT = 7; + public const int F_BIT = 6; + public const int T_BIT = 5; + + public const uint N_MASK = (uint)(1U << N_BIT); + public const uint Z_MASK = (uint)(1U << Z_BIT); + public const uint C_MASK = (uint)(1U << C_BIT); + public const uint V_MASK = (uint)(1U << V_BIT); + public const uint I_MASK = (uint)(1U << I_BIT); + public const uint F_MASK = (uint)(1U << F_BIT); + public const uint T_MASK = (uint)(1U << T_BIT); + + // Standard registers + private uint[] registers = new uint[16]; + private uint cpsr = 0; + + // Banked registers + private uint[] bankedFIQ = new uint[7]; + private uint[] bankedIRQ = new uint[2]; + private uint[] bankedSVC = new uint[2]; + private uint[] bankedABT = new uint[2]; + private uint[] bankedUND = new uint[2]; + + // Saved CPSR's + private uint spsrFIQ = 0; + private uint spsrIRQ = 0; + private uint spsrSVC = 0; + private uint spsrABT = 0; + private uint spsrUND = 0; + + private ushort keyState; + + private bool breakpointHit = false; + private bool cpuHalted = false; + + public ushort KeyState + { + set { this.keyState = value; } + } + + public int Cycles + { + get { return this.cycles; } + set { this.cycles = value; } + } + + public bool ArmState + { + get { return (this.cpsr & Arm7Processor.T_MASK) != Arm7Processor.T_MASK; } + } + + public uint[] Registers + { + get { return this.registers; } + } + + public uint CPSR + { + get { return this.cpsr; } + set { this.cpsr = value; } + } + + public bool SPSRExists + { + get + { + switch (this.cpsr & 0x1F) + { + case USR: + case SYS: + return false; + case FIQ: + case SVC: + case ABT: + case IRQ: + case UND: + return true; + default: + return false; + } + } + } + + public uint SPSR + { + get + { + switch (this.cpsr & 0x1F) + { + case USR: + case SYS: + return 0xFFFFFFFF; + case FIQ: + return this.spsrFIQ; + case SVC: + return this.spsrSVC; + case ABT: + return this.spsrABT; + case IRQ: + return this.spsrIRQ; + case UND: + return this.spsrUND; + default: + throw new Exception("Unhandled CPSR state..."); + } + } + set + { + switch (this.cpsr & 0x1F) + { + case USR: + case SYS: + break; + case FIQ: + this.spsrFIQ = value; + break; + case SVC: + this.spsrSVC = value; + break; + case ABT: + this.spsrABT = value; + break; + case IRQ: + this.spsrIRQ = value; + break; + case UND: + this.spsrUND = value; + break; + default: + throw new Exception("Unhandled CPSR state..."); + } + } + } + + public Dictionary Breakpoints + { + get + { + return this.breakpoints; + } + } + + public bool BreakpointHit + { + get { return this.breakpointHit; } + set { this.breakpointHit = value; } + } + + public Arm7Processor(Memory memory) + { + this.memory = memory; + this.memory.Processor = this; + this.armCore = new FastArmCore(this, this.memory); + this.thumbCore = new ThumbCore(this, this.memory); + this.breakpoints = new Dictionary(); + this.breakpointHit = false; + } + + private void SwapRegsHelper(uint[] swapRegs) + { + for (int i = 14; i > 14 - swapRegs.Length; i--) + { + uint tmp = this.registers[i]; + this.registers[i] = swapRegs[swapRegs.Length - (14 - i) - 1]; + swapRegs[swapRegs.Length - (14 - i) - 1] = tmp; + } + } + + private void SwapRegisters(uint bank) + { + switch (bank & 0x1F) + { + case FIQ: + this.SwapRegsHelper(this.bankedFIQ); + break; + case SVC: + this.SwapRegsHelper(this.bankedSVC); + break; + case ABT: + this.SwapRegsHelper(this.bankedABT); + break; + case IRQ: + this.SwapRegsHelper(this.bankedIRQ); + break; + case UND: + this.SwapRegsHelper(this.bankedUND); + break; + } + } + + public void WriteCpsr(uint newCpsr) + { + if ((newCpsr & 0x1F) != (this.cpsr & 0x1F)) + { + // Swap out the old registers + this.SwapRegisters(this.cpsr); + // Swap in the new registers + this.SwapRegisters(newCpsr); + } + + this.cpsr = newCpsr; + } + + public void EnterException(uint mode, uint vector, bool interruptsDisabled, bool fiqDisabled) + { + uint oldCpsr = this.cpsr; + + if ((oldCpsr & Arm7Processor.T_MASK) != 0) + { + registers[15] += 2U; + } + + // Clear T bit, and set mode + uint newCpsr = (oldCpsr & ~0x3FU) | mode; + if (interruptsDisabled) newCpsr |= 1 << 7; + if (fiqDisabled) newCpsr |= 1 << 6; + this.WriteCpsr(newCpsr); + + this.SPSR = oldCpsr; + registers[14] = registers[15]; + registers[15] = vector; + + this.ReloadQueue(); + } + + public void RequestIrq(int irq) + { + ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF); + iflag |= (ushort)(1 << irq); + Memory.WriteU16(this.memory.IORam, Memory.IF, iflag); + } + + public void FireIrq() + { + ushort ime = Memory.ReadU16(this.memory.IORam, Memory.IME); + ushort ie = Memory.ReadU16(this.memory.IORam, Memory.IE); + ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF); + + if ((ie & (iflag)) != 0 && (ime & 1) != 0 && (this.cpsr & (1 << 7)) == 0) + { + // Off to the irq exception vector + this.EnterException(Arm7Processor.IRQ, 0x18, true, false); + } + } + + public void Reset(bool skipBios) + { + this.breakpointHit = false; + this.cpuHalted = false; + + // Default to ARM state + this.cycles = 0; + this.timerCycles = 0; + this.soundCycles = 0; + + this.bankedSVC[0] = 0x03007FE0; + this.bankedIRQ[0] = 0x03007FA0; + + this.cpsr = SYS; + this.spsrSVC = this.cpsr; + for (int i = 0; i < 15; i++) this.registers[i] = 0; + + if (skipBios) + { + this.registers[15] = 0x8000000; + } + else + { + this.registers[15] = 0; + } + + this.armCore.BeginExecution(); + } + + public void Halt() + { + this.cpuHalted = true; + this.cycles = 0; + } + + public void Step() + { + this.breakpointHit = false; + + if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbCore.Step(); + } + else + { + this.armCore.Step(); + } + + this.UpdateTimers(); + } + + public void ReloadQueue() + { + if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbCore.BeginExecution(); + } + else + { + this.armCore.BeginExecution(); + } + } + + private void UpdateTimer(int timer, int cycles, bool countUp) + { + ushort control = Memory.ReadU16(this.memory.IORam, Memory.TM0CNT + (uint)(timer * 4)); + + // Make sure timer is enabled, or count up is disabled + if ((control & (1 << 7)) == 0) return; + if (!countUp && (control & (1 << 2)) != 0) return; + + if (!countUp) + { + switch (control & 3) + { + case 0: cycles *= 1 << 10; break; + case 1: cycles *= 1 << 4; break; + case 2: cycles *= 1 << 2; break; + // Don't need to do anything for case 3 + } + } + + this.memory.TimerCnt[timer] += (uint)cycles; + uint timerCnt = this.memory.TimerCnt[timer] >> 10; + + if (timerCnt > 0xffff) + { + ushort soundCntX = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_X); + if ((soundCntX & (1 << 7)) != 0) + { + ushort soundCntH = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_H); + if (timer == ((soundCntH >> 10) & 1)) + { + // FIFO A overflow + this.memory.SoundManager.DequeueA(); + if (this.memory.SoundManager.QueueSizeA < 16) + { + this.memory.FifoDma(1); + // TODO + if (this.memory.SoundManager.QueueSizeA < 16) + { + } + } + } + if (timer == ((soundCntH >> 14) & 1)) + { + // FIFO B overflow + this.memory.SoundManager.DequeueB(); + if (this.memory.SoundManager.QueueSizeB < 16) + { + this.memory.FifoDma(2); + } + } + } + + // Overflow, attempt to fire IRQ + if ((control & (1 << 6)) != 0) + { + this.RequestIrq(3 + timer); + } + + if (timer < 3) + { + ushort control2 = Memory.ReadU16(this.memory.IORam, Memory.TM0CNT + (uint)((timer + 1) * 4)); + if ((control2 & (1 << 2)) != 0) + { + // Count-up + this.UpdateTimer(timer + 1, (int)((timerCnt >> 16) << 10), true); + } + } + + // Reset the original value + uint count = Memory.ReadU16(this.memory.IORam, Memory.TM0D + (uint)(timer * 4)); + this.memory.TimerCnt[timer] = count << 10; + } + } + + public void UpdateTimers() + { + int cycles = this.timerCycles - this.cycles; + + for (int i = 0; i < 4; i++) + { + this.UpdateTimer(i, cycles, false); + } + + this.timerCycles = this.cycles; + } + + public void UpdateKeyState() + { + ushort KEYCNT = this.memory.ReadU16Debug(Memory.REG_BASE + Memory.KEYCNT); + + if ((KEYCNT & (1 << 14)) != 0) + { + if ((KEYCNT & (1 << 15)) != 0) + { + KEYCNT &= 0x3FF; + if (((~this.keyState) & KEYCNT) == KEYCNT) + this.RequestIrq(12); + } + else + { + KEYCNT &= 0x3FF; + if (((~this.keyState) & KEYCNT) != 0) + this.RequestIrq(12); + } + } + + this.memory.KeyState = this.keyState; + } + + public void UpdateSound() + { + this.memory.SoundManager.Mix(this.soundCycles); + this.soundCycles = 0; + } + + public void Execute(int cycles) + { + this.cycles += cycles; + this.timerCycles += cycles; + this.soundCycles += cycles; + this.breakpointHit = false; + + if (this.cpuHalted) + { + ushort ie = Memory.ReadU16(this.memory.IORam, Memory.IE); + ushort iflag = Memory.ReadU16(this.memory.IORam, Memory.IF); + + if ((ie & iflag) != 0) + { + this.cpuHalted = false; + } + else + { + this.cycles = 0; + this.UpdateTimers(); + this.UpdateSound(); + return; + } + } + + while (this.cycles > 0) + { + if ((this.cpsr & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbCore.Execute(); + } + else + { + this.armCore.Execute(); + } + + this.UpdateTimers(); + this.UpdateSound(); + + if (this.breakpointHit) + { + break; + } + } + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/ArmCore.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/ArmCore.cs new file mode 100644 index 0000000000..5eda1e7a5d --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/ArmCore.cs @@ -0,0 +1,1273 @@ +//#define ARM_DEBUG + +namespace GarboDev +{ + using System; + + public class ArmCore + { + private const uint COND_EQ = 0; // Z set + private const uint COND_NE = 1; // Z clear + private const uint COND_CS = 2; // C set + private const uint COND_CC = 3; // C clear + private const uint COND_MI = 4; // N set + private const uint COND_PL = 5; // N clear + private const uint COND_VS = 6; // V set + private const uint COND_VC = 7; // V clear + private const uint COND_HI = 8; // C set and Z clear + private const uint COND_LS = 9; // C clear or Z set + private const uint COND_GE = 10; // N equals V + private const uint COND_LT = 11; // N not equal to V + private const uint COND_GT = 12; // Z clear AND (N equals V) + private const uint COND_LE = 13; // Z set OR (N not equal to V) + private const uint COND_AL = 14; // Always + private const uint COND_NV = 15; // Never execute + + private const uint OP_AND = 0x0; + private const uint OP_EOR = 0x1; + private const uint OP_SUB = 0x2; + private const uint OP_RSB = 0x3; + private const uint OP_ADD = 0x4; + private const uint OP_ADC = 0x5; + private const uint OP_SBC = 0x6; + private const uint OP_RSC = 0x7; + private const uint OP_TST = 0x8; + private const uint OP_TEQ = 0x9; + private const uint OP_CMP = 0xA; + private const uint OP_CMN = 0xB; + private const uint OP_ORR = 0xC; + private const uint OP_MOV = 0xD; + private const uint OP_BIC = 0xE; + private const uint OP_MVN = 0xF; + + private delegate void ExecuteInstruction(); + private ExecuteInstruction[] NormalOps = null; + + private Arm7Processor parent; + private Memory memory; + private uint[] registers; + + private uint instructionQueue; + private uint curInstruction; + + // CPU flags + private uint zero, carry, negative, overflow; + private uint shifterCarry; + + private bool thumbMode; + + public ArmCore(Arm7Processor parent, Memory memory) + { + this.parent = parent; + this.memory = memory; + this.registers = this.parent.Registers; + + this.NormalOps = new ExecuteInstruction[8] + { + this.DataProcessing, + this.DataProcessingImmed, + this.LoadStoreImmediate, + this.LoadStoreRegister, + this.LoadStoreMultiple, + this.Branch, + this.CoprocessorLoadStore, + this.SoftwareInterrupt + }; + } + + public void BeginExecution() + { + this.FlushQueue(); + } + + public void Step() + { + this.UnpackFlags(); + + this.curInstruction = this.instructionQueue; + this.instructionQueue = this.memory.ReadU32(registers[15]); + registers[15] += 4; + + uint cond = 0; + switch (this.curInstruction >> 28) + { + case COND_AL: cond = 1; break; + case COND_EQ: cond = zero; break; + case COND_NE: cond = 1 - zero; break; + case COND_CS: cond = carry; break; + case COND_CC: cond = 1 - carry; break; + case COND_MI: cond = negative; break; + case COND_PL: cond = 1 - negative; break; + case COND_VS: cond = overflow; break; + case COND_VC: cond = 1 - overflow; break; + case COND_HI: cond = carry & (1 - zero); break; + case COND_LS: cond = (1 - carry) | zero; break; + case COND_GE: cond = (1 - negative) ^ overflow; break; + case COND_LT: cond = negative ^ overflow; break; + case COND_GT: cond = (1 - zero) & (negative ^ (1 - overflow)); break; + case COND_LE: cond = (negative ^ overflow) | zero; break; + } + + if (cond == 1) + { + // Execute the instruction + this.NormalOps[(curInstruction >> 25) & 0x7](); + } + + this.parent.Cycles -= this.memory.WaitCycles; + + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.parent.ReloadQueue(); + } + + this.PackFlags(); + } + + public void Execute() + { + this.UnpackFlags(); + this.thumbMode = false; + + while (this.parent.Cycles > 0) + { + this.curInstruction = this.instructionQueue; + this.instructionQueue = this.memory.ReadU32Aligned(registers[15]); + registers[15] += 4; + + if ((this.curInstruction >> 28) == COND_AL) + { + this.NormalOps[(curInstruction >> 25) & 0x7](); + } + else + { + uint cond = 0; + switch (this.curInstruction >> 28) + { + case COND_EQ: cond = zero; break; + case COND_NE: cond = 1 - zero; break; + case COND_CS: cond = carry; break; + case COND_CC: cond = 1 - carry; break; + case COND_MI: cond = negative; break; + case COND_PL: cond = 1 - negative; break; + case COND_VS: cond = overflow; break; + case COND_VC: cond = 1 - overflow; break; + case COND_HI: cond = carry & (1 - zero); break; + case COND_LS: cond = (1 - carry) | zero; break; + case COND_GE: cond = (1 - negative) ^ overflow; break; + case COND_LT: cond = negative ^ overflow; break; + case COND_GT: cond = (1 - zero) & (negative ^ (1 - overflow)); break; + case COND_LE: cond = (negative ^ overflow) | zero; break; + } + + if (cond == 1) + { + // Execute the instruction + this.NormalOps[(curInstruction >> 25) & 0x7](); + } + } + + this.parent.Cycles -= this.memory.WaitCycles; + + if (this.thumbMode) + { + this.parent.ReloadQueue(); + break; + } + +#if ARM_DEBUG + // Check the current PC + if (this.parent.Breakpoints.ContainsKey(registers[15] - 4U)) + { + this.parent.BreakpointHit = true; + break; + } +#endif + } + + this.PackFlags(); + } + + #region Barrel Shifter + private const uint SHIFT_LSL = 0; + private const uint SHIFT_LSR = 1; + private const uint SHIFT_ASR = 2; + private const uint SHIFT_ROR = 3; + + private uint BarrelShifter(uint shifterOperand) + { + uint type = (shifterOperand >> 5) & 0x3; + + bool registerShift = (shifterOperand & (1 << 4)) == (1 << 4); + + uint rm = registers[shifterOperand & 0xF]; + + int amount; + if (registerShift) + { + uint rs = (shifterOperand >> 8) & 0xF; + if (rs == 15) + { + amount = (int)((registers[rs] + 0x4) & 0xFF); + } + else + { + amount = (int)(registers[rs] & 0xFF); + } + + if ((shifterOperand & 0xF) == 15) + { + rm += 4; + } + } + else + { + amount = (int)((shifterOperand >> 7) & 0x1F); + } + + if (registerShift) + { + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + + switch (type) + { + case SHIFT_LSL: + if (amount < 32) + { + this.shifterCarry = (rm >> (32 - amount)) & 1; + return rm << amount; + } + else if (amount == 32) + { + this.shifterCarry = rm & 1; + return 0; + } + else + { + this.shifterCarry = 0; + return 0; + } + + case SHIFT_LSR: + if (amount < 32) + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return rm >> amount; + } + else if (amount == 32) + { + this.shifterCarry = (rm >> 31) & 1; + return 0; + } + else + { + this.shifterCarry = 0; + return 0; + } + + case SHIFT_ASR: + if (amount >= 32) + { + if ((rm & (1 << 31)) == 0) + { + this.shifterCarry = 0; + return 0; + } + else + { + this.shifterCarry = 1; + return 0xFFFFFFFF; + } + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (uint)(((int)rm) >> amount); + } + + case SHIFT_ROR: + if ((amount & 0x1F) == 0) + { + this.shifterCarry = (rm >> 31) & 1; + return rm; + } + else + { + amount &= 0x1F; + this.shifterCarry = (rm >> amount) & 1; + return (rm >> amount) | (rm << (32 - amount)); + } + } + } + else + { + switch (type) + { + case SHIFT_LSL: + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + else + { + this.shifterCarry = (rm >> (32 - amount)) & 1; + return rm << amount; + } + + case SHIFT_LSR: + if (amount == 0) + { + this.shifterCarry = (rm >> 31) & 1; + return 0; + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return rm >> amount; + } + + case SHIFT_ASR: + if (amount == 0) + { + if ((rm & (1 << 31)) == 0) + { + this.shifterCarry = 0; + return 0; + } + else + { + this.shifterCarry = 1; + return 0xFFFFFFFF; + } + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (uint)(((int)rm) >> amount); + } + + case SHIFT_ROR: + if (amount == 0) + { + // Actually an RRX + this.shifterCarry = rm & 1; + return (this.carry << 31) | (rm >> 1); + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (rm >> amount) | (rm << (32 - amount)); + } + } + } + + // Should never happen... + throw new Exception("Barrel Shifter has messed up."); + } + #endregion + + #region Flag helpers + public void OverflowCarryAdd(uint a, uint b, uint r) + { + overflow = ((a & b & ~r) | (~a & ~b & r)) >> 31; + carry = ((a & b) | (a & ~r) | (b & ~r)) >> 31; + } + + public void OverflowCarrySub(uint a, uint b, uint r) + { + overflow = ((a & ~b & ~r) | (~a & b & r)) >> 31; + carry = ((a & ~b) | (a & ~r) | (~b & ~r)) >> 31; + } + #endregion + + #region Opcodes + private void DoDataProcessing(uint shifterOperand) + { + uint rn = (this.curInstruction >> 16) & 0xF; + uint rd = (this.curInstruction >> 12) & 0xF; + uint alu; + + bool registerShift = (this.curInstruction & (1 << 4)) == (1 << 4); + if (rn == 15 && ((this.curInstruction >> 25) & 0x7) == 0 && registerShift) + { + rn = registers[rn] + 4; + } + else + { + rn = registers[rn]; + } + + uint opcode = (this.curInstruction >> 21) & 0xF; + + if (((this.curInstruction >> 20) & 1) == 1) + { + // Set flag bit set + switch (opcode) + { + case OP_ADC: + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + break; + + case OP_ADD: + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + break; + + case OP_AND: + registers[rd] = rn & shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_BIC: + registers[rd] = rn & ~shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_CMN: + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case OP_CMP: + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case OP_EOR: + registers[rd] = rn ^ shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_MOV: + registers[rd] = shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_MVN: + registers[rd] = ~shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_ORR: + registers[rd] = rn | shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_RSB: + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + break; + + case OP_RSC: + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + break; + + case OP_SBC: + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + break; + + case OP_SUB: + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + break; + + case OP_TEQ: + alu = rn ^ shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_TST: + alu = rn & shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + } + + if (rd == 15) + { + // Prevent writing if no SPSR exists (this will be true for USER or SYSTEM mode) + if (this.parent.SPSRExists) this.parent.WriteCpsr(this.parent.SPSR); + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + // Otherwise, flush the instruction queue + this.FlushQueue(); + } + } + else + { + // Set flag bit not set + switch (opcode) + { + case OP_ADC: registers[rd] = rn + shifterOperand + carry; break; + case OP_ADD: registers[rd] = rn + shifterOperand; break; + case OP_AND: registers[rd] = rn & shifterOperand; break; + case OP_BIC: registers[rd] = rn & ~shifterOperand; break; + case OP_EOR: registers[rd] = rn ^ shifterOperand; break; + case OP_MOV: registers[rd] = shifterOperand; break; + case OP_MVN: registers[rd] = ~shifterOperand; break; + case OP_ORR: registers[rd] = rn | shifterOperand; break; + case OP_RSB: registers[rd] = shifterOperand - rn; break; + case OP_RSC: registers[rd] = shifterOperand - rn - (1U - carry); break; + case OP_SBC: registers[rd] = rn - shifterOperand - (1U - carry); break; + case OP_SUB: registers[rd] = rn - shifterOperand; break; + + case OP_CMN: + // MSR SPSR, shifterOperand + if ((this.curInstruction & (1 << 16)) == 1 << 16 && this.parent.SPSRExists) + { + this.parent.SPSR &= 0xFFFFFF00; + this.parent.SPSR |= shifterOperand & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17 && this.parent.SPSRExists) + { + this.parent.SPSR &= 0xFFFF00FF; + this.parent.SPSR |= shifterOperand & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18 && this.parent.SPSRExists) + { + this.parent.SPSR &= 0xFF00FFFF; + this.parent.SPSR |= shifterOperand & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19 && this.parent.SPSRExists) + { + this.parent.SPSR &= 0x00FFFFFF; + this.parent.SPSR |= shifterOperand & 0xFF000000; + } + + // Queue will be flushed since rd == 15, so adjust the PC + registers[15] -= 4; + break; + + case OP_CMP: + // MRS rd, SPSR + if (this.parent.SPSRExists) registers[rd] = this.parent.SPSR; + break; + + case OP_TEQ: + if (((this.curInstruction >> 4) & 0xf) == 1) + { + // BX + uint rm = this.curInstruction & 0xf; + + this.PackFlags(); + + this.parent.CPSR &= ~Arm7Processor.T_MASK; + this.parent.CPSR |= (registers[rm] & 1) << Arm7Processor.T_BIT; + + registers[15] = registers[rm] & (~1U); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + // Queue will be flushed later because rd == 15 + } + else if (((this.curInstruction >> 4) & 0xf) == 0) + { + // MSR CPSR, shifterOperand + bool userMode = (this.parent.CPSR & 0x1F) == Arm7Processor.USR; + + this.PackFlags(); + + uint tmpCPSR = this.parent.CPSR; + + if ((this.curInstruction & (1 << 16)) == 1 << 16 && !userMode) + { + tmpCPSR &= 0xFFFFFF00; + tmpCPSR |= shifterOperand & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17 && !userMode) + { + tmpCPSR &= 0xFFFF00FF; + tmpCPSR |= shifterOperand & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18 && !userMode) + { + tmpCPSR &= 0xFF00FFFF; + tmpCPSR |= shifterOperand & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + tmpCPSR &= 0x00FFFFFF; + tmpCPSR |= shifterOperand & 0xFF000000; + } + + this.parent.WriteCpsr(tmpCPSR); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + // Queue will be flushed since rd == 15, so adjust the PC + registers[15] -= 4; + } + break; + + case OP_TST: + // MRS rd, CPSR + this.PackFlags(); + registers[rd] = this.parent.CPSR; + break; + } + + if (rd == 15) + { + // Flush the queue + this.FlushQueue(); + } + } + } + + private void DataProcessing() + { + // Special instruction + switch ((this.curInstruction >> 4) & 0xF) + { + case 0x9: + // Multiply or swap instructions + this.MultiplyOrSwap(); + return; + case 0xB: + // Load/Store Unsigned halfword + this.LoadStoreHalfword(); + return; + case 0xD: + // Load/Store Signed byte + this.LoadStoreHalfword(); + return; + case 0xF: + // Load/Store Signed halfword + this.LoadStoreHalfword(); + return; + } + + this.DoDataProcessing(this.BarrelShifter(this.curInstruction)); + } + + private void DataProcessingImmed() + { + uint immed = this.curInstruction & 0xFF; + int rotateAmount = (int)(((this.curInstruction >> 8) & 0xF) * 2); + + immed = (immed >> rotateAmount) | (immed << (32 - rotateAmount)); + + if (rotateAmount == 0) + { + this.shifterCarry = this.carry; + } + else + { + this.shifterCarry = (immed >> 31) & 1; + } + + this.DoDataProcessing(immed); + } + + private void LoadStore(uint offset) + { + uint rn = (this.curInstruction >> 16) & 0xF; + uint rd = (this.curInstruction >> 12) & 0xF; + + uint address = registers[rn]; + + bool preIndexed = (this.curInstruction & (1 << 24)) == 1 << 24; + bool byteTransfer = (this.curInstruction & (1 << 22)) == 1 << 22; + bool writeback = (this.curInstruction & (1 << 21)) == 1 << 21; + + // Add or subtract offset + if ((this.curInstruction & (1 << 23)) != 1 << 23) offset = (uint)-offset; + + if (preIndexed) + { + address += offset; + + if (writeback) + { + registers[rn] = address; + } + } + + if ((this.curInstruction & (1 << 20)) == 1 << 20) + { + // Load + if (byteTransfer) + { + registers[rd] = this.memory.ReadU8(address); + } + else + { + registers[rd] = this.memory.ReadU32(address); + } + + // ARM9 fix here + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (!preIndexed) + { + if (rn != rd) + registers[rn] = address + offset; + } + } + else + { + // Store + uint amount = registers[rd]; + if (rd == 15) amount += 4; + + if (byteTransfer) + { + this.memory.WriteU8(address, (byte)(amount & 0xFF)); + } + else + { + this.memory.WriteU32(address, amount); + } + + if (!preIndexed) + { + registers[rn] = address + offset; + } + } + } + + private void LoadStoreImmediate() + { + this.LoadStore(this.curInstruction & 0xFFF); + } + + private void LoadStoreRegister() + { + // The barrel shifter expects a 0 in bit 4 for immediate shifts, this is implicit in + // the meaning of the instruction, so it is fine + this.LoadStore(this.BarrelShifter(this.curInstruction)); + } + + private void LoadStoreMultiple() + { + uint rn = (this.curInstruction >> 16) & 0xF; + + this.PackFlags(); + uint curCpsr = this.parent.CPSR; + + bool preIncrement = (this.curInstruction & (1 << 24)) != 0; + bool up = (this.curInstruction & (1 << 23)) != 0; + bool writeback = (this.curInstruction & (1 << 21)) != 0; + + uint address; + uint bitsSet = 0; + for (int i = 0; i < 16; i++) if (((this.curInstruction >> i) & 1) != 0) bitsSet++; + + if (preIncrement) + { + if (up) + { + // Increment before + address = this.registers[rn] + 4; + if (writeback) this.registers[rn] += bitsSet * 4; + } + else + { + // Decrement before + address = this.registers[rn] - (bitsSet * 4); + if (writeback) this.registers[rn] -= bitsSet * 4; + } + } + else + { + if (up) + { + // Increment after + address = this.registers[rn]; + if (writeback) this.registers[rn] += bitsSet * 4; + } + else + { + // Decrement after + address = this.registers[rn] - (bitsSet * 4) + 4; + if (writeback) this.registers[rn] -= bitsSet * 4; + } + } + + if ((this.curInstruction & (1 << 20)) != 0) + { + if ((this.curInstruction & (1 << 22)) != 0 && ((this.curInstruction >> 15) & 1) == 0) + { + // Switch to user mode temporarily + this.parent.WriteCpsr((curCpsr & ~0x1FU) | Arm7Processor.USR); + } + + // Load multiple + for (int i = 0; i < 15; i++) + { + if (((this.curInstruction >> i) & 1) != 1) continue; + this.registers[i] = this.memory.ReadU32Aligned(address & (~0x3U)); + address += 4; + } + + if (((this.curInstruction >> 15) & 1) == 1) + { + // Arm9 fix here + + this.registers[15] = this.memory.ReadU32Aligned(address & (~0x3U)); + + if ((this.curInstruction & (1 << 22)) != 0) + { + // Load the CPSR from the SPSR + if (this.parent.SPSRExists) + { + this.parent.WriteCpsr(this.parent.SPSR); + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + this.registers[15] &= ~0x1U; + return; + } + } + } + + this.registers[15] &= ~0x3U; + this.FlushQueue(); + } + else + { + if ((this.curInstruction & (1 << 22)) != 0) + { + // Switch back to the correct mode + this.parent.WriteCpsr(curCpsr); + this.UnpackFlags(); + + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + } + } + } + else + { + if ((this.curInstruction & (1 << 22)) != 0) + { + // Switch to user mode temporarily + this.parent.WriteCpsr((curCpsr & ~0x1FU) | Arm7Processor.USR); + } + + if (((this.curInstruction >> (int)rn) & 1) != 0 && writeback && + (this.curInstruction & ~(0xFFFFFFFF << (int)rn)) == 0) + { + // If the lowest register is also the writeback, we use the original value + // Does anybody do this???? + throw new Exception("Unhandled STM state"); + } + else + { + // Store multiple + for (int i = 0; i < 15; i++) + { + if (((this.curInstruction >> i) & 1) == 0) continue; + this.memory.WriteU32(address, this.registers[i]); + address += 4; + } + + if (((this.curInstruction >> 15) & 1) != 0) + { + this.memory.WriteU32(address, this.registers[15] + 4U); + } + } + + if ((this.curInstruction & (1 << 22)) != 0) + { + // Switch back to the correct mode + this.parent.WriteCpsr(curCpsr); + this.UnpackFlags(); + } + } + } + + private void Branch() + { + if ((this.curInstruction & (1 << 24)) != 0) + { + this.registers[14] = (this.registers[15] - 4U) & ~3U; + } + + uint branchOffset = this.curInstruction & 0x00FFFFFF; + if (branchOffset >> 23 == 1) branchOffset |= 0xFF000000; + + this.registers[15] += branchOffset << 2; + + this.FlushQueue(); + } + + private void CoprocessorLoadStore() + { + throw new Exception("Unhandled opcode - coproc load/store"); + } + + private void SoftwareInterrupt() + { + // Adjust PC for prefetch + this.registers[15] -= 4U; + this.parent.EnterException(Arm7Processor.SVC, 0x8, false, false); + } + + private void MultiplyOrSwap() + { + if ((this.curInstruction & (1 << 24)) == 1 << 24) + { + // Swap instruction + uint rn = (this.curInstruction >> 16) & 0xF; + uint rd = (this.curInstruction >> 12) & 0xF; + uint rm = this.curInstruction & 0xF; + + if ((this.curInstruction & (1 << 22)) != 0) + { + // SWPB + byte tmp = this.memory.ReadU8(registers[rn]); + this.memory.WriteU8(registers[rn], (byte)(registers[rm] & 0xFF)); + registers[rd] = tmp; + } + else + { + // SWP + uint tmp = this.memory.ReadU32(registers[rn]); + this.memory.WriteU32(registers[rn], registers[rm]); + registers[rd] = tmp; + } + } + else + { + // Multiply instruction + switch ((this.curInstruction >> 21) & 0x7) + { + case 0: + case 1: + { + // Multiply/Multiply + Accumulate + uint rd = (this.curInstruction >> 16) & 0xF; + uint rn = registers[(this.curInstruction >> 12) & 0xF]; + uint rs = (this.curInstruction >> 8) & 0xF; + uint rm = this.curInstruction & 0xF; + + int cycles = 4; + // Multiply cycle calculations + if ((registers[rs] & 0xFFFFFF00) == 0 || (registers[rs] & 0xFFFFFF00) == 0xFFFFFF00) + { + cycles = 1; + } + else if ((registers[rs] & 0xFFFF0000) == 0 || (registers[rs] & 0xFFFF0000) == 0xFFFF0000) + { + cycles = 2; + } + else if ((registers[rs] & 0xFF000000) == 0 || (registers[rs] & 0xFF000000) == 0xFF000000) + { + cycles = 3; + } + + registers[rd] = registers[rs] * registers[rm]; + this.parent.Cycles -= cycles; + + if ((this.curInstruction & (1 << 21)) == 1 << 21) + { + registers[rd] += rn; + this.parent.Cycles -= 1; + } + + if ((this.curInstruction & (1 << 20)) == 1 << 20) + { + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + break; + } + + case 2: + case 3: + throw new Exception("Invalid multiply"); + + case 4: + case 5: + case 6: + case 7: + { + // Multiply/Signed Multiply Long + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + uint rs = (this.curInstruction >> 8) & 0xF; + uint rm = this.curInstruction & 0xF; + + int cycles = 5; + // Multiply cycle calculations + if ((registers[rs] & 0xFFFFFF00) == 0 || (registers[rs] & 0xFFFFFF00) == 0xFFFFFF00) + { + cycles = 2; + } + else if ((registers[rs] & 0xFFFF0000) == 0 || (registers[rs] & 0xFFFF0000) == 0xFFFF0000) + { + cycles = 3; + } + else if ((registers[rs] & 0xFF000000) == 0 || (registers[rs] & 0xFF000000) == 0xFF000000) + { + cycles = 4; + } + + this.parent.Cycles -= cycles; + + switch ((this.curInstruction >> 21) & 0x3) + { + case 0: + { + // UMULL + ulong result = ((ulong)registers[rm]) * registers[rs]; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + break; + } + case 1: + { + // UMLAL + ulong accum = (((ulong)registers[rdhi]) << 32) | registers[rdlo]; + ulong result = ((ulong)registers[rm]) * registers[rs]; + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + break; + } + case 2: + { + // SMULL + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + break; + } + case 3: + { + // SMLAL + long accum = (((long)((int)registers[rdhi])) << 32) | registers[rdlo]; + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + break; + } + } + + if ((this.curInstruction & (1 << 20)) == 1 << 20) + { + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + } + break; + } + } + } + } + + private void LoadStoreHalfword() + { + uint rn = (this.curInstruction >> 16) & 0xF; + uint rd = (this.curInstruction >> 12) & 0xF; + + uint address = registers[rn]; + + bool preIndexed = (this.curInstruction & (1 << 24)) != 0; + bool byteTransfer = (this.curInstruction & (1 << 5)) == 0; + bool signedTransfer = (this.curInstruction & (1 << 6)) != 0; + bool writeback = (this.curInstruction & (1 << 21)) != 0; + + uint offset; + if ((this.curInstruction & (1 << 22)) != 0) + { + // Immediate offset + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + } + else + { + // Register offset + offset = this.registers[this.curInstruction & 0xF]; + } + + // Add or subtract offset + if ((this.curInstruction & (1 << 23)) == 0) offset = (uint)-offset; + + if (preIndexed) + { + address += offset; + + if (writeback) + { + registers[rn] = address; + } + } + + if ((this.curInstruction & (1 << 20)) != 0) + { + // Load + if (byteTransfer) + { + if (signedTransfer) + { + registers[rd] = this.memory.ReadU8(address); + if ((registers[rd] & 0x80) != 0) + { + registers[rd] |= 0xFFFFFF00; + } + } + else + { + registers[rd] = this.memory.ReadU8(address); + } + } + else + { + if (signedTransfer) + { + registers[rd] = this.memory.ReadU16(address); + if ((registers[rd] & 0x8000) != 0) + { + registers[rd] |= 0xFFFF0000; + } + } + else + { + registers[rd] = this.memory.ReadU16(address); + } + } + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (!preIndexed) + { + if (rn != rd) + registers[rn] = address + offset; + } + } + else + { + // Store + if (byteTransfer) + { + this.memory.WriteU8(address, (byte)(registers[rd] & 0xFF)); + } + else + { + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + } + + if (!preIndexed) + { + registers[rn] = address + offset; + } + } + } + #endregion Opcodes + + private void PackFlags() + { + this.parent.CPSR &= 0x0FFFFFFF; + this.parent.CPSR |= this.negative << Arm7Processor.N_BIT; + this.parent.CPSR |= this.zero << Arm7Processor.Z_BIT; + this.parent.CPSR |= this.carry << Arm7Processor.C_BIT; + this.parent.CPSR |= this.overflow << Arm7Processor.V_BIT; + } + + private void UnpackFlags() + { + this.negative = (this.parent.CPSR >> Arm7Processor.N_BIT) & 1; + this.zero = (this.parent.CPSR >> Arm7Processor.Z_BIT) & 1; + this.carry = (this.parent.CPSR >> Arm7Processor.C_BIT) & 1; + this.overflow = (this.parent.CPSR >> Arm7Processor.V_BIT) & 1; + } + + private void FlushQueue() + { + this.instructionQueue = this.memory.ReadU32(registers[15]); + registers[15] += 4; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/FastArmCore.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/FastArmCore.cs new file mode 100644 index 0000000000..c944036fb1 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/FastArmCore.cs @@ -0,0 +1,9916 @@ +//#define ARM_DEBUG + +namespace GarboDev +{ + using System; + + public partial class FastArmCore + { + private const uint COND_EQ = 0; // Z set + private const uint COND_NE = 1; // Z clear + private const uint COND_CS = 2; // C set + private const uint COND_CC = 3; // C clear + private const uint COND_MI = 4; // N set + private const uint COND_PL = 5; // N clear + private const uint COND_VS = 6; // V set + private const uint COND_VC = 7; // V clear + private const uint COND_HI = 8; // C set and Z clear + private const uint COND_LS = 9; // C clear or Z set + private const uint COND_GE = 10; // N equals V + private const uint COND_LT = 11; // N not equal to V + private const uint COND_GT = 12; // Z clear AND (N equals V) + private const uint COND_LE = 13; // Z set OR (N not equal to V) + private const uint COND_AL = 14; // Always + private const uint COND_NV = 15; // Never execute + + private const uint OP_AND = 0x0; + private const uint OP_EOR = 0x1; + private const uint OP_SUB = 0x2; + private const uint OP_RSB = 0x3; + private const uint OP_ADD = 0x4; + private const uint OP_ADC = 0x5; + private const uint OP_SBC = 0x6; + private const uint OP_RSC = 0x7; + private const uint OP_TST = 0x8; + private const uint OP_TEQ = 0x9; + private const uint OP_CMP = 0xA; + private const uint OP_CMN = 0xB; + private const uint OP_ORR = 0xC; + private const uint OP_MOV = 0xD; + private const uint OP_BIC = 0xE; + private const uint OP_MVN = 0xF; + + private delegate void ExecuteInstructionDelegate(); + private ExecuteInstructionDelegate[] NormalOps = null; + + private Arm7Processor parent; + private Memory memory; + private uint[] registers; + + private uint instructionQueue; + private uint curInstruction; + + // CPU flags + private uint zero, carry, negative, overflow; + private uint shifterCarry; + + private bool thumbMode; + + public FastArmCore(Arm7Processor parent, Memory memory) + { + this.parent = parent; + this.memory = memory; + this.registers = this.parent.Registers; + + this.NormalOps = new ExecuteInstructionDelegate[8] + { + this.UndefinedInstruction, + this.UndefinedInstruction, + this.UndefinedInstruction, + this.UndefinedInstruction, + this.LoadStoreMultiple, + this.UndefinedInstruction, + this.CoprocessorLoadStore, + this.UndefinedInstruction + }; + + this.InitializeDispatchFunc(); + } + + public void BeginExecution() + { + this.FlushQueue(); + } + + public void Step() + { + this.UnpackFlags(); + + this.curInstruction = this.instructionQueue; + this.instructionQueue = this.memory.ReadU32(registers[15]); + registers[15] += 4; + + uint cond = 0; + switch (this.curInstruction >> 28) + { + case COND_AL: cond = 1; break; + case COND_EQ: cond = zero; break; + case COND_NE: cond = 1 - zero; break; + case COND_CS: cond = carry; break; + case COND_CC: cond = 1 - carry; break; + case COND_MI: cond = negative; break; + case COND_PL: cond = 1 - negative; break; + case COND_VS: cond = overflow; break; + case COND_VC: cond = 1 - overflow; break; + case COND_HI: cond = carry & (1 - zero); break; + case COND_LS: cond = (1 - carry) | zero; break; + case COND_GE: cond = (1 - negative) ^ overflow; break; + case COND_LT: cond = negative ^ overflow; break; + case COND_GT: cond = (1 - zero) & (negative ^ (1 - overflow)); break; + case COND_LE: cond = (negative ^ overflow) | zero; break; + } + + if (cond == 1) + { + // Execute the instruction + this.ExecuteInstruction((ushort)(((this.curInstruction >> 16) & 0xFF0) | ((this.curInstruction >> 4) & 0xF))); + } + + this.parent.Cycles -= this.memory.WaitCycles; + + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.parent.ReloadQueue(); + } + + this.PackFlags(); + } + + public void Execute() + { + this.UnpackFlags(); + this.thumbMode = false; + + while (this.parent.Cycles > 0) + { + this.curInstruction = this.instructionQueue; + this.instructionQueue = this.memory.ReadU32Aligned(registers[15]); + registers[15] += 4; + + if ((this.curInstruction >> 28) == COND_AL) + { + this.fastDispatch[(ushort)(((this.curInstruction >> 16) & 0xFF0) | ((this.curInstruction >> 4) & 0xF))](); +// this.ExecuteInstruction((ushort)(((this.curInstruction >> 16) & 0xFF0) | ((this.curInstruction >> 4) & 0xF))); + } + else + { + uint cond = 0; + switch (this.curInstruction >> 28) + { + case COND_EQ: cond = zero; break; + case COND_NE: cond = 1 - zero; break; + case COND_CS: cond = carry; break; + case COND_CC: cond = 1 - carry; break; + case COND_MI: cond = negative; break; + case COND_PL: cond = 1 - negative; break; + case COND_VS: cond = overflow; break; + case COND_VC: cond = 1 - overflow; break; + case COND_HI: cond = carry & (1 - zero); break; + case COND_LS: cond = (1 - carry) | zero; break; + case COND_GE: cond = (1 - negative) ^ overflow; break; + case COND_LT: cond = negative ^ overflow; break; + case COND_GT: cond = (1 - zero) & (negative ^ (1 - overflow)); break; + case COND_LE: cond = (negative ^ overflow) | zero; break; + } + + if (cond == 1) + { + // Execute the instruction + this.fastDispatch[(ushort)(((this.curInstruction >> 16) & 0xFF0) | ((this.curInstruction >> 4) & 0xF))](); +// this.ExecuteInstruction((ushort)(((this.curInstruction >> 16) & 0xFF0) | ((this.curInstruction >> 4) & 0xF))); + } + } + + this.parent.Cycles -= this.memory.WaitCycles; + + if (this.thumbMode) + { + this.parent.ReloadQueue(); + break; + } + +#if ARM_DEBUG + // Check the current PC + if (this.parent.Breakpoints.ContainsKey(registers[15] - 4U)) + { + this.parent.BreakpointHit = true; + break; + } +#endif + } + + this.PackFlags(); + } + + private void DataProcessingWriteToR15() + { + // Prevent writing if no SPSR exists (this will be true for USER or SYSTEM mode) + if (this.parent.SPSRExists) this.parent.WriteCpsr(this.parent.SPSR); + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + // Otherwise, flush the instruction queue + this.FlushQueue(); + } + + private uint BarrelShifterLslImmed() + { + // rm lsl immed + uint rm = registers[this.curInstruction & 0xF]; + int amount = (int)((this.curInstruction >> 7) & 0x1F); + + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + else + { + this.shifterCarry = (rm >> (32 - amount)) & 1; + return rm << amount; + } + } + + private uint BarrelShifterLslReg() + { + // rm lsl rs + uint rm = registers[this.curInstruction & 0xF]; + uint rs = (this.curInstruction >> 8) & 0xF; + + int amount; + if (rs == 15) + { + amount = (int)((registers[rs] + 0x4) & 0xFF); + } + else + { + amount = (int)(registers[rs] & 0xFF); + } + + if ((this.curInstruction & 0xF) == 15) + { + rm += 4; + } + + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + + if (amount < 32) + { + this.shifterCarry = (rm >> (32 - amount)) & 1; + return rm << amount; + } + else if (amount == 32) + { + this.shifterCarry = rm & 1; + return 0; + } + else + { + this.shifterCarry = 0; + return 0; + } + } + + private uint BarrelShifterLsrImmed() + { + // rm lsr immed + uint rm = registers[this.curInstruction & 0xF]; + int amount = (int)((this.curInstruction >> 7) & 0x1F); + + if (amount == 0) + { + this.shifterCarry = (rm >> 31) & 1; + return 0; + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return rm >> amount; + } + } + + private uint BarrelShifterLsrReg() + { + // rm lsr rs + uint rm = registers[this.curInstruction & 0xF]; + uint rs = (this.curInstruction >> 8) & 0xF; + + int amount; + if (rs == 15) + { + amount = (int)((registers[rs] + 0x4) & 0xFF); + } + else + { + amount = (int)(registers[rs] & 0xFF); + } + + if ((this.curInstruction & 0xF) == 15) + { + rm += 4; + } + + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + + if (amount < 32) + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return rm >> amount; + } + else if (amount == 32) + { + this.shifterCarry = (rm >> 31) & 1; + return 0; + } + else + { + this.shifterCarry = 0; + return 0; + } + } + + private uint BarrelShifterAsrImmed() + { + // rm asr immed + uint rm = registers[this.curInstruction & 0xF]; + int amount = (int)((this.curInstruction >> 7) & 0x1F); + + if (amount == 0) + { + if ((rm & (1 << 31)) == 0) + { + this.shifterCarry = 0; + return 0; + } + else + { + this.shifterCarry = 1; + return 0xFFFFFFFF; + } + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (uint)(((int)rm) >> amount); + } + } + + private uint BarrelShifterAsrReg() + { + // rm asr rs + uint rm = registers[this.curInstruction & 0xF]; + uint rs = (this.curInstruction >> 8) & 0xF; + + int amount; + if (rs == 15) + { + amount = (int)((registers[rs] + 0x4) & 0xFF); + } + else + { + amount = (int)(registers[rs] & 0xFF); + } + + if ((this.curInstruction & 0xF) == 15) + { + rm += 4; + } + + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + + if (amount >= 32) + { + if ((rm & (1 << 31)) == 0) + { + this.shifterCarry = 0; + return 0; + } + else + { + this.shifterCarry = 1; + return 0xFFFFFFFF; + } + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (uint)(((int)rm) >> amount); + } + } + + private uint BarrelShifterRorImmed() + { + // rm ror immed + uint rm = registers[this.curInstruction & 0xF]; + int amount = (int)((this.curInstruction >> 7) & 0x1F); + + if (amount == 0) + { + // Actually an RRX + this.shifterCarry = rm & 1; + return (this.carry << 31) | (rm >> 1); + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (rm >> amount) | (rm << (32 - amount)); + } + } + + private uint BarrelShifterRorReg() + { + // rm ror rs + uint rm = registers[this.curInstruction & 0xF]; + uint rs = (this.curInstruction >> 8) & 0xF; + + int amount; + if (rs == 15) + { + amount = (int)((registers[rs] + 0x4) & 0xFF); + } + else + { + amount = (int)(registers[rs] & 0xFF); + } + + if ((this.curInstruction & 0xF) == 15) + { + rm += 4; + } + + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + + if ((amount & 0x1F) == 0) + { + this.shifterCarry = (rm >> 31) & 1; + return rm; + } + else + { + amount &= 0x1F; + this.shifterCarry = (rm >> amount) & 1; + return (rm >> amount) | (rm << (32 - amount)); + } + } + + private uint BarrelShifterImmed() + { + uint immed = this.curInstruction & 0xFF; + int rotateAmount = (int)(((this.curInstruction >> 8) & 0xF) * 2); + + if (rotateAmount == 0) + { + this.shifterCarry = this.carry; + return immed; + } + else + { + immed = (immed >> rotateAmount) | (immed << (32 - rotateAmount)); + this.shifterCarry = (immed >> 31) & 1; + return immed; + } + } + + private int MultiplyCycleCalculation(uint rs) + { + // Multiply cycle calculations + if ((registers[rs] & 0xFFFFFF00) == 0 || (registers[rs] & 0xFFFFFF00) == 0xFFFFFF00) + { + return 1; + } + else if ((registers[rs] & 0xFFFF0000) == 0 || (registers[rs] & 0xFFFF0000) == 0xFFFF0000) + { + return 2; + } + else if ((registers[rs] & 0xFF000000) == 0 || (registers[rs] & 0xFF000000) == 0xFF000000) + { + return 3; + } + return 4; + } + + private void UndefinedInstruction() + { + // Do the undefined instruction dance here + throw new Exception("Undefined exception"); + } + + private void ExecuteInstruction(ushort op) + { + // Not emulating rn += 4 when register shift, in data operand when rn == pc + + uint rn, rd, rs, rm, address, offset, shifterOperand, alu; + int cycles; + + switch (op) + { + //////////////////////////////////////////////////////////////////////////////////////////// + // + // AND implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x000: + case 0x008: + // AND rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x001: + // AND rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x002: + case 0x00A: + // AND rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x003: + // AND rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x004: + case 0x00C: + // AND rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x005: + // AND rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x006: + case 0x00E: + // AND rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x007: + // AND rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x010: + case 0x018: + // ANDS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x011: + // ANDS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x012: + case 0x01A: + // ANDS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x013: + // ANDS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x014: + case 0x01C: + // ANDS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x015: + // ANDS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x016: + case 0x01E: + // ANDS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x017: + // ANDS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // Undefined instruction implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x00D: case 0x02D: case 0x04D: case 0x06D: + case 0x08D: case 0x0AD: case 0x0CD: case 0x0ED: + case 0x10D: case 0x12D: case 0x14D: case 0x16D: + case 0x18D: case 0x1AD: case 0x1CD: case 0x1ED: + case 0x00F: case 0x02F: case 0x04F: case 0x06F: + case 0x08F: case 0x0AF: case 0x0CF: case 0x0EF: + case 0x10F: case 0x12F: case 0x14F: case 0x16F: + case 0x18F: case 0x1AF: case 0x1CF: case 0x1EF: + UndefinedInstruction(); + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // MUL implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x009: + // MUL rd, rm, rs + rd = (this.curInstruction >> 16) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs); + + registers[rd] = registers[rs] * registers[rm]; + this.parent.Cycles -= cycles; + break; + + case 0x019: + // MULS rd, rm, rs + rd = (this.curInstruction >> 16) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs); + + registers[rd] = registers[rs] * registers[rm]; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + + this.parent.Cycles -= cycles; + break; + + case 0x029: + // MLA rd, rm, rs, rn + rd = (this.curInstruction >> 16) & 0xF; + rn = registers[(this.curInstruction >> 12) & 0xF]; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs); + + registers[rd] = registers[rs] * registers[rm] + rn; + this.parent.Cycles -= cycles + 1; + break; + + case 0x039: + // MLAS rd, rm, rs + rd = (this.curInstruction >> 16) & 0xF; + rn = registers[(this.curInstruction >> 12) & 0xF]; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs); + + registers[rd] = registers[rs] * registers[rm] + rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + + this.parent.Cycles -= cycles + 1; + break; + + case 0x089: + // UMULL rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 1; + + ulong result = ((ulong)registers[rm]) * registers[rs]; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + this.parent.Cycles -= cycles; + } + break; + + case 0x099: + // UMULLS rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 1; + + ulong result = ((ulong)registers[rm]) * registers[rs]; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + break; + + case 0x0A9: + // UMLAL rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 2; + + ulong accum = (((ulong)registers[rdhi]) << 32) | registers[rdlo]; + ulong result = ((ulong)registers[rm]) * registers[rs]; + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + this.parent.Cycles -= cycles; + } + break; + + case 0x0B9: + // UMLALS rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 2; + + ulong accum = (((ulong)registers[rdhi]) << 32) | registers[rdlo]; + ulong result = ((ulong)registers[rm]) * registers[rs]; + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + break; + + case 0x0C9: + // SMULL rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 1; + + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + this.parent.Cycles -= cycles; + } + break; + + case 0x0D9: + // SMULLS rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 1; + + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + break; + + case 0x0E9: + // SMLAL rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 2; + + long accum = (((long)((int)registers[rdhi])) << 32) | registers[rdlo]; + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + this.parent.Cycles -= cycles; + } + break; + + case 0x0F9: + // SMLALS rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 2; + + long accum = (((long)((int)registers[rdhi])) << 32) | registers[rdlo]; + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // STRH implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x00B: + // STRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + break; + + case 0x02B: + // Writeback bit set, instruction is unpredictable + // STRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + break; + + case 0x04B: + // STRH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + break; + + case 0x06B: + // Writeback bit set, instruction is unpredictable + // STRH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + break; + + case 0x08B: + // STRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + break; + + case 0x0AB: + // Writeback bit set, instruction is unpredictable + // STRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + break; + + case 0x0CB: + // STRH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + break; + + case 0x0EB: + // Writeback bit set, instruction is unpredictable + // STRH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + break; + + case 0x10B: + // STRH rd, [rn, -rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + this.memory.WriteU16(registers[rn] + offset, (ushort)(registers[rd] & 0xFFFF)); + break; + + case 0x12B: + // STRH rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rn] += offset; + this.memory.WriteU16(registers[rn], (ushort)(registers[rd] & 0xFFFF)); + break; + + case 0x14B: + // STRH rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + this.memory.WriteU16(registers[rn] + offset, (ushort)(registers[rd] & 0xFFFF)); + break; + + case 0x16B: + // STRH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rn] += offset; + this.memory.WriteU16(registers[rn], (ushort)(registers[rd] & 0xFFFF)); + break; + + case 0x18B: + // STRH rd, [rn, rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + this.memory.WriteU16(registers[rn] + offset, (ushort)(registers[rd] & 0xFFFF)); + break; + + case 0x1AB: + // STRH rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rn] += offset; + this.memory.WriteU16(registers[rn], (ushort)(registers[rd] & 0xFFFF)); + break; + + case 0x1CB: + // STRH rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + this.memory.WriteU16(registers[rn] + offset, (ushort)(registers[rd] & 0xFFFF)); + break; + + case 0x1EB: + // STRH rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rn] += offset; + this.memory.WriteU16(registers[rn], (ushort)(registers[rd] & 0xFFFF)); + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // LDRH implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x01B: + // LDRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x03B: + // Writeback bit set, instruction is unpredictable + // LDRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x05B: + // LDRH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x07B: + // Writeback bit set, instruction is unpredictable + // LDRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x09B: + // LDRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0BB: + // Writeback bit set, instruction is unpredictable + // LDRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0DB: + // LDRH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0FB: + // Writeback bit set, instruction is unpredictable + // LDRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x11B: + // LDRH rd, [rn, -rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x13B: + // LDRH rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rn] += offset; + + registers[rd] = this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x15B: + // LDRH rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x17B: + // LDRH rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x19B: + // LDRH rd, [rn, rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1BB: + // LDRH rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1DB: + // LDRH rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1FB: + // LDRH rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rn] += offset; + registers[rd] = this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // LDRSB implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x01D: + // LDRSB rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x03D: + // Writeback bit set, instruction is unpredictable + // LDRSB rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x05D: + // LDRSB rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x07D: + // Writeback bit set, instruction is unpredictable + // LDRSB rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x09D: + // LDRSB rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0BD: + // Writeback bit set, instruction is unpredictable + // LDRSB rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0DD: + // LDRSB rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0FD: + // Writeback bit set, instruction is unpredictable + // LDRSB rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x11D: + // LDRSB rd, [rn, -rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x13D: + // LDRSB rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x15D: + // LDRSB rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x17D: + // LDRSB rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x19D: + // LDRSB rd, [rn, rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1BD: + // LDRSB rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1DD: + // LDRSB rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1FD: + // LDRSB rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rn] += offset; + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // LDRSH implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x01F: + // LDRSH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x03F: + // Writeback bit set, instruction is unpredictable + // LDRSH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x05F: + // LDRSH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x07F: + // Writeback bit set, instruction is unpredictable + // LDRSH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x09F: + // LDRSH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0BF: + // Writeback bit set, instruction is unpredictable + // LDRSH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0DF: + // LDRSH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x0FF: + // Writeback bit set, instruction is unpredictable + // LDRSH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + break; + + case 0x11F: + // LDRSH rd, [rn, -rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x13F: + // LDRSH rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x15F: + // LDRSH rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x17F: + // LDRSH rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x19F: + // LDRSH rd, [rn, rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1BF: + // LDRSH rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rn] += offset; + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1DF: + // LDRSH rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x1FF: + // LDRSH rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rn] += offset; + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // EOR implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x020: + case 0x028: + // EOR rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x021: + // EOR rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x022: + case 0x02A: + // EOR rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x023: + // EOR rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x024: + case 0x02C: + // EOR rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x025: + // EOR rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x026: + case 0x02E: + // EOR rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x027: + // EOR rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x030: + case 0x038: + // EORS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x031: + // EORS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x032: + case 0x03A: + // EORS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x033: + // EORS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x034: + case 0x03C: + // EORS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x035: + // EORS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x036: + case 0x03E: + // EORS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x037: + // EORS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // SUB implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x040: + case 0x048: + // SUB rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x041: + // SUB rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x042: + case 0x04A: + // SUB rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x043: + // SUB rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x044: + case 0x04C: + // SUB rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x045: + // SUB rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x046: + case 0x04E: + // SUB rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x047: + // SUB rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x050: + case 0x058: + // SUBS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x051: + // SUBS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x052: + case 0x05A: + // SUBS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x053: + // SUBS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x054: + case 0x05C: + // SUBS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x055: + // SUBS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x056: + case 0x05E: + // SUBS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x057: + // SUBS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // RSB implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x060: + case 0x068: + // RSB rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x061: + // RSB rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslReg() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x062: + case 0x06A: + // RSB rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x063: + // RSB rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrReg() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x064: + case 0x06C: + // RSB rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x065: + // RSB rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrReg() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x066: + case 0x06E: + // RSB rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x067: + // RSB rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorReg() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x070: + case 0x078: + // RSBS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x071: + // RSBS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x072: + case 0x07A: + // RSBS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x073: + // RSBS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x074: + case 0x07C: + // RSBS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x075: + // RSBS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x076: + case 0x07E: + // RSBS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x077: + // RSBS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // ADD implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x080: + case 0x088: + // ADD rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x081: + // ADD rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x082: + case 0x08A: + // ADD rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x083: + // ADD rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x084: + case 0x08C: + // ADD rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x085: + // ADD rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x086: + case 0x08E: + // ADD rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x087: + // ADD rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x090: + case 0x098: + // ADDS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x091: + // ADDS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x092: + case 0x09A: + // ADDS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x093: + // ADDS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x094: + case 0x09C: + // ADDS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x095: + // ADDS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x096: + case 0x09E: + // ADDS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x097: + // ADDS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // ADC implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x0A0: + case 0x0A8: + // ADC rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLslImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0A1: + // ADC rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLslReg() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0A2: + case 0x0AA: + // ADC rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLsrImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0A3: + // ADC rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLsrReg() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0A4: + case 0x0AC: + // ADC rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterAsrImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0A5: + // ADC rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterAsrReg() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0A6: + case 0x0AE: + // ADC rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterRorImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0A7: + // ADC rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterRorReg() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0B0: + case 0x0B8: + // ADCS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0B1: + // ADCS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0B2: + case 0x0BA: + // ADCS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0B3: + // ADCS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0B4: + case 0x0BC: + // ADCS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0B5: + // ADCS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0B6: + case 0x0BE: + // ADCS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0B7: + // ADCS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // SBC implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x0C0: + case 0x0C8: + // SBC rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLslImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0C1: + // SBC rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLslReg() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0C2: + case 0x0CA: + // SBC rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLsrImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0C3: + // SBC rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLsrReg() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0C4: + case 0x0CC: + // SBC rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterAsrImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0C5: + // SBC rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterAsrReg() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0C6: + case 0x0CE: + // SBC rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterRorImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0C7: + // SBC rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterRorReg() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0D0: + case 0x0D8: + // SBCS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0D1: + // SBCS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0D2: + case 0x0DA: + // SBCS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0D3: + // SBCS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0D4: + case 0x0DC: + // SBCS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0D5: + // SBCS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0D6: + case 0x0DE: + // SBCS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0D7: + // SBCS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // RSC implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x0E0: + case 0x0E8: + // RSC rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0E1: + // RSC rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslReg() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0E2: + case 0x0EA: + // RSC rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0E3: + // RSC rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrReg() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0E4: + case 0x0EC: + // RSC rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0E5: + // RSC rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrReg() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0E6: + case 0x0EE: + // RSC rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0E7: + // RSC rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorReg() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x0F0: + case 0x0F8: + // RSCS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0F1: + // RSCS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0F2: + case 0x0FA: + // RSCS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0F3: + // RSCS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0F4: + case 0x0FC: + // RSCS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0F5: + // RSCS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0F6: + case 0x0FE: + // RSCS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x0F7: + // RSCS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // Misc. instructions (MSR, MRS, SWP, BX) + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x109: + // SWP rd, rm, [rn] + { + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + rm = this.curInstruction & 0xF; + uint tmp = this.memory.ReadU32(registers[rn]); + this.memory.WriteU32(registers[rn], registers[rm]); + registers[rd] = tmp; + + if (rd == 15) + { + this.FlushQueue(); + } + } + break; + + case 0x149: + // SWPB rd, rm, [rn] + { + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + rm = this.curInstruction & 0xF; + + byte tmp = this.memory.ReadU8(registers[rn]); + this.memory.WriteU8(registers[rn], (byte)(registers[rm] & 0xFF)); + registers[rd] = tmp; + + if (rd == 15) + { + this.FlushQueue(); + } + } + break; + + case 0x100: + // MRS rd, cpsr + rd = (this.curInstruction >> 12) & 0xF; + this.PackFlags(); + registers[rd] = this.parent.CPSR; + break; + + case 0x140: + // MRS rd, spsr + rd = (this.curInstruction >> 12) & 0xF; + if (this.parent.SPSRExists) registers[rd] = this.parent.SPSR; + break; + + case 0x120: + // MSR cpsr, rm + { + rm = registers[this.curInstruction & 0xF]; + bool userMode = (this.parent.CPSR & 0x1F) == Arm7Processor.USR; + + this.PackFlags(); + + uint tmpCPSR = this.parent.CPSR; + + if ((this.curInstruction & (1 << 16)) == 1 << 16 && !userMode) + { + tmpCPSR &= 0xFFFFFF00; + tmpCPSR |= rm & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17 && !userMode) + { + tmpCPSR &= 0xFFFF00FF; + tmpCPSR |= rm & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18 && !userMode) + { + tmpCPSR &= 0xFF00FFFF; + tmpCPSR |= rm & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + tmpCPSR &= 0x00FFFFFF; + tmpCPSR |= rm & 0xFF000000; + } + + this.parent.WriteCpsr(tmpCPSR); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + } + break; + + case 0x160: + // MSR spsr, rm + if (this.parent.SPSRExists) + { + rm = registers[this.curInstruction & 0xF]; + if ((this.curInstruction & (1 << 16)) == 1 << 16) + { + this.parent.SPSR &= 0xFFFFFF00; + this.parent.SPSR |= rm & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17) + { + this.parent.SPSR &= 0xFFFF00FF; + this.parent.SPSR |= rm & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18) + { + this.parent.SPSR &= 0xFF00FFFF; + this.parent.SPSR |= rm & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + this.parent.SPSR &= 0x00FFFFFF; + this.parent.SPSR |= rm & 0xFF000000; + } + } + break; + + case 0x320: + // MSR cpsr, immed + { + uint immed = this.curInstruction & 0xFF; + int rotateAmount = (int)(((this.curInstruction >> 8) & 0xF) * 2); + + immed = (immed >> rotateAmount) | (immed << (32 - rotateAmount)); + + bool userMode = (this.parent.CPSR & 0x1F) == Arm7Processor.USR; + + this.PackFlags(); + + uint tmpCPSR = this.parent.CPSR; + + if ((this.curInstruction & (1 << 16)) == 1 << 16 && !userMode) + { + tmpCPSR &= 0xFFFFFF00; + tmpCPSR |= immed & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17 && !userMode) + { + tmpCPSR &= 0xFFFF00FF; + tmpCPSR |= immed & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18 && !userMode) + { + tmpCPSR &= 0xFF00FFFF; + tmpCPSR |= immed & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + tmpCPSR &= 0x00FFFFFF; + tmpCPSR |= immed & 0xFF000000; + } + + this.parent.WriteCpsr(tmpCPSR); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + } + break; + + case 0x360: + // MSR spsr, immed + if (this.parent.SPSRExists) + { + uint immed = this.curInstruction & 0xFF; + int rotateAmount = (int)(((this.curInstruction >> 8) & 0xF) * 2); + + immed = (immed >> rotateAmount) | (immed << (32 - rotateAmount)); + + if ((this.curInstruction & (1 << 16)) == 1 << 16) + { + this.parent.SPSR &= 0xFFFFFF00; + this.parent.SPSR |= immed & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17) + { + this.parent.SPSR &= 0xFFFF00FF; + this.parent.SPSR |= immed & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18) + { + this.parent.SPSR &= 0xFF00FFFF; + this.parent.SPSR |= immed & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + this.parent.SPSR &= 0x00FFFFFF; + this.parent.SPSR |= immed & 0xFF000000; + } + } + break; + + case 0x121: + // BX rm + rm = this.curInstruction & 0xf; + + this.PackFlags(); + + this.parent.CPSR &= ~Arm7Processor.T_MASK; + this.parent.CPSR |= (registers[rm] & 1) << Arm7Processor.T_BIT; + + registers[15] = registers[rm] & (~1U); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + this.FlushQueue(); + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // TST implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x110: + case 0x118: + // TSTS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterLslImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x111: + // TSTS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterLslReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x112: + case 0x11A: + // TSTS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterLsrImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x113: + // TSTS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterLsrReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x114: + case 0x11C: + // TSTS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterAsrImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x115: + // TSTS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterAsrReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x116: + case 0x11E: + // TSTS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterRorImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x117: + // TSTS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + alu = rn & BarrelShifterRorReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // TEQ implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x130: + case 0x138: + // TEQS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterLslImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x131: + // TEQS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterLslReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x132: + case 0x13A: + // TEQS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterLsrImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x133: + // TEQS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterLsrReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x134: + case 0x13C: + // TEQS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterAsrImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x135: + // TEQS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterAsrReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x136: + case 0x13E: + // TEQS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterRorImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x137: + // TEQS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + alu = rn ^ BarrelShifterRorReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // CMP implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x150: + case 0x158: + // CMP rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLslImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case 0x151: + // CMP rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLslReg(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case 0x152: + case 0x15A: + // CMP rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLsrImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case 0x153: + // CMP rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLsrReg(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case 0x154: + case 0x15C: + // CMP rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterAsrImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case 0x155: + // CMP rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterAsrReg(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case 0x156: + case 0x15E: + // CMP rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterRorImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case 0x157: + // CMP rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterRorReg(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // CMN implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x170: + case 0x178: + // CMN rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLslImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case 0x171: + // CMN rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLslReg(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case 0x172: + case 0x17A: + // CMN rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLsrImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case 0x173: + // CMN rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLsrReg(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case 0x174: + case 0x17C: + // CMN rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterAsrImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case 0x175: + // CMN rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterAsrReg(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case 0x176: + case 0x17E: + // CMN rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterRorImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case 0x177: + // CMN rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterRorReg(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // ORR implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x180: + case 0x188: + // ORR rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x181: + // ORR rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x182: + case 0x18A: + // ORR rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x183: + // ORR rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x184: + case 0x18C: + // ORR rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x185: + // ORR rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x186: + case 0x18E: + // ORR rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x187: + // ORR rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x190: + case 0x198: + // ORRS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x191: + // ORRS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x192: + case 0x19A: + // ORRS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x193: + // ORRS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x194: + case 0x19C: + // ORRS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x195: + // ORRS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x196: + case 0x19E: + // ORRS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x197: + // ORRS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // MOV implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x1A0: + case 0x1A8: + // MOV rd, rm lsl immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1A1: + // MOV rd, rm lsl rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1A2: + case 0x1AA: + // MOV rd, rm lsr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1A3: + // MOV rd, rm lsr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1A4: + case 0x1AC: + // MOV rd, rm asr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1A5: + // MOV rd, rm asr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1A6: + case 0x1AE: + // MOV rd, rm ror immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1A7: + // MOV rd, rm ror rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1B0: + case 0x1B8: + // MOVS rd, rm lsl immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1B1: + // MOVS rd, rm lsl rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1B2: + case 0x1BA: + // MOVS rd, rm lsr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1B3: + // MOVS rd, rn, rm lsr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1B4: + case 0x1BC: + // MOVS rd, rn, rm asr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1B5: + // MOVS rd, rn, rm asr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1B6: + case 0x1BE: + // MOVS rd, rn, rm ror immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1B7: + // MOVS rd, rn, rm ror rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // BIC implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x1C0: + case 0x1C8: + // BIC rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1C1: + // BIC rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1C2: + case 0x1CA: + // BIC rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1C3: + // BIC rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1C4: + case 0x1CC: + // BIC rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1C5: + // BIC rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1C6: + case 0x1CE: + // BIC rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1C7: + // BIC rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1D0: + case 0x1D8: + // BICS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1D1: + // BICS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1D2: + case 0x1DA: + // BICS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1D3: + // BICS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1D4: + case 0x1DC: + // BICS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1D5: + // BICS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1D6: + case 0x1DE: + // BICS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1D7: + // BICS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // MVN implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x1E0: + case 0x1E8: + // MVN rd, rm lsl immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1E1: + // MVN rd, rm lsl rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1E2: + case 0x1EA: + // MVN rd, rm lsr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1E3: + // MVN rd, rm lsr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1E4: + case 0x1EC: + // MVN rd, rm asr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1E5: + // MVN rd, rm asr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1E6: + case 0x1EE: + // MVN rd, rm ror immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1E7: + // MVN rd, rm ror rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x1F0: + case 0x1F8: + // MVNS rd, rm lsl immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1F1: + // MVNS rd, rm lsl rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1F2: + case 0x1FA: + // MVNS rd, rm lsr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1F3: + // MVNS rd, rn, rm lsr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1F4: + case 0x1FC: + // MVNS rd, rn, rm asr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1F5: + // MVNS rd, rn, rm asr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1F6: + case 0x1FE: + // MVNS rd, rn, rm ror immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x1F7: + // MVNS rd, rn, rm ror rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // Data processing immediate operand implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x200: case 0x201: case 0x202: case 0x203: case 0x204: case 0x205: case 0x206: case 0x207: + case 0x208: case 0x209: case 0x20A: case 0x20B: case 0x20C: case 0x20D: case 0x20E: case 0x20F: + // AND rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn & BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x210: case 0x211: case 0x212: case 0x213: case 0x214: case 0x215: case 0x216: case 0x217: + case 0x218: case 0x219: case 0x21A: case 0x21B: case 0x21C: case 0x21D: case 0x21E: case 0x21F: + // ANDS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn & BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x220: case 0x221: case 0x222: case 0x223: case 0x224: case 0x225: case 0x226: case 0x227: + case 0x228: case 0x229: case 0x22A: case 0x22B: case 0x22C: case 0x22D: case 0x22E: case 0x22F: + // EOR rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn ^ BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x230: case 0x231: case 0x232: case 0x233: case 0x234: case 0x235: case 0x236: case 0x237: + case 0x238: case 0x239: case 0x23A: case 0x23B: case 0x23C: case 0x23D: case 0x23E: case 0x23F: + // EORS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn ^ BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x240: case 0x241: case 0x242: case 0x243: case 0x244: case 0x245: case 0x246: case 0x247: + case 0x248: case 0x249: case 0x24A: case 0x24B: case 0x24C: case 0x24D: case 0x24E: case 0x24F: + // SUB rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn - BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x250: case 0x251: case 0x252: case 0x253: case 0x254: case 0x255: case 0x256: case 0x257: + case 0x258: case 0x259: case 0x25A: case 0x25B: case 0x25C: case 0x25D: case 0x25E: case 0x25F: + // SUBS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x260: case 0x261: case 0x262: case 0x263: case 0x264: case 0x265: case 0x266: case 0x267: + case 0x268: case 0x269: case 0x26A: case 0x26B: case 0x26C: case 0x26D: case 0x26E: case 0x26F: + // RSB rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = BarrelShifterImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x270: case 0x271: case 0x272: case 0x273: case 0x274: case 0x275: case 0x276: case 0x277: + case 0x278: case 0x279: case 0x27A: case 0x27B: case 0x27C: case 0x27D: case 0x27E: case 0x27F: + // RSBS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x280: case 0x281: case 0x282: case 0x283: case 0x284: case 0x285: case 0x286: case 0x287: + case 0x288: case 0x289: case 0x28A: case 0x28B: case 0x28C: case 0x28D: case 0x28E: case 0x28F: + // ADD rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn + BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x290: case 0x291: case 0x292: case 0x293: case 0x294: case 0x295: case 0x296: case 0x297: + case 0x298: case 0x299: case 0x29A: case 0x29B: case 0x29C: case 0x29D: case 0x29E: case 0x29F: + // ADDS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x2A0: case 0x2A1: case 0x2A2: case 0x2A3: case 0x2A4: case 0x2A5: case 0x2A6: case 0x2A7: + case 0x2A8: case 0x2A9: case 0x2AA: case 0x2AB: case 0x2AC: case 0x2AD: case 0x2AE: case 0x2AF: + // ADC rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn + BarrelShifterImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x2B0: case 0x2B1: case 0x2B2: case 0x2B3: case 0x2B4: case 0x2B5: case 0x2B6: case 0x2B7: + case 0x2B8: case 0x2B9: case 0x2BA: case 0x2BB: case 0x2BC: case 0x2BD: case 0x2BE: case 0x2BF: + // ADCS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x2C0: case 0x2C1: case 0x2C2: case 0x2C3: case 0x2C4: case 0x2C5: case 0x2C6: case 0x2C7: + case 0x2C8: case 0x2C9: case 0x2CA: case 0x2CB: case 0x2CC: case 0x2CD: case 0x2CE: case 0x2CF: + // SBC rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn - BarrelShifterImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x2D0: case 0x2D1: case 0x2D2: case 0x2D3: case 0x2D4: case 0x2D5: case 0x2D6: case 0x2D7: + case 0x2D8: case 0x2D9: case 0x2DA: case 0x2DB: case 0x2DC: case 0x2DD: case 0x2DE: case 0x2DF: + // SBCS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x2E0: case 0x2E1: case 0x2E2: case 0x2E3: case 0x2E4: case 0x2E5: case 0x2E6: case 0x2E7: + case 0x2E8: case 0x2E9: case 0x2EA: case 0x2EB: case 0x2EC: case 0x2ED: case 0x2EE: case 0x2EF: + // RSC rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = BarrelShifterImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x2F0: case 0x2F1: case 0x2F2: case 0x2F3: case 0x2F4: case 0x2F5: case 0x2F6: case 0x2F7: + case 0x2F8: case 0x2F9: case 0x2FA: case 0x2FB: case 0x2FC: case 0x2FD: case 0x2FE: case 0x2FF: + // RSCS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x310: case 0x311: case 0x312: case 0x313: case 0x314: case 0x315: case 0x316: case 0x317: + case 0x318: case 0x319: case 0x31A: case 0x31B: case 0x31C: case 0x31D: case 0x31E: case 0x31F: + // TSTS rn, immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x330: case 0x331: case 0x332: case 0x333: case 0x334: case 0x335: case 0x336: case 0x337: + case 0x338: case 0x339: case 0x33A: case 0x33B: case 0x33C: case 0x33D: case 0x33E: case 0x33F: + // TEQS rn, immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case 0x350: case 0x351: case 0x352: case 0x353: case 0x354: case 0x355: case 0x356: case 0x357: + case 0x358: case 0x359: case 0x35A: case 0x35B: case 0x35C: case 0x35D: case 0x35E: case 0x35F: + // CMP rn, immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case 0x370: case 0x371: case 0x372: case 0x373: case 0x374: case 0x375: case 0x376: case 0x377: + case 0x378: case 0x379: case 0x37A: case 0x37B: case 0x37C: case 0x37D: case 0x37E: case 0x37F: + // CMN rn, immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case 0x380: case 0x381: case 0x382: case 0x383: case 0x384: case 0x385: case 0x386: case 0x387: + case 0x388: case 0x389: case 0x38A: case 0x38B: case 0x38C: case 0x38D: case 0x38E: case 0x38F: + // ORR rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn | BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x390: case 0x391: case 0x392: case 0x393: case 0x394: case 0x395: case 0x396: case 0x397: + case 0x398: case 0x399: case 0x39A: case 0x39B: case 0x39C: case 0x39D: case 0x39E: case 0x39F: + // ORRS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn | BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x3A0: case 0x3A1: case 0x3A2: case 0x3A3: case 0x3A4: case 0x3A5: case 0x3A6: case 0x3A7: + case 0x3A8: case 0x3A9: case 0x3AA: case 0x3AB: case 0x3AC: case 0x3AD: case 0x3AE: case 0x3AF: + // MOV rd, immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x3B0: case 0x3B1: case 0x3B2: case 0x3B3: case 0x3B4: case 0x3B5: case 0x3B6: case 0x3B7: + case 0x3B8: case 0x3B9: case 0x3BA: case 0x3BB: case 0x3BC: case 0x3BD: case 0x3BE: case 0x3BF: + // MOVS rd, immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x3C0: case 0x3C1: case 0x3C2: case 0x3C3: case 0x3C4: case 0x3C5: case 0x3C6: case 0x3C7: + case 0x3C8: case 0x3C9: case 0x3CA: case 0x3CB: case 0x3CC: case 0x3CD: case 0x3CE: case 0x3CF: + // BIC rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn & ~BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x3D0: case 0x3D1: case 0x3D2: case 0x3D3: case 0x3D4: case 0x3D5: case 0x3D6: case 0x3D7: + case 0x3D8: case 0x3D9: case 0x3DA: case 0x3DB: case 0x3DC: case 0x3DD: case 0x3DE: case 0x3DF: + // BICS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn & ~BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + case 0x3E0: case 0x3E1: case 0x3E2: case 0x3E3: case 0x3E4: case 0x3E5: case 0x3E6: case 0x3E7: + case 0x3E8: case 0x3E9: case 0x3EA: case 0x3EB: case 0x3EC: case 0x3ED: case 0x3EE: case 0x3EF: + // MVN rd, immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + break; + + case 0x3F0: case 0x3F1: case 0x3F2: case 0x3F3: case 0x3F4: case 0x3F5: case 0x3F6: case 0x3F7: + case 0x3F8: case 0x3F9: case 0x3FA: case 0x3FB: case 0x3FC: case 0x3FD: case 0x3FE: case 0x3FF: + // MVNS rd, immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // STR immediate implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x400: case 0x401: case 0x402: case 0x403: case 0x404: case 0x405: case 0x406: case 0x407: + case 0x408: case 0x409: case 0x40A: case 0x40B: case 0x40C: case 0x40D: case 0x40E: case 0x40F: + // STR rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x420: case 0x421: case 0x422: case 0x423: case 0x424: case 0x425: case 0x426: case 0x427: + case 0x428: case 0x429: case 0x42A: case 0x42B: case 0x42C: case 0x42D: case 0x42E: case 0x42F: + // STRT rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x440: case 0x441: case 0x442: case 0x443: case 0x444: case 0x445: case 0x446: case 0x447: + case 0x448: case 0x449: case 0x44A: case 0x44B: case 0x44C: case 0x44D: case 0x44E: case 0x44F: + // STRB rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x460: case 0x461: case 0x462: case 0x463: case 0x464: case 0x465: case 0x466: case 0x467: + case 0x468: case 0x469: case 0x46A: case 0x46B: case 0x46C: case 0x46D: case 0x46E: case 0x46F: + // STRBT rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x480: case 0x481: case 0x482: case 0x483: case 0x484: case 0x485: case 0x486: case 0x487: + case 0x488: case 0x489: case 0x48A: case 0x48B: case 0x48C: case 0x48D: case 0x48E: case 0x48F: + // STR rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.curInstruction & 0xFFF; + break; + + case 0x4A0: case 0x4A1: case 0x4A2: case 0x4A3: case 0x4A4: case 0x4A5: case 0x4A6: case 0x4A7: + case 0x4A8: case 0x4A9: case 0x4AA: case 0x4AB: case 0x4AC: case 0x4AD: case 0x4AE: case 0x4AF: + // STRT rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.curInstruction & 0xFFF; + break; + + case 0x4C0: case 0x4C1: case 0x4C2: case 0x4C3: case 0x4C4: case 0x4C5: case 0x4C6: case 0x4C7: + case 0x4C8: case 0x4C9: case 0x4CA: case 0x4CB: case 0x4CC: case 0x4CD: case 0x4CE: case 0x4CF: + // STRB rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.curInstruction & 0xFFF; + break; + + case 0x4E0: case 0x4E1: case 0x4E2: case 0x4E3: case 0x4E4: case 0x4E5: case 0x4E6: case 0x4E7: + case 0x4E8: case 0x4E9: case 0x4EA: case 0x4EB: case 0x4EC: case 0x4ED: case 0x4EE: case 0x4EF: + // STRBT rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.curInstruction & 0xFFF; + break; + + case 0x500: case 0x501: case 0x502: case 0x503: case 0x504: case 0x505: case 0x506: case 0x507: + case 0x508: case 0x509: case 0x50A: case 0x50B: case 0x50C: case 0x50D: case 0x50E: case 0x50F: + // STR rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + break; + + case 0x520: case 0x521: case 0x522: case 0x523: case 0x524: case 0x525: case 0x526: case 0x527: + case 0x528: case 0x529: case 0x52A: case 0x52B: case 0x52C: case 0x52D: case 0x52E: case 0x52F: + // STR rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x540: case 0x541: case 0x542: case 0x543: case 0x544: case 0x545: case 0x546: case 0x547: + case 0x548: case 0x549: case 0x54A: case 0x54B: case 0x54C: case 0x54D: case 0x54E: case 0x54F: + // STRB rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + break; + + case 0x560: case 0x561: case 0x562: case 0x563: case 0x564: case 0x565: case 0x566: case 0x567: + case 0x568: case 0x569: case 0x56A: case 0x56B: case 0x56C: case 0x56D: case 0x56E: case 0x56F: + // STRB rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + case 0x580: case 0x581: case 0x582: case 0x583: case 0x584: case 0x585: case 0x586: case 0x587: + case 0x588: case 0x589: case 0x58A: case 0x58B: case 0x58C: case 0x58D: case 0x58E: case 0x58F: + // STR rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + (this.curInstruction & 0xFFF), alu); + break; + + case 0x5A0: case 0x5A1: case 0x5A2: case 0x5A3: case 0x5A4: case 0x5A5: case 0x5A6: case 0x5A7: + case 0x5A8: case 0x5A9: case 0x5AA: case 0x5AB: case 0x5AC: case 0x5AD: case 0x5AE: case 0x5AF: + // STRT rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.curInstruction & 0xFFF; + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x5C0: case 0x5C1: case 0x5C2: case 0x5C3: case 0x5C4: case 0x5C5: case 0x5C6: case 0x5C7: + case 0x5C8: case 0x5C9: case 0x5CA: case 0x5CB: case 0x5CC: case 0x5CD: case 0x5CE: case 0x5CF: + // STRB rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + (this.curInstruction & 0xFFF), (byte)(alu & 0xFF)); + break; + + case 0x5E0: case 0x5E1: case 0x5E2: case 0x5E3: case 0x5E4: case 0x5E5: case 0x5E6: case 0x5E7: + case 0x5E8: case 0x5E9: case 0x5EA: case 0x5EB: case 0x5EC: case 0x5ED: case 0x5EE: case 0x5EF: + // STRB rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.curInstruction & 0xFFF; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // LDR immediate implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x410: case 0x411: case 0x412: case 0x413: case 0x414: case 0x415: case 0x416: case 0x417: + case 0x418: case 0x419: case 0x41A: case 0x41B: case 0x41C: case 0x41D: case 0x41E: case 0x41F: + // LDR rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x430: case 0x431: case 0x432: case 0x433: case 0x434: case 0x435: case 0x436: case 0x437: + case 0x438: case 0x439: case 0x43A: case 0x43B: case 0x43C: case 0x43D: case 0x43E: case 0x43F: + // LDRT rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x450: case 0x451: case 0x452: case 0x453: case 0x454: case 0x455: case 0x456: case 0x457: + case 0x458: case 0x459: case 0x45A: case 0x45B: case 0x45C: case 0x45D: case 0x45E: case 0x45F: + // LDRB rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x470: case 0x471: case 0x472: case 0x473: case 0x474: case 0x475: case 0x476: case 0x477: + case 0x478: case 0x479: case 0x47A: case 0x47B: case 0x47C: case 0x47D: case 0x47E: case 0x47F: + // LDRBT rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x490: case 0x491: case 0x492: case 0x493: case 0x494: case 0x495: case 0x496: case 0x497: + case 0x498: case 0x499: case 0x49A: case 0x49B: case 0x49C: case 0x49D: case 0x49E: case 0x49F: + // LDR rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += this.curInstruction & 0xFFF; + break; + + case 0x4B0: case 0x4B1: case 0x4B2: case 0x4B3: case 0x4B4: case 0x4B5: case 0x4B6: case 0x4B7: + case 0x4B8: case 0x4B9: case 0x4BA: case 0x4BB: case 0x4BC: case 0x4BD: case 0x4BE: case 0x4BF: + // LDRT rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += this.curInstruction & 0xFFF; + break; + + case 0x4D0: case 0x4D1: case 0x4D2: case 0x4D3: case 0x4D4: case 0x4D5: case 0x4D6: case 0x4D7: + case 0x4D8: case 0x4D9: case 0x4DA: case 0x4DB: case 0x4DC: case 0x4DD: case 0x4DE: case 0x4DF: + // LDRB rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += this.curInstruction & 0xFFF; + break; + + case 0x4F0: case 0x4F1: case 0x4F2: case 0x4F3: case 0x4F4: case 0x4F5: case 0x4F6: case 0x4F7: + case 0x4F8: case 0x4F9: case 0x4FA: case 0x4FB: case 0x4FC: case 0x4FD: case 0x4FE: case 0x4FF: + // LDRBT rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += this.curInstruction & 0xFFF; + break; + + case 0x510: case 0x511: case 0x512: case 0x513: case 0x514: case 0x515: case 0x516: case 0x517: + case 0x518: case 0x519: case 0x51A: case 0x51B: case 0x51C: case 0x51D: case 0x51E: case 0x51F: + // LDR rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x530: case 0x531: case 0x532: case 0x533: case 0x534: case 0x535: case 0x536: case 0x537: + case 0x538: case 0x539: case 0x53A: case 0x53B: case 0x53C: case 0x53D: case 0x53E: case 0x53F: + // LDR rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x550: case 0x551: case 0x552: case 0x553: case 0x554: case 0x555: case 0x556: case 0x557: + case 0x558: case 0x559: case 0x55A: case 0x55B: case 0x55C: case 0x55D: case 0x55E: case 0x55F: + // LDRB rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x570: case 0x571: case 0x572: case 0x573: case 0x574: case 0x575: case 0x576: case 0x577: + case 0x578: case 0x579: case 0x57A: case 0x57B: case 0x57C: case 0x57D: case 0x57E: case 0x57F: + // LDRB rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x590: case 0x591: case 0x592: case 0x593: case 0x594: case 0x595: case 0x596: case 0x597: + case 0x598: case 0x599: case 0x59A: case 0x59B: case 0x59C: case 0x59D: case 0x59E: case 0x59F: + // LDR rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + (this.curInstruction & 0xFFF)); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x5B0: case 0x5B1: case 0x5B2: case 0x5B3: case 0x5B4: case 0x5B5: case 0x5B6: case 0x5B7: + case 0x5B8: case 0x5B9: case 0x5BA: case 0x5BB: case 0x5BC: case 0x5BD: case 0x5BE: case 0x5BF: + // LDR rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.curInstruction & 0xFFF; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x5D0: case 0x5D1: case 0x5D2: case 0x5D3: case 0x5D4: case 0x5D5: case 0x5D6: case 0x5D7: + case 0x5D8: case 0x5D9: case 0x5DA: case 0x5DB: case 0x5DC: case 0x5DD: case 0x5DE: case 0x5DF: + // LDRB rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + (this.curInstruction & 0xFFF)); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x5F0: case 0x5F1: case 0x5F2: case 0x5F3: case 0x5F4: case 0x5F5: case 0x5F6: case 0x5F7: + case 0x5F8: case 0x5F9: case 0x5FA: case 0x5FB: case 0x5FC: case 0x5FD: case 0x5FE: case 0x5FF: + // LDRB rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.curInstruction & 0xFFF; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // STR register shift implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x600: + case 0x608: + // STR rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x602: + case 0x60A: + // STR rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x604: + case 0x60C: + // STR rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x606: + case 0x60E: + // STR rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x620: + case 0x628: + // STRT rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x622: + case 0x62A: + // STRT rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x624: + case 0x62C: + // STRT rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x626: + case 0x62E: + // STRT rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + break; + + case 0x640: + case 0x648: + // STRB rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x642: + case 0x64A: + // STRB rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x644: + case 0x64C: + // STRB rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x646: + case 0x64E: + // STRB rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x660: + case 0x668: + // STRBT rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x662: + case 0x66A: + // STRBT rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x664: + case 0x66C: + // STRBT rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x666: + case 0x66E: + // STRBT rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + break; + + case 0x680: + case 0x688: + // STR rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterLslImmed(); + break; + + case 0x682: + case 0x68A: + // STR rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterLsrImmed(); + break; + + case 0x684: + case 0x68C: + // STR rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterAsrImmed(); + break; + + case 0x686: + case 0x68E: + // STR rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterRorImmed(); + break; + + case 0x6A0: + case 0x6A2: + // STRT rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterLslImmed(); + break; + + case 0x6A4: + case 0x6A6: + // STRT rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterLsrImmed(); + break; + + case 0x6A8: + case 0x6AA: + // STRT rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterAsrImmed(); + break; + + case 0x6AC: + case 0x6AE: + // STRT rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterRorImmed(); + break; + + case 0x6C0: + case 0x6C8: + // STRB rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterLslImmed(); + break; + + case 0x6C2: + case 0x6CA: + // STRB rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterLsrImmed(); + break; + + case 0x6C4: + case 0x6CC: + // STRB rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterAsrImmed(); + break; + + case 0x6C6: + case 0x6CE: + // STRB rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterRorImmed(); + break; + + case 0x6E0: + case 0x6E8: + // STRBT rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterLslImmed(); + break; + + case 0x6E2: + case 0x6EA: + // STRBT rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterLsrImmed(); + break; + + case 0x6E4: + case 0x6EC: + // STRBT rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterAsrImmed(); + break; + + case 0x6E6: + case 0x6EE: + // STRBT rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterRorImmed(); + break; + + case 0x700: + case 0x708: + // STR rd, [rn, -rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + break; + + case 0x702: + case 0x70A: + // STR rd, [rn, -rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + break; + + case 0x704: + case 0x70C: + // STR rd, [rn, -rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + break; + + case 0x706: + case 0x70E: + // STR rd, [rn, -rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + break; + + case 0x720: + case 0x728: + // STR rd, [rn, -rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x722: + case 0x72A: + // STR rd, [rn, -rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x724: + case 0x72C: + // STR rd, [rn, -rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x726: + case 0x72E: + // STR rd, [rn, -rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x740: + case 0x748: + // STRB rd, [rn, -rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + break; + + case 0x742: + case 0x74A: + // STRB rd, [rn, -rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + break; + + case 0x744: + case 0x74C: + // STRB rd, [rn, -rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + break; + + case 0x746: + case 0x74E: + // STRB rd, [rn, -rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + break; + + case 0x760: + case 0x768: + // STRB rd, [rn, -rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + case 0x762: + case 0x76A: + // STRB rd, [rn, -rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + case 0x764: + case 0x76C: + // STRB rd, [rn, -rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + case 0x766: + case 0x76E: + // STRB rd, [rn, -rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + case 0x780: + case 0x788: + // STR rd, [rn, rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + this.BarrelShifterLslImmed(), alu); + break; + + case 0x782: + case 0x78A: + // STR rd, [rn, rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + this.BarrelShifterLsrImmed(), alu); + break; + + case 0x784: + case 0x78C: + // STR rd, [rn, rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + this.BarrelShifterAsrImmed(), alu); + break; + + case 0x786: + case 0x78E: + // STR rd, [rn, rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + this.BarrelShifterRorImmed(), alu); + break; + + case 0x7A0: + case 0x7A8: + // STR rd, [rn, rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterLslImmed(); + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x7A2: + case 0x7AA: + // STR rd, [rn, rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterLsrImmed(); + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x7A4: + case 0x7AC: + // STR rd, [rn, rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterAsrImmed(); + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x7A6: + case 0x7AE: + // STR rd, [rn, rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterRorImmed(); + this.memory.WriteU32(registers[rn], alu); + break; + + case 0x7C0: + case 0x7C8: + // STRB rd, [rn, rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + this.BarrelShifterLslImmed(), (byte)(alu & 0xFF)); + break; + + case 0x7C2: + case 0x7CA: + // STRB rd, [rn, rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + this.BarrelShifterLsrImmed(), (byte)(alu & 0xFF)); + break; + + case 0x7C4: + case 0x7CC: + // STRB rd, [rn, rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + this.BarrelShifterAsrImmed(), (byte)(alu & 0xFF)); + break; + + case 0x7C6: + case 0x7CE: + // STRB rd, [rn, rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + this.BarrelShifterRorImmed(), (byte)(alu & 0xFF)); + break; + + case 0x7E0: + case 0x7E8: + // STRB rd, [rn, rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterLslImmed(); + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + case 0x7E2: + case 0x7EA: + // STRB rd, [rn, rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterLsrImmed(); + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + case 0x7E4: + case 0x7EC: + // STRB rd, [rn, rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterAsrImmed(); + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + case 0x7E6: + case 0x7EE: + // STRB rd, [rn, rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterRorImmed(); + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // LDR register shift implementations + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0x610: + case 0x618: + // LDR rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x612: + case 0x61A: + // LDR rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x614: + case 0x61C: + // LDR rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x616: + case 0x61E: + // LDR rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x630: + case 0x638: + // LDRT rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x632: + case 0x63A: + // LDRT rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x634: + case 0x63C: + // LDRT rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x636: + case 0x63E: + // LDRT rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x650: + case 0x658: + // LDRB rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x652: + case 0x65A: + // LDRB rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x654: + case 0x65C: + // LDRB rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x656: + case 0x65E: + // LDRB rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x670: + case 0x678: + // LDRBT rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x672: + case 0x67A: + // LDRBT rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x674: + case 0x67C: + // LDRBT rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x676: + case 0x67E: + // LDRBT rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x690: + case 0x698: + // LDR rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x692: + case 0x69A: + // LDR rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x694: + case 0x69C: + // LDR rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x696: + case 0x69E: + // LDR rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6B0: + case 0x6B8: + // LDRT rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6B2: + case 0x6BA: + // LDRT rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6B4: + case 0x6BC: + // LDRT rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6B6: + case 0x6BE: + // LDRT rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6D0: + case 0x6D8: + // LDRB rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6D2: + case 0x6DA: + // LDRB rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6D4: + case 0x6DC: + // LDRB rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6D6: + case 0x6DE: + // LDRB rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6F0: + case 0x6F8: + // LDRBT rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6F2: + case 0x6FA: + // LDRBT rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6F4: + case 0x6FC: + // LDRBT rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x6F6: + case 0x6FE: + // LDRBT rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + break; + + case 0x710: + case 0x718: + // LDR rd, [rn, -rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x712: + case 0x71A: + // LDR rd, [rn, -rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x714: + case 0x71C: + // LDR rd, [rn, -rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x716: + case 0x71E: + // LDR rd, [rn, -rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x730: + case 0x738: + // LDR rd, [rn, -rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x732: + case 0x73A: + // LDR rd, [rn, -rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x734: + case 0x73C: + // LDR rd, [rn, -rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x736: + case 0x73E: + // LDR rd, [rn, -rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x750: + case 0x758: + // LDRB rd, [rn, -rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x752: + case 0x75A: + // LDRB rd, [rn, -rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x754: + case 0x75C: + // LDRB rd, [rn, -rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x756: + case 0x75E: + // LDRB rd, [rn, -rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x770: + case 0x778: + // LDRB rd, [rn, -rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x772: + case 0x77A: + // LDRB rd, [rn, -rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x774: + case 0x77C: + // LDRB rd, [rn, -rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x776: + case 0x77E: + // LDRB rd, [rn, -rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x790: + case 0x798: + // LDR rd, [rn, rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + this.BarrelShifterLslImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x792: + case 0x79A: + // LDR rd, [rn, rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + this.BarrelShifterLsrImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x794: + case 0x79C: + // LDR rd, [rn, rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + this.BarrelShifterAsrImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x796: + case 0x79E: + // LDR rd, [rn, rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + this.BarrelShifterRorImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7B0: + case 0x7B8: + // LDR rd, [rn, rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7B2: + case 0x7BA: + // LDR rd, [rn, rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7B4: + case 0x7BC: + // LDR rd, [rn, rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7B6: + case 0x7BE: + // LDR rd, [rn, rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7D0: + case 0x7D8: + // LDRB rd, [rn, rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + this.BarrelShifterLslImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7D2: + case 0x7DA: + // LDRB rd, [rn, rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + this.BarrelShifterLsrImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7D4: + case 0x7DC: + // LDRB rd, [rn, rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + this.BarrelShifterAsrImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7D6: + case 0x7DE: + // LDRB rd, [rn, rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + this.BarrelShifterRorImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7F0: + case 0x7F8: + // LDRB rd, [rn, rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7F2: + case 0x7FA: + // LDRB rd, [rn, rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7F4: + case 0x7FC: + // LDRB rd, [rn, rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + case 0x7F6: + case 0x7FE: + // LDRB rd, [rn, rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // LDM implementations (TODO) + // + //////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // STM implementations (TODO) + // + //////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // B implementation + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0xA00: case 0xA01: case 0xA02: case 0xA03: case 0xA04: case 0xA05: case 0xA06: case 0xA07: + case 0xA08: case 0xA09: case 0xA0A: case 0xA0B: case 0xA0C: case 0xA0D: case 0xA0E: case 0xA0F: + case 0xA10: case 0xA11: case 0xA12: case 0xA13: case 0xA14: case 0xA15: case 0xA16: case 0xA17: + case 0xA18: case 0xA19: case 0xA1A: case 0xA1B: case 0xA1C: case 0xA1D: case 0xA1E: case 0xA1F: + case 0xA20: case 0xA21: case 0xA22: case 0xA23: case 0xA24: case 0xA25: case 0xA26: case 0xA27: + case 0xA28: case 0xA29: case 0xA2A: case 0xA2B: case 0xA2C: case 0xA2D: case 0xA2E: case 0xA2F: + case 0xA30: case 0xA31: case 0xA32: case 0xA33: case 0xA34: case 0xA35: case 0xA36: case 0xA37: + case 0xA38: case 0xA39: case 0xA3A: case 0xA3B: case 0xA3C: case 0xA3D: case 0xA3E: case 0xA3F: + case 0xA40: case 0xA41: case 0xA42: case 0xA43: case 0xA44: case 0xA45: case 0xA46: case 0xA47: + case 0xA48: case 0xA49: case 0xA4A: case 0xA4B: case 0xA4C: case 0xA4D: case 0xA4E: case 0xA4F: + case 0xA50: case 0xA51: case 0xA52: case 0xA53: case 0xA54: case 0xA55: case 0xA56: case 0xA57: + case 0xA58: case 0xA59: case 0xA5A: case 0xA5B: case 0xA5C: case 0xA5D: case 0xA5E: case 0xA5F: + case 0xA60: case 0xA61: case 0xA62: case 0xA63: case 0xA64: case 0xA65: case 0xA66: case 0xA67: + case 0xA68: case 0xA69: case 0xA6A: case 0xA6B: case 0xA6C: case 0xA6D: case 0xA6E: case 0xA6F: + case 0xA70: case 0xA71: case 0xA72: case 0xA73: case 0xA74: case 0xA75: case 0xA76: case 0xA77: + case 0xA78: case 0xA79: case 0xA7A: case 0xA7B: case 0xA7C: case 0xA7D: case 0xA7E: case 0xA7F: + case 0xA80: case 0xA81: case 0xA82: case 0xA83: case 0xA84: case 0xA85: case 0xA86: case 0xA87: + case 0xA88: case 0xA89: case 0xA8A: case 0xA8B: case 0xA8C: case 0xA8D: case 0xA8E: case 0xA8F: + case 0xA90: case 0xA91: case 0xA92: case 0xA93: case 0xA94: case 0xA95: case 0xA96: case 0xA97: + case 0xA98: case 0xA99: case 0xA9A: case 0xA9B: case 0xA9C: case 0xA9D: case 0xA9E: case 0xA9F: + case 0xAA0: case 0xAA1: case 0xAA2: case 0xAA3: case 0xAA4: case 0xAA5: case 0xAA6: case 0xAA7: + case 0xAA8: case 0xAA9: case 0xAAA: case 0xAAB: case 0xAAC: case 0xAAD: case 0xAAE: case 0xAAF: + case 0xAB0: case 0xAB1: case 0xAB2: case 0xAB3: case 0xAB4: case 0xAB5: case 0xAB6: case 0xAB7: + case 0xAB8: case 0xAB9: case 0xABA: case 0xABB: case 0xABC: case 0xABD: case 0xABE: case 0xABF: + case 0xAC0: case 0xAC1: case 0xAC2: case 0xAC3: case 0xAC4: case 0xAC5: case 0xAC6: case 0xAC7: + case 0xAC8: case 0xAC9: case 0xACA: case 0xACB: case 0xACC: case 0xACD: case 0xACE: case 0xACF: + case 0xAD0: case 0xAD1: case 0xAD2: case 0xAD3: case 0xAD4: case 0xAD5: case 0xAD6: case 0xAD7: + case 0xAD8: case 0xAD9: case 0xADA: case 0xADB: case 0xADC: case 0xADD: case 0xADE: case 0xADF: + case 0xAE0: case 0xAE1: case 0xAE2: case 0xAE3: case 0xAE4: case 0xAE5: case 0xAE6: case 0xAE7: + case 0xAE8: case 0xAE9: case 0xAEA: case 0xAEB: case 0xAEC: case 0xAED: case 0xAEE: case 0xAEF: + case 0xAF0: case 0xAF1: case 0xAF2: case 0xAF3: case 0xAF4: case 0xAF5: case 0xAF6: case 0xAF7: + case 0xAF8: case 0xAF9: case 0xAFA: case 0xAFB: case 0xAFC: case 0xAFD: case 0xAFE: case 0xAFF: + { + uint branchOffset = this.curInstruction & 0x00FFFFFF; + if (branchOffset >> 23 == 1) branchOffset |= 0xFF000000; + + this.registers[15] += branchOffset << 2; + + this.FlushQueue(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // BL implementation + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0xB00: case 0xB01: case 0xB02: case 0xB03: case 0xB04: case 0xB05: case 0xB06: case 0xB07: + case 0xB08: case 0xB09: case 0xB0A: case 0xB0B: case 0xB0C: case 0xB0D: case 0xB0E: case 0xB0F: + case 0xB10: case 0xB11: case 0xB12: case 0xB13: case 0xB14: case 0xB15: case 0xB16: case 0xB17: + case 0xB18: case 0xB19: case 0xB1A: case 0xB1B: case 0xB1C: case 0xB1D: case 0xB1E: case 0xB1F: + case 0xB20: case 0xB21: case 0xB22: case 0xB23: case 0xB24: case 0xB25: case 0xB26: case 0xB27: + case 0xB28: case 0xB29: case 0xB2A: case 0xB2B: case 0xB2C: case 0xB2D: case 0xB2E: case 0xB2F: + case 0xB30: case 0xB31: case 0xB32: case 0xB33: case 0xB34: case 0xB35: case 0xB36: case 0xB37: + case 0xB38: case 0xB39: case 0xB3A: case 0xB3B: case 0xB3C: case 0xB3D: case 0xB3E: case 0xB3F: + case 0xB40: case 0xB41: case 0xB42: case 0xB43: case 0xB44: case 0xB45: case 0xB46: case 0xB47: + case 0xB48: case 0xB49: case 0xB4A: case 0xB4B: case 0xB4C: case 0xB4D: case 0xB4E: case 0xB4F: + case 0xB50: case 0xB51: case 0xB52: case 0xB53: case 0xB54: case 0xB55: case 0xB56: case 0xB57: + case 0xB58: case 0xB59: case 0xB5A: case 0xB5B: case 0xB5C: case 0xB5D: case 0xB5E: case 0xB5F: + case 0xB60: case 0xB61: case 0xB62: case 0xB63: case 0xB64: case 0xB65: case 0xB66: case 0xB67: + case 0xB68: case 0xB69: case 0xB6A: case 0xB6B: case 0xB6C: case 0xB6D: case 0xB6E: case 0xB6F: + case 0xB70: case 0xB71: case 0xB72: case 0xB73: case 0xB74: case 0xB75: case 0xB76: case 0xB77: + case 0xB78: case 0xB79: case 0xB7A: case 0xB7B: case 0xB7C: case 0xB7D: case 0xB7E: case 0xB7F: + case 0xB80: case 0xB81: case 0xB82: case 0xB83: case 0xB84: case 0xB85: case 0xB86: case 0xB87: + case 0xB88: case 0xB89: case 0xB8A: case 0xB8B: case 0xB8C: case 0xB8D: case 0xB8E: case 0xB8F: + case 0xB90: case 0xB91: case 0xB92: case 0xB93: case 0xB94: case 0xB95: case 0xB96: case 0xB97: + case 0xB98: case 0xB99: case 0xB9A: case 0xB9B: case 0xB9C: case 0xB9D: case 0xB9E: case 0xB9F: + case 0xBA0: case 0xBA1: case 0xBA2: case 0xBA3: case 0xBA4: case 0xBA5: case 0xBA6: case 0xBA7: + case 0xBA8: case 0xBA9: case 0xBAA: case 0xBAB: case 0xBAC: case 0xBAD: case 0xBAE: case 0xBAF: + case 0xBB0: case 0xBB1: case 0xBB2: case 0xBB3: case 0xBB4: case 0xBB5: case 0xBB6: case 0xBB7: + case 0xBB8: case 0xBB9: case 0xBBA: case 0xBBB: case 0xBBC: case 0xBBD: case 0xBBE: case 0xBBF: + case 0xBC0: case 0xBC1: case 0xBC2: case 0xBC3: case 0xBC4: case 0xBC5: case 0xBC6: case 0xBC7: + case 0xBC8: case 0xBC9: case 0xBCA: case 0xBCB: case 0xBCC: case 0xBCD: case 0xBCE: case 0xBCF: + case 0xBD0: case 0xBD1: case 0xBD2: case 0xBD3: case 0xBD4: case 0xBD5: case 0xBD6: case 0xBD7: + case 0xBD8: case 0xBD9: case 0xBDA: case 0xBDB: case 0xBDC: case 0xBDD: case 0xBDE: case 0xBDF: + case 0xBE0: case 0xBE1: case 0xBE2: case 0xBE3: case 0xBE4: case 0xBE5: case 0xBE6: case 0xBE7: + case 0xBE8: case 0xBE9: case 0xBEA: case 0xBEB: case 0xBEC: case 0xBED: case 0xBEE: case 0xBEF: + case 0xBF0: case 0xBF1: case 0xBF2: case 0xBF3: case 0xBF4: case 0xBF5: case 0xBF6: case 0xBF7: + case 0xBF8: case 0xBF9: case 0xBFA: case 0xBFB: case 0xBFC: case 0xBFD: case 0xBFE: case 0xBFF: + { + uint branchOffset = this.curInstruction & 0x00FFFFFF; + if (branchOffset >> 23 == 1) branchOffset |= 0xFF000000; + + this.registers[14] = this.registers[15] - 4U; + this.registers[15] += branchOffset << 2; + + this.FlushQueue(); + } + break; + + //////////////////////////////////////////////////////////////////////////////////////////// + // + // SWI implementation + // + //////////////////////////////////////////////////////////////////////////////////////////// + case 0xF00: case 0xF01: case 0xF02: case 0xF03: case 0xF04: case 0xF05: case 0xF06: case 0xF07: + case 0xF08: case 0xF09: case 0xF0A: case 0xF0B: case 0xF0C: case 0xF0D: case 0xF0E: case 0xF0F: + case 0xF10: case 0xF11: case 0xF12: case 0xF13: case 0xF14: case 0xF15: case 0xF16: case 0xF17: + case 0xF18: case 0xF19: case 0xF1A: case 0xF1B: case 0xF1C: case 0xF1D: case 0xF1E: case 0xF1F: + case 0xF20: case 0xF21: case 0xF22: case 0xF23: case 0xF24: case 0xF25: case 0xF26: case 0xF27: + case 0xF28: case 0xF29: case 0xF2A: case 0xF2B: case 0xF2C: case 0xF2D: case 0xF2E: case 0xF2F: + case 0xF30: case 0xF31: case 0xF32: case 0xF33: case 0xF34: case 0xF35: case 0xF36: case 0xF37: + case 0xF38: case 0xF39: case 0xF3A: case 0xF3B: case 0xF3C: case 0xF3D: case 0xF3E: case 0xF3F: + case 0xF40: case 0xF41: case 0xF42: case 0xF43: case 0xF44: case 0xF45: case 0xF46: case 0xF47: + case 0xF48: case 0xF49: case 0xF4A: case 0xF4B: case 0xF4C: case 0xF4D: case 0xF4E: case 0xF4F: + case 0xF50: case 0xF51: case 0xF52: case 0xF53: case 0xF54: case 0xF55: case 0xF56: case 0xF57: + case 0xF58: case 0xF59: case 0xF5A: case 0xF5B: case 0xF5C: case 0xF5D: case 0xF5E: case 0xF5F: + case 0xF60: case 0xF61: case 0xF62: case 0xF63: case 0xF64: case 0xF65: case 0xF66: case 0xF67: + case 0xF68: case 0xF69: case 0xF6A: case 0xF6B: case 0xF6C: case 0xF6D: case 0xF6E: case 0xF6F: + case 0xF70: case 0xF71: case 0xF72: case 0xF73: case 0xF74: case 0xF75: case 0xF76: case 0xF77: + case 0xF78: case 0xF79: case 0xF7A: case 0xF7B: case 0xF7C: case 0xF7D: case 0xF7E: case 0xF7F: + case 0xF80: case 0xF81: case 0xF82: case 0xF83: case 0xF84: case 0xF85: case 0xF86: case 0xF87: + case 0xF88: case 0xF89: case 0xF8A: case 0xF8B: case 0xF8C: case 0xF8D: case 0xF8E: case 0xF8F: + case 0xF90: case 0xF91: case 0xF92: case 0xF93: case 0xF94: case 0xF95: case 0xF96: case 0xF97: + case 0xF98: case 0xF99: case 0xF9A: case 0xF9B: case 0xF9C: case 0xF9D: case 0xF9E: case 0xF9F: + case 0xFA0: case 0xFA1: case 0xFA2: case 0xFA3: case 0xFA4: case 0xFA5: case 0xFA6: case 0xFA7: + case 0xFA8: case 0xFA9: case 0xFAA: case 0xFAB: case 0xFAC: case 0xFAD: case 0xFAE: case 0xFAF: + case 0xFB0: case 0xFB1: case 0xFB2: case 0xFB3: case 0xFB4: case 0xFB5: case 0xFB6: case 0xFB7: + case 0xFB8: case 0xFB9: case 0xFBA: case 0xFBB: case 0xFBC: case 0xFBD: case 0xFBE: case 0xFBF: + case 0xFC0: case 0xFC1: case 0xFC2: case 0xFC3: case 0xFC4: case 0xFC5: case 0xFC6: case 0xFC7: + case 0xFC8: case 0xFC9: case 0xFCA: case 0xFCB: case 0xFCC: case 0xFCD: case 0xFCE: case 0xFCF: + case 0xFD0: case 0xFD1: case 0xFD2: case 0xFD3: case 0xFD4: case 0xFD5: case 0xFD6: case 0xFD7: + case 0xFD8: case 0xFD9: case 0xFDA: case 0xFDB: case 0xFDC: case 0xFDD: case 0xFDE: case 0xFDF: + case 0xFE0: case 0xFE1: case 0xFE2: case 0xFE3: case 0xFE4: case 0xFE5: case 0xFE6: case 0xFE7: + case 0xFE8: case 0xFE9: case 0xFEA: case 0xFEB: case 0xFEC: case 0xFED: case 0xFEE: case 0xFEF: + case 0xFF0: case 0xFF1: case 0xFF2: case 0xFF3: case 0xFF4: case 0xFF5: case 0xFF6: case 0xFF7: + case 0xFF8: case 0xFF9: case 0xFFA: case 0xFFB: case 0xFFC: case 0xFFD: case 0xFFE: case 0xFFF: + this.registers[15] -= 4U; + this.parent.EnterException(Arm7Processor.SVC, 0x8, false, false); + break; + + default: + this.NormalOps[(curInstruction >> 25) & 0x7](); + break; + } + } + + #region Barrel Shifter + private const uint SHIFT_LSL = 0; + private const uint SHIFT_LSR = 1; + private const uint SHIFT_ASR = 2; + private const uint SHIFT_ROR = 3; + + private uint BarrelShifter(uint shifterOperand) + { + uint type = (shifterOperand >> 5) & 0x3; + + bool registerShift = (shifterOperand & (1 << 4)) == (1 << 4); + + uint rm = registers[shifterOperand & 0xF]; + + int amount; + if (registerShift) + { + uint rs = (shifterOperand >> 8) & 0xF; + if (rs == 15) + { + amount = (int)((registers[rs] + 0x4) & 0xFF); + } + else + { + amount = (int)(registers[rs] & 0xFF); + } + + if ((shifterOperand & 0xF) == 15) + { + rm += 4; + } + } + else + { + amount = (int)((shifterOperand >> 7) & 0x1F); + } + + if (registerShift) + { + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + + switch (type) + { + case SHIFT_LSL: + if (amount < 32) + { + this.shifterCarry = (rm >> (32 - amount)) & 1; + return rm << amount; + } + else if (amount == 32) + { + this.shifterCarry = rm & 1; + return 0; + } + else + { + this.shifterCarry = 0; + return 0; + } + + case SHIFT_LSR: + if (amount < 32) + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return rm >> amount; + } + else if (amount == 32) + { + this.shifterCarry = (rm >> 31) & 1; + return 0; + } + else + { + this.shifterCarry = 0; + return 0; + } + + case SHIFT_ASR: + if (amount >= 32) + { + if ((rm & (1 << 31)) == 0) + { + this.shifterCarry = 0; + return 0; + } + else + { + this.shifterCarry = 1; + return 0xFFFFFFFF; + } + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (uint)(((int)rm) >> amount); + } + + case SHIFT_ROR: + if ((amount & 0x1F) == 0) + { + this.shifterCarry = (rm >> 31) & 1; + return rm; + } + else + { + amount &= 0x1F; + this.shifterCarry = (rm >> amount) & 1; + return (rm >> amount) | (rm << (32 - amount)); + } + } + } + else + { + switch (type) + { + case SHIFT_LSL: + if (amount == 0) + { + this.shifterCarry = this.carry; + return rm; + } + else + { + this.shifterCarry = (rm >> (32 - amount)) & 1; + return rm << amount; + } + + case SHIFT_LSR: + if (amount == 0) + { + this.shifterCarry = (rm >> 31) & 1; + return 0; + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return rm >> amount; + } + + case SHIFT_ASR: + if (amount == 0) + { + if ((rm & (1 << 31)) == 0) + { + this.shifterCarry = 0; + return 0; + } + else + { + this.shifterCarry = 1; + return 0xFFFFFFFF; + } + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (uint)(((int)rm) >> amount); + } + + case SHIFT_ROR: + if (amount == 0) + { + // Actually an RRX + this.shifterCarry = rm & 1; + return (this.carry << 31) | (rm >> 1); + } + else + { + this.shifterCarry = (rm >> (amount - 1)) & 1; + return (rm >> amount) | (rm << (32 - amount)); + } + } + } + + // Should never happen... + throw new Exception("Barrel Shifter has messed up."); + } + #endregion + + #region Flag helpers + public void OverflowCarryAdd(uint a, uint b, uint r) + { + overflow = ((a & b & ~r) | (~a & ~b & r)) >> 31; + carry = ((a & b) | (a & ~r) | (b & ~r)) >> 31; + } + + public void OverflowCarrySub(uint a, uint b, uint r) + { + overflow = ((a & ~b & ~r) | (~a & b & r)) >> 31; + carry = ((a & ~b) | (a & ~r) | (~b & ~r)) >> 31; + } + #endregion + #region Opcodes + private void DoDataProcessing(uint shifterOperand) + { + uint rn = (this.curInstruction >> 16) & 0xF; + uint rd = (this.curInstruction >> 12) & 0xF; + uint alu; + + bool registerShift = (this.curInstruction & (1 << 4)) == (1 << 4); + if (rn == 15 && ((this.curInstruction >> 25) & 0x7) == 0 && registerShift) + { + rn = registers[rn] + 4; + } + else + { + rn = registers[rn]; + } + + uint opcode = (this.curInstruction >> 21) & 0xF; + + if (((this.curInstruction >> 20) & 1) == 1) + { + // Set flag bit set + switch (opcode) + { + case OP_ADC: + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + break; + + case OP_ADD: + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + break; + + case OP_AND: + registers[rd] = rn & shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_BIC: + registers[rd] = rn & ~shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_CMN: + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + break; + + case OP_CMP: + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + break; + + case OP_EOR: + registers[rd] = rn ^ shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_MOV: + registers[rd] = shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_MVN: + registers[rd] = ~shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_ORR: + registers[rd] = rn | shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_RSB: + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + break; + + case OP_RSC: + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + break; + + case OP_SBC: + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + break; + + case OP_SUB: + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + break; + + case OP_TEQ: + alu = rn ^ shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + + case OP_TST: + alu = rn & shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + break; + } + + if (rd == 15) + { + // Prevent writing if no SPSR exists (this will be true for USER or SYSTEM mode) + if (this.parent.SPSRExists) this.parent.WriteCpsr(this.parent.SPSR); + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + // Otherwise, flush the instruction queue + this.FlushQueue(); + } + } + else + { + // Set flag bit not set + switch (opcode) + { + case OP_ADC: registers[rd] = rn + shifterOperand + carry; break; + case OP_ADD: registers[rd] = rn + shifterOperand; break; + case OP_AND: registers[rd] = rn & shifterOperand; break; + case OP_BIC: registers[rd] = rn & ~shifterOperand; break; + case OP_EOR: registers[rd] = rn ^ shifterOperand; break; + case OP_MOV: registers[rd] = shifterOperand; break; + case OP_MVN: registers[rd] = ~shifterOperand; break; + case OP_ORR: registers[rd] = rn | shifterOperand; break; + case OP_RSB: registers[rd] = shifterOperand - rn; break; + case OP_RSC: registers[rd] = shifterOperand - rn - (1U - carry); break; + case OP_SBC: registers[rd] = rn - shifterOperand - (1U - carry); break; + case OP_SUB: registers[rd] = rn - shifterOperand; break; + + case OP_CMN: + // MSR SPSR, shifterOperand + if ((this.curInstruction & (1 << 16)) == 1 << 16 && this.parent.SPSRExists) + { + this.parent.SPSR &= 0xFFFFFF00; + this.parent.SPSR |= shifterOperand & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17 && this.parent.SPSRExists) + { + this.parent.SPSR &= 0xFFFF00FF; + this.parent.SPSR |= shifterOperand & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18 && this.parent.SPSRExists) + { + this.parent.SPSR &= 0xFF00FFFF; + this.parent.SPSR |= shifterOperand & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19 && this.parent.SPSRExists) + { + this.parent.SPSR &= 0x00FFFFFF; + this.parent.SPSR |= shifterOperand & 0xFF000000; + } + + // Queue will be flushed since rd == 15, so adjust the PC + registers[15] -= 4; + break; + + case OP_CMP: + // MRS rd, SPSR + if (this.parent.SPSRExists) registers[rd] = this.parent.SPSR; + break; + + case OP_TEQ: + if (((this.curInstruction >> 4) & 0xf) == 1) + { + // BX + uint rm = this.curInstruction & 0xf; + + this.PackFlags(); + + this.parent.CPSR &= ~Arm7Processor.T_MASK; + this.parent.CPSR |= (registers[rm] & 1) << Arm7Processor.T_BIT; + + registers[15] = registers[rm] & (~1U); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + // Queue will be flushed later because rd == 15 + } + else if (((this.curInstruction >> 4) & 0xf) == 0) + { + // MSR CPSR, shifterOperand + bool userMode = (this.parent.CPSR & 0x1F) == Arm7Processor.USR; + + this.PackFlags(); + + uint tmpCPSR = this.parent.CPSR; + + if ((this.curInstruction & (1 << 16)) == 1 << 16 && !userMode) + { + tmpCPSR &= 0xFFFFFF00; + tmpCPSR |= shifterOperand & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17 && !userMode) + { + tmpCPSR &= 0xFFFF00FF; + tmpCPSR |= shifterOperand & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18 && !userMode) + { + tmpCPSR &= 0xFF00FFFF; + tmpCPSR |= shifterOperand & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + tmpCPSR &= 0x00FFFFFF; + tmpCPSR |= shifterOperand & 0xFF000000; + } + + this.parent.WriteCpsr(tmpCPSR); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + // Queue will be flushed since rd == 15, so adjust the PC + registers[15] -= 4; + } + break; + + case OP_TST: + // MRS rd, CPSR + this.PackFlags(); + registers[rd] = this.parent.CPSR; + break; + } + + if (rd == 15) + { + // Flush the queue + this.FlushQueue(); + } + } + } + + private void DataProcessing() + { + // Special instruction + switch ((this.curInstruction >> 4) & 0xF) + { + case 0x9: + // Multiply or swap instructions + this.MultiplyOrSwap(); + return; + case 0xB: + // Load/Store Unsigned halfword + this.LoadStoreHalfword(); + return; + case 0xD: + // Load/Store Signed byte + this.LoadStoreHalfword(); + return; + case 0xF: + // Load/Store Signed halfword + this.LoadStoreHalfword(); + return; + } + + this.DoDataProcessing(this.BarrelShifter(this.curInstruction)); + } + + private void DataProcessingImmed() + { + uint immed = this.curInstruction & 0xFF; + int rotateAmount = (int)(((this.curInstruction >> 8) & 0xF) * 2); + + immed = (immed >> rotateAmount) | (immed << (32 - rotateAmount)); + + if (rotateAmount == 0) + { + this.shifterCarry = this.carry; + } + else + { + this.shifterCarry = (immed >> 31) & 1; + } + + this.DoDataProcessing(immed); + } + + private void LoadStore(uint offset) + { + uint rn = (this.curInstruction >> 16) & 0xF; + uint rd = (this.curInstruction >> 12) & 0xF; + + uint address = registers[rn]; + + bool preIndexed = (this.curInstruction & (1 << 24)) == 1 << 24; + bool byteTransfer = (this.curInstruction & (1 << 22)) == 1 << 22; + bool writeback = (this.curInstruction & (1 << 21)) == 1 << 21; + + // Add or subtract offset + if ((this.curInstruction & (1 << 23)) != 1 << 23) offset = (uint)-offset; + + if (preIndexed) + { + address += offset; + + if (writeback) + { + registers[rn] = address; + } + } + + if ((this.curInstruction & (1 << 20)) == 1 << 20) + { + // Load + if (byteTransfer) + { + registers[rd] = this.memory.ReadU8(address); + } + else + { + registers[rd] = this.memory.ReadU32(address); + } + + // ARM9 fix here + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (!preIndexed) + { + if (rn != rd) + registers[rn] = address + offset; + } + } + else + { + // Store + uint amount = registers[rd]; + if (rd == 15) amount += 4; + + if (byteTransfer) + { + this.memory.WriteU8(address, (byte)(amount & 0xFF)); + } + else + { + this.memory.WriteU32(address, amount); + } + + if (!preIndexed) + { + registers[rn] = address + offset; + } + } + } + + private void LoadStoreImmediate() + { + this.LoadStore(this.curInstruction & 0xFFF); + } + + private void LoadStoreRegister() + { + // The barrel shifter expects a 0 in bit 4 for immediate shifts, this is implicit in + // the meaning of the instruction, so it is fine + this.LoadStore(this.BarrelShifter(this.curInstruction)); + } + + private void LoadStoreMultiple() + { + uint rn = (this.curInstruction >> 16) & 0xF; + + this.PackFlags(); + uint curCpsr = this.parent.CPSR; + + bool preIncrement = (this.curInstruction & (1 << 24)) != 0; + bool up = (this.curInstruction & (1 << 23)) != 0; + bool writeback = (this.curInstruction & (1 << 21)) != 0; + + uint address; + uint bitsSet = 0; + for (int i = 0; i < 16; i++) if (((this.curInstruction >> i) & 1) != 0) bitsSet++; + + if (preIncrement) + { + if (up) + { + // Increment before + address = this.registers[rn] + 4; + if (writeback) this.registers[rn] += bitsSet * 4; + } + else + { + // Decrement before + address = this.registers[rn] - (bitsSet * 4); + if (writeback) this.registers[rn] -= bitsSet * 4; + } + } + else + { + if (up) + { + // Increment after + address = this.registers[rn]; + if (writeback) this.registers[rn] += bitsSet * 4; + } + else + { + // Decrement after + address = this.registers[rn] - (bitsSet * 4) + 4; + if (writeback) this.registers[rn] -= bitsSet * 4; + } + } + + if ((this.curInstruction & (1 << 20)) != 0) + { + if ((this.curInstruction & (1 << 22)) != 0 && ((this.curInstruction >> 15) & 1) == 0) + { + // Switch to user mode temporarily + this.parent.WriteCpsr((curCpsr & ~0x1FU) | Arm7Processor.USR); + } + + // Load multiple + for (int i = 0; i < 15; i++) + { + if (((this.curInstruction >> i) & 1) != 1) continue; + this.registers[i] = this.memory.ReadU32Aligned(address & (~0x3U)); + address += 4; + } + + if (((this.curInstruction >> 15) & 1) == 1) + { + // Arm9 fix here + + this.registers[15] = this.memory.ReadU32Aligned(address & (~0x3U)); + + if ((this.curInstruction & (1 << 22)) != 0) + { + // Load the CPSR from the SPSR + if (this.parent.SPSRExists) + { + this.parent.WriteCpsr(this.parent.SPSR); + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + this.registers[15] &= ~0x1U; + return; + } + } + } + + this.registers[15] &= ~0x3U; + this.FlushQueue(); + } + else + { + if ((this.curInstruction & (1 << 22)) != 0) + { + // Switch back to the correct mode + this.parent.WriteCpsr(curCpsr); + this.UnpackFlags(); + + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + } + } + } + else + { + if ((this.curInstruction & (1 << 22)) != 0) + { + // Switch to user mode temporarily + this.parent.WriteCpsr((curCpsr & ~0x1FU) | Arm7Processor.USR); + } + +/* if (((this.curInstruction >> (int)rn) & 1) != 0 && writeback && + (this.curInstruction & ~(0xFFFFFFFF << (int)rn)) == 0) + { + // If the lowest register is also the writeback, we use the original value + // Does anybody do this???? + throw new Exception("Unhandled STM state"); + } + else*/ + { + // Store multiple + for (int i = 0; i < 15; i++) + { + if (((this.curInstruction >> i) & 1) == 0) continue; + this.memory.WriteU32(address, this.registers[i]); + address += 4; + } + + if (((this.curInstruction >> 15) & 1) != 0) + { + this.memory.WriteU32(address, this.registers[15] + 4U); + } + } + + if ((this.curInstruction & (1 << 22)) != 0) + { + // Switch back to the correct mode + this.parent.WriteCpsr(curCpsr); + this.UnpackFlags(); + } + } + } + + private void Branch() + { + if ((this.curInstruction & (1 << 24)) != 0) + { + this.registers[14] = (this.registers[15] - 4U) & ~3U; + } + + uint branchOffset = this.curInstruction & 0x00FFFFFF; + if (branchOffset >> 23 == 1) branchOffset |= 0xFF000000; + + this.registers[15] += branchOffset << 2; + + this.FlushQueue(); + } + + private void CoprocessorLoadStore() + { + throw new Exception("Unhandled opcode - coproc load/store"); + } + + private void SoftwareInterrupt() + { + // Adjust PC for prefetch + this.registers[15] -= 4U; + this.parent.EnterException(Arm7Processor.SVC, 0x8, false, false); + } + + private void MultiplyOrSwap() + { + if ((this.curInstruction & (1 << 24)) == 1 << 24) + { + // Swap instruction + uint rn = (this.curInstruction >> 16) & 0xF; + uint rd = (this.curInstruction >> 12) & 0xF; + uint rm = this.curInstruction & 0xF; + + if ((this.curInstruction & (1 << 22)) != 0) + { + // SWPB + byte tmp = this.memory.ReadU8(registers[rn]); + this.memory.WriteU8(registers[rn], (byte)(registers[rm] & 0xFF)); + registers[rd] = tmp; + } + else + { + // SWP + uint tmp = this.memory.ReadU32(registers[rn]); + this.memory.WriteU32(registers[rn], registers[rm]); + registers[rd] = tmp; + } + } + else + { + // Multiply instruction + switch ((this.curInstruction >> 21) & 0x7) + { + case 0: + case 1: + { + // Multiply/Multiply + Accumulate + uint rd = (this.curInstruction >> 16) & 0xF; + uint rn = registers[(this.curInstruction >> 12) & 0xF]; + uint rs = (this.curInstruction >> 8) & 0xF; + uint rm = this.curInstruction & 0xF; + + int cycles = 4; + // Multiply cycle calculations + if ((registers[rs] & 0xFFFFFF00) == 0 || (registers[rs] & 0xFFFFFF00) == 0xFFFFFF00) + { + cycles = 1; + } + else if ((registers[rs] & 0xFFFF0000) == 0 || (registers[rs] & 0xFFFF0000) == 0xFFFF0000) + { + cycles = 2; + } + else if ((registers[rs] & 0xFF000000) == 0 || (registers[rs] & 0xFF000000) == 0xFF000000) + { + cycles = 3; + } + + registers[rd] = registers[rs] * registers[rm]; + this.parent.Cycles -= cycles; + + if ((this.curInstruction & (1 << 21)) == 1 << 21) + { + registers[rd] += rn; + this.parent.Cycles -= 1; + } + + if ((this.curInstruction & (1 << 20)) == 1 << 20) + { + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + break; + } + + case 2: + case 3: + throw new Exception("Invalid multiply"); + + case 4: + case 5: + case 6: + case 7: + { + // Multiply/Signed Multiply Long + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + uint rs = (this.curInstruction >> 8) & 0xF; + uint rm = this.curInstruction & 0xF; + + int cycles = 5; + // Multiply cycle calculations + if ((registers[rs] & 0xFFFFFF00) == 0 || (registers[rs] & 0xFFFFFF00) == 0xFFFFFF00) + { + cycles = 2; + } + else if ((registers[rs] & 0xFFFF0000) == 0 || (registers[rs] & 0xFFFF0000) == 0xFFFF0000) + { + cycles = 3; + } + else if ((registers[rs] & 0xFF000000) == 0 || (registers[rs] & 0xFF000000) == 0xFF000000) + { + cycles = 4; + } + + this.parent.Cycles -= cycles; + + switch ((this.curInstruction >> 21) & 0x3) + { + case 0: + { + // UMULL + ulong result = ((ulong)registers[rm]) * registers[rs]; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + break; + } + case 1: + { + // UMLAL + ulong accum = (((ulong)registers[rdhi]) << 32) | registers[rdlo]; + ulong result = ((ulong)registers[rm]) * registers[rs]; + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + break; + } + case 2: + { + // SMULL + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + break; + } + case 3: + { + // SMLAL + long accum = (((long)((int)registers[rdhi])) << 32) | registers[rdlo]; + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + break; + } + } + + if ((this.curInstruction & (1 << 20)) == 1 << 20) + { + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + } + break; + } + } + } + } + + private void LoadStoreHalfword() + { + uint rn = (this.curInstruction >> 16) & 0xF; + uint rd = (this.curInstruction >> 12) & 0xF; + + uint address = registers[rn]; + + bool preIndexed = (this.curInstruction & (1 << 24)) != 0; + bool byteTransfer = (this.curInstruction & (1 << 5)) == 0; + bool signedTransfer = (this.curInstruction & (1 << 6)) != 0; + bool writeback = (this.curInstruction & (1 << 21)) != 0; + + uint offset; + if ((this.curInstruction & (1 << 22)) != 0) + { + // Immediate offset + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + } + else + { + // Register offset + offset = this.registers[this.curInstruction & 0xF]; + } + + // Add or subtract offset + if ((this.curInstruction & (1 << 23)) == 0) offset = (uint)-offset; + + if (preIndexed) + { + address += offset; + + if (writeback) + { + registers[rn] = address; + } + } + + if ((this.curInstruction & (1 << 20)) != 0) + { + // Load + if (byteTransfer) + { + if (signedTransfer) + { + registers[rd] = this.memory.ReadU8(address); + if ((registers[rd] & 0x80) != 0) + { + registers[rd] |= 0xFFFFFF00; + } + } + else + { + registers[rd] = this.memory.ReadU8(address); + } + } + else + { + if (signedTransfer) + { + registers[rd] = this.memory.ReadU16(address); + if ((registers[rd] & 0x8000) != 0) + { + registers[rd] |= 0xFFFF0000; + } + } + else + { + registers[rd] = this.memory.ReadU16(address); + } + } + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (!preIndexed) + { + if (rn != rd) + registers[rn] = address + offset; + } + } + else + { + // Store + if (byteTransfer) + { + this.memory.WriteU8(address, (byte)(registers[rd] & 0xFF)); + } + else + { + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + } + + if (!preIndexed) + { + registers[rn] = address + offset; + } + } + } + #endregion Opcodes + + private void PackFlags() + { + this.parent.CPSR &= 0x0FFFFFFF; + this.parent.CPSR |= this.negative << Arm7Processor.N_BIT; + this.parent.CPSR |= this.zero << Arm7Processor.Z_BIT; + this.parent.CPSR |= this.carry << Arm7Processor.C_BIT; + this.parent.CPSR |= this.overflow << Arm7Processor.V_BIT; + } + + private void UnpackFlags() + { + this.negative = (this.parent.CPSR >> Arm7Processor.N_BIT) & 1; + this.zero = (this.parent.CPSR >> Arm7Processor.Z_BIT) & 1; + this.carry = (this.parent.CPSR >> Arm7Processor.C_BIT) & 1; + this.overflow = (this.parent.CPSR >> Arm7Processor.V_BIT) & 1; + } + + private void FlushQueue() + { + this.instructionQueue = this.memory.ReadU32(registers[15]); + registers[15] += 4; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/FastDispatchCore.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/FastDispatchCore.cs new file mode 100644 index 0000000000..cb2c5e6a0f --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/FastDispatchCore.cs @@ -0,0 +1,8469 @@ +namespace GarboDev +{ + partial class FastArmCore + { + #region Delegate Dispatcher + private void DispatchFunc0() + { + uint rn, rd; + // AND rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc1() + { + uint rn, rd; + // AND rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc2() + { + uint rn, rd; + // AND rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc3() + { + uint rn, rd; + // AND rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc4() + { + uint rn, rd; + // AND rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc5() + { + uint rn, rd; + // AND rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc6() + { + uint rn, rd; + // AND rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc7() + { + uint rn, rd; + // AND rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc8() + { + uint rn, rd; + // ANDS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc9() + { + uint rn, rd; + // ANDS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc10() + { + uint rn, rd; + // ANDS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc11() + { + uint rn, rd; + // ANDS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc12() + { + uint rn, rd; + // ANDS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc13() + { + uint rn, rd; + // ANDS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc14() + { + uint rn, rd; + // ANDS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc15() + { + uint rn, rd; + // ANDS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc16() + { + UndefinedInstruction(); + } + private void DispatchFunc17() + { + uint rd, rs, rm; + int cycles; + // MUL rd, rm, rs + rd = (this.curInstruction >> 16) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs); + + registers[rd] = registers[rs] * registers[rm]; + this.parent.Cycles -= cycles; + } + private void DispatchFunc18() + { + uint rd, rs, rm; + int cycles; + // MULS rd, rm, rs + rd = (this.curInstruction >> 16) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs); + + registers[rd] = registers[rs] * registers[rm]; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + private void DispatchFunc19() + { + uint rn, rd, rs, rm; + int cycles; + // MLA rd, rm, rs, rn + rd = (this.curInstruction >> 16) & 0xF; + rn = registers[(this.curInstruction >> 12) & 0xF]; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs); + + registers[rd] = registers[rs] * registers[rm] + rn; + this.parent.Cycles -= cycles + 1; + } + private void DispatchFunc20() + { + uint rn, rd, rs, rm; + int cycles; + // MLAS rd, rm, rs + rd = (this.curInstruction >> 16) & 0xF; + rn = registers[(this.curInstruction >> 12) & 0xF]; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs); + + registers[rd] = registers[rs] * registers[rm] + rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + + this.parent.Cycles -= cycles + 1; + } + private void DispatchFunc21() + { + uint rs, rm; + int cycles; + // UMULL rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 1; + + ulong result = ((ulong)registers[rm]) * registers[rs]; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + this.parent.Cycles -= cycles; + } + } + private void DispatchFunc22() + { + uint rs, rm; + int cycles; + // UMULLS rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 1; + + ulong result = ((ulong)registers[rm]) * registers[rs]; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + } + private void DispatchFunc23() + { + uint rs, rm; + int cycles; + // UMLAL rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 2; + + ulong accum = (((ulong)registers[rdhi]) << 32) | registers[rdlo]; + ulong result = ((ulong)registers[rm]) * registers[rs]; + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + this.parent.Cycles -= cycles; + } + } + private void DispatchFunc24() + { + uint rs, rm; + int cycles; + // UMLALS rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 2; + + ulong accum = (((ulong)registers[rdhi]) << 32) | registers[rdlo]; + ulong result = ((ulong)registers[rm]) * registers[rs]; + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + } + private void DispatchFunc25() + { + uint rs, rm; + int cycles; + // SMULL rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 1; + + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + this.parent.Cycles -= cycles; + } + } + private void DispatchFunc26() + { + uint rs, rm; + int cycles; + // SMULLS rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 1; + + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + } + private void DispatchFunc27() + { + uint rs, rm; + int cycles; + // SMLAL rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 2; + + long accum = (((long)((int)registers[rdhi])) << 32) | registers[rdlo]; + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + this.parent.Cycles -= cycles; + } + } + private void DispatchFunc28() + { + uint rs, rm; + int cycles; + // SMLALS rdlo, rdhi, rm, rs + { + uint rdhi = (this.curInstruction >> 16) & 0xF; + uint rdlo = (this.curInstruction >> 12) & 0xF; + rs = (this.curInstruction >> 8) & 0xF; + rm = this.curInstruction & 0xF; + + cycles = this.MultiplyCycleCalculation(rs) + 2; + + long accum = (((long)((int)registers[rdhi])) << 32) | registers[rdlo]; + long result = ((long)((int)registers[rm])) * ((long)((int)registers[rs])); + result += accum; + registers[rdhi] = (uint)(result >> 32); + registers[rdlo] = (uint)(result & 0xFFFFFFFF); + + negative = registers[rdhi] >> 31; + zero = (registers[rdhi] == 0 && registers[rdlo] == 0) ? 1U : 0U; + + this.parent.Cycles -= cycles; + } + } + private void DispatchFunc29() + { + uint rn, rd, address, offset; + // STRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + } + private void DispatchFunc30() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // STRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + } + private void DispatchFunc31() + { + uint rn, rd, address, offset; + // STRH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + } + private void DispatchFunc32() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // STRH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + } + private void DispatchFunc33() + { + uint rn, rd, address, offset; + // STRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + } + private void DispatchFunc34() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // STRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + } + private void DispatchFunc35() + { + uint rn, rd, address, offset; + // STRH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + } + private void DispatchFunc36() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // STRH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + this.memory.WriteU16(address, (ushort)(registers[rd] & 0xFFFF)); + registers[rn] = address + offset; + } + private void DispatchFunc37() + { + uint rn, rd, offset; + // STRH rd, [rn, -rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + this.memory.WriteU16(registers[rn] + offset, (ushort)(registers[rd] & 0xFFFF)); + } + private void DispatchFunc38() + { + uint rn, rd, offset; + // STRH rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rn] += offset; + this.memory.WriteU16(registers[rn], (ushort)(registers[rd] & 0xFFFF)); + } + private void DispatchFunc39() + { + uint rn, rd, offset; + // STRH rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + this.memory.WriteU16(registers[rn] + offset, (ushort)(registers[rd] & 0xFFFF)); + } + private void DispatchFunc40() + { + uint rn, rd, offset; + // STRH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rn] += offset; + this.memory.WriteU16(registers[rn], (ushort)(registers[rd] & 0xFFFF)); + } + private void DispatchFunc41() + { + uint rn, rd, offset; + // STRH rd, [rn, rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + this.memory.WriteU16(registers[rn] + offset, (ushort)(registers[rd] & 0xFFFF)); + } + private void DispatchFunc42() + { + uint rn, rd, offset; + // STRH rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rn] += offset; + this.memory.WriteU16(registers[rn], (ushort)(registers[rd] & 0xFFFF)); + } + private void DispatchFunc43() + { + uint rn, rd, offset; + // STRH rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + this.memory.WriteU16(registers[rn] + offset, (ushort)(registers[rd] & 0xFFFF)); + } + private void DispatchFunc44() + { + uint rn, rd, offset; + // STRH rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rn] += offset; + this.memory.WriteU16(registers[rn], (ushort)(registers[rd] & 0xFFFF)); + } + private void DispatchFunc45() + { + uint rn, rd, address, offset; + // LDRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc46() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc47() + { + uint rn, rd, address, offset; + // LDRH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc48() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc49() + { + uint rn, rd, address, offset; + // LDRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc50() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc51() + { + uint rn, rd, address, offset; + // LDRH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc52() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc53() + { + uint rn, rd, offset; + // LDRH rd, [rn, -rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc54() + { + uint rn, rd, offset; + // LDRH rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rn] += offset; + + registers[rd] = this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc55() + { + uint rn, rd, offset; + // LDRH rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc56() + { + uint rn, rd, offset; + // LDRH rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc57() + { + uint rn, rd, offset; + // LDRH rd, [rn, rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc58() + { + uint rn, rd, offset; + // LDRH rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc59() + { + uint rn, rd, offset; + // LDRH rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc60() + { + uint rn, rd, offset; + // LDRH rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rn] += offset; + registers[rd] = this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc61() + { + uint rn, rd, address, offset; + // LDRSB rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc62() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRSB rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc63() + { + uint rn, rd, address, offset; + // LDRSB rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc64() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRSB rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc65() + { + uint rn, rd, address, offset; + // LDRSB rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc66() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRSB rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc67() + { + uint rn, rd, address, offset; + // LDRSB rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc68() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRSB rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc69() + { + uint rn, rd, offset; + // LDRSB rd, [rn, -rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc70() + { + uint rn, rd, offset; + // LDRSB rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc71() + { + uint rn, rd, offset; + // LDRSB rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc72() + { + uint rn, rd, offset; + // LDRSB rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc73() + { + uint rn, rd, offset; + // LDRSB rd, [rn, rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc74() + { + uint rn, rd, offset; + // LDRSB rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc75() + { + uint rn, rd, offset; + // LDRSB rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc76() + { + uint rn, rd, offset; + // LDRSB rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rn] += offset; + registers[rd] = (uint)(sbyte)this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc77() + { + uint rn, rd, address, offset; + // LDRSH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc78() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRSH rd, [rn], -rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc79() + { + uint rn, rd, address, offset; + // LDRSH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc80() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRSH rd, [rn], -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc81() + { + uint rn, rd, address, offset; + // LDRSH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc82() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRSH rd, [rn], rm + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc83() + { + uint rn, rd, address, offset; + // LDRSH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc84() + { + uint rn, rd, address, offset; + // Writeback bit set, instruction is unpredictable + // LDRSH rd, [rn], immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + address = registers[rn]; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(short)this.memory.ReadU16(address); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] = address + offset; + } + private void DispatchFunc85() + { + uint rn, rd, offset; + // LDRSH rd, [rn, -rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc86() + { + uint rn, rd, offset; + // LDRSH rd, [rn, -rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc87() + { + uint rn, rd, offset; + // LDRSH rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc88() + { + uint rn, rd, offset; + // LDRSH rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc89() + { + uint rn, rd, offset; + // LDRSH rd, [rn, rm] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc90() + { + uint rn, rd, offset; + // LDRSH rd, [rn, rm]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.registers[this.curInstruction & 0xF]; + + registers[rn] += offset; + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc91() + { + uint rn, rd, offset; + // LDRSH rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc92() + { + uint rn, rd, offset; + // LDRSH rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = ((this.curInstruction & 0xF00) >> 4) | (this.curInstruction & 0xF); + + registers[rn] += offset; + registers[rd] = (uint)(short)this.memory.ReadU16(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc93() + { + uint rn, rd; + // EOR rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc94() + { + uint rn, rd; + // EOR rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc95() + { + uint rn, rd; + // EOR rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc96() + { + uint rn, rd; + // EOR rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc97() + { + uint rn, rd; + // EOR rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc98() + { + uint rn, rd; + // EOR rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc99() + { + uint rn, rd; + // EOR rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc100() + { + uint rn, rd; + // EOR rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc101() + { + uint rn, rd; + // EORS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc102() + { + uint rn, rd; + // EORS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc103() + { + uint rn, rd; + // EORS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc104() + { + uint rn, rd; + // EORS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc105() + { + uint rn, rd; + // EORS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc106() + { + uint rn, rd; + // EORS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc107() + { + uint rn, rd; + // EORS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc108() + { + uint rn, rd; + // EORS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn ^ BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc109() + { + uint rn, rd; + // SUB rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc110() + { + uint rn, rd; + // SUB rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc111() + { + uint rn, rd; + // SUB rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc112() + { + uint rn, rd; + // SUB rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc113() + { + uint rn, rd; + // SUB rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc114() + { + uint rn, rd; + // SUB rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc115() + { + uint rn, rd; + // SUB rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc116() + { + uint rn, rd; + // SUB rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc117() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc118() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc119() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc120() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc121() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc122() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc123() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc124() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc125() + { + uint rn, rd; + // RSB rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc126() + { + uint rn, rd; + // RSB rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslReg() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc127() + { + uint rn, rd; + // RSB rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc128() + { + uint rn, rd; + // RSB rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrReg() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc129() + { + uint rn, rd; + // RSB rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc130() + { + uint rn, rd; + // RSB rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrReg() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc131() + { + uint rn, rd; + // RSB rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc132() + { + uint rn, rd; + // RSB rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorReg() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc133() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc134() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc135() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc136() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc137() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc138() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc139() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc140() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc141() + { + uint rn, rd; + // ADD rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc142() + { + uint rn, rd; + // ADD rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc143() + { + uint rn, rd; + // ADD rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc144() + { + uint rn, rd; + // ADD rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc145() + { + uint rn, rd; + // ADD rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc146() + { + uint rn, rd; + // ADD rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc147() + { + uint rn, rd; + // ADD rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc148() + { + uint rn, rd; + // ADD rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc149() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc150() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc151() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc152() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc153() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc154() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc155() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc156() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc157() + { + uint rn, rd; + // ADC rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLslImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc158() + { + uint rn, rd; + // ADC rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLslReg() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc159() + { + uint rn, rd; + // ADC rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLsrImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc160() + { + uint rn, rd; + // ADC rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterLsrReg() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc161() + { + uint rn, rd; + // ADC rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterAsrImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc162() + { + uint rn, rd; + // ADC rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterAsrReg() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc163() + { + uint rn, rd; + // ADC rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterRorImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc164() + { + uint rn, rd; + // ADC rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn + BarrelShifterRorReg() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc165() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc166() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc167() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc168() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc169() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc170() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc171() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc172() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc173() + { + uint rn, rd; + // SBC rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLslImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc174() + { + uint rn, rd; + // SBC rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLslReg() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc175() + { + uint rn, rd; + // SBC rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLsrImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc176() + { + uint rn, rd; + // SBC rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterLsrReg() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc177() + { + uint rn, rd; + // SBC rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterAsrImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc178() + { + uint rn, rd; + // SBC rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterAsrReg() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc179() + { + uint rn, rd; + // SBC rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterRorImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc180() + { + uint rn, rd; + // SBC rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn - BarrelShifterRorReg() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc181() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc182() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc183() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc184() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc185() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc186() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc187() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc188() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc189() + { + uint rn, rd; + // RSC rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc190() + { + uint rn, rd; + // RSC rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslReg() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc191() + { + uint rn, rd; + // RSC rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc192() + { + uint rn, rd; + // RSC rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrReg() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc193() + { + uint rn, rd; + // RSC rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc194() + { + uint rn, rd; + // RSC rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrReg() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc195() + { + uint rn, rd; + // RSC rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc196() + { + uint rn, rd; + // RSC rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorReg() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc197() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc198() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLslReg(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc199() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc200() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterLsrReg(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc201() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc202() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterAsrReg(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc203() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc204() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + shifterOperand = BarrelShifterRorReg(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc205() + { + uint rn, rd, rm; + // SWP rd, rm, [rn] + { + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + rm = this.curInstruction & 0xF; + uint tmp = this.memory.ReadU32(registers[rn]); + this.memory.WriteU32(registers[rn], registers[rm]); + registers[rd] = tmp; + + if (rd == 15) + { + this.FlushQueue(); + } + } + } + private void DispatchFunc206() + { + uint rn, rd, rm; + // SWPB rd, rm, [rn] + { + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + rm = this.curInstruction & 0xF; + + byte tmp = this.memory.ReadU8(registers[rn]); + this.memory.WriteU8(registers[rn], (byte)(registers[rm] & 0xFF)); + registers[rd] = tmp; + + if (rd == 15) + { + this.FlushQueue(); + } + } + } + private void DispatchFunc207() + { + uint rd; + // MRS rd, cpsr + rd = (this.curInstruction >> 12) & 0xF; + this.PackFlags(); + registers[rd] = this.parent.CPSR; + } + private void DispatchFunc208() + { + uint rd; + // MRS rd, spsr + rd = (this.curInstruction >> 12) & 0xF; + if (this.parent.SPSRExists) registers[rd] = this.parent.SPSR; + } + private void DispatchFunc209() + { + uint rm; + // MSR cpsr, rm + { + rm = registers[this.curInstruction & 0xF]; + bool userMode = (this.parent.CPSR & 0x1F) == Arm7Processor.USR; + + this.PackFlags(); + + uint tmpCPSR = this.parent.CPSR; + + if ((this.curInstruction & (1 << 16)) == 1 << 16 && !userMode) + { + tmpCPSR &= 0xFFFFFF00; + tmpCPSR |= rm & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17 && !userMode) + { + tmpCPSR &= 0xFFFF00FF; + tmpCPSR |= rm & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18 && !userMode) + { + tmpCPSR &= 0xFF00FFFF; + tmpCPSR |= rm & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + tmpCPSR &= 0x00FFFFFF; + tmpCPSR |= rm & 0xFF000000; + } + + this.parent.WriteCpsr(tmpCPSR); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + } + } + private void DispatchFunc210() + { + uint rm; + // MSR spsr, rm + if (this.parent.SPSRExists) + { + rm = registers[this.curInstruction & 0xF]; + if ((this.curInstruction & (1 << 16)) == 1 << 16) + { + this.parent.SPSR &= 0xFFFFFF00; + this.parent.SPSR |= rm & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17) + { + this.parent.SPSR &= 0xFFFF00FF; + this.parent.SPSR |= rm & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18) + { + this.parent.SPSR &= 0xFF00FFFF; + this.parent.SPSR |= rm & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + this.parent.SPSR &= 0x00FFFFFF; + this.parent.SPSR |= rm & 0xFF000000; + } + } + } + private void DispatchFunc211() + { + // MSR cpsr, immed + { + uint immed = this.curInstruction & 0xFF; + int rotateAmount = (int)(((this.curInstruction >> 8) & 0xF) * 2); + + immed = (immed >> rotateAmount) | (immed << (32 - rotateAmount)); + + bool userMode = (this.parent.CPSR & 0x1F) == Arm7Processor.USR; + + this.PackFlags(); + + uint tmpCPSR = this.parent.CPSR; + + if ((this.curInstruction & (1 << 16)) == 1 << 16 && !userMode) + { + tmpCPSR &= 0xFFFFFF00; + tmpCPSR |= immed & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17 && !userMode) + { + tmpCPSR &= 0xFFFF00FF; + tmpCPSR |= immed & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18 && !userMode) + { + tmpCPSR &= 0xFF00FFFF; + tmpCPSR |= immed & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + tmpCPSR &= 0x00FFFFFF; + tmpCPSR |= immed & 0xFF000000; + } + + this.parent.WriteCpsr(tmpCPSR); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + } + } + private void DispatchFunc212() + { + // MSR spsr, immed + if (this.parent.SPSRExists) + { + uint immed = this.curInstruction & 0xFF; + int rotateAmount = (int)(((this.curInstruction >> 8) & 0xF) * 2); + + immed = (immed >> rotateAmount) | (immed << (32 - rotateAmount)); + + if ((this.curInstruction & (1 << 16)) == 1 << 16) + { + this.parent.SPSR &= 0xFFFFFF00; + this.parent.SPSR |= immed & 0x000000FF; + } + if ((this.curInstruction & (1 << 17)) == 1 << 17) + { + this.parent.SPSR &= 0xFFFF00FF; + this.parent.SPSR |= immed & 0x0000FF00; + } + if ((this.curInstruction & (1 << 18)) == 1 << 18) + { + this.parent.SPSR &= 0xFF00FFFF; + this.parent.SPSR |= immed & 0x00FF0000; + } + if ((this.curInstruction & (1 << 19)) == 1 << 19) + { + this.parent.SPSR &= 0x00FFFFFF; + this.parent.SPSR |= immed & 0xFF000000; + } + } + } + private void DispatchFunc213() + { + uint rm; + // BX rm + rm = this.curInstruction & 0xf; + + this.PackFlags(); + + this.parent.CPSR &= ~Arm7Processor.T_MASK; + this.parent.CPSR |= (registers[rm] & 1) << Arm7Processor.T_BIT; + + registers[15] = registers[rm] & (~1U); + + this.UnpackFlags(); + + // Check for branch back to Thumb Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) == Arm7Processor.T_MASK) + { + this.thumbMode = true; + return; + } + + this.FlushQueue(); + } + private void DispatchFunc214() + { + uint rn, alu; + // TSTS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterLslImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc215() + { + uint rn, alu; + // TSTS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterLslReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc216() + { + uint rn, alu; + // TSTS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterLsrImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc217() + { + uint rn, alu; + // TSTS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterLsrReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc218() + { + uint rn, alu; + // TSTS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterAsrImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc219() + { + uint rn, alu; + // TSTS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterAsrReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc220() + { + uint rn, alu; + // TSTS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterRorImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc221() + { + uint rn, rd, alu; + // TSTS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + alu = rn & BarrelShifterRorReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc222() + { + uint rn, alu; + // TEQS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterLslImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc223() + { + uint rn, alu; + // TEQS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterLslReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc224() + { + uint rn, alu; + // TEQS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterLsrImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc225() + { + uint rn, alu; + // TEQS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterLsrReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc226() + { + uint rn, alu; + // TEQS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterAsrImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc227() + { + uint rn, alu; + // TEQS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterAsrReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc228() + { + uint rn, alu; + // TEQS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterRorImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc229() + { + uint rn, rd, alu; + // TEQS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + alu = rn ^ BarrelShifterRorReg(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc230() + { + uint rn, shifterOperand, alu; + // CMP rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLslImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc231() + { + uint rn, shifterOperand, alu; + // CMP rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLslReg(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc232() + { + uint rn, shifterOperand, alu; + // CMP rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLsrImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc233() + { + uint rn, shifterOperand, alu; + // CMP rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLsrReg(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc234() + { + uint rn, shifterOperand, alu; + // CMP rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterAsrImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc235() + { + uint rn, shifterOperand, alu; + // CMP rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterAsrReg(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc236() + { + uint rn, shifterOperand, alu; + // CMP rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterRorImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc237() + { + uint rn, shifterOperand, alu; + // CMP rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterRorReg(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc238() + { + uint rn, shifterOperand, alu; + // CMN rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLslImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc239() + { + uint rn, shifterOperand, alu; + // CMN rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLslReg(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc240() + { + uint rn, shifterOperand, alu; + // CMN rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLsrImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc241() + { + uint rn, shifterOperand, alu; + // CMN rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterLsrReg(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc242() + { + uint rn, shifterOperand, alu; + // CMN rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterAsrImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc243() + { + uint rn, shifterOperand, alu; + // CMN rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterAsrReg(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc244() + { + uint rn, shifterOperand, alu; + // CMN rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterRorImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc245() + { + uint rn, shifterOperand, alu; + // CMN rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterRorReg(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc246() + { + uint rn, rd; + // ORR rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc247() + { + uint rn, rd; + // ORR rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc248() + { + uint rn, rd; + // ORR rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc249() + { + uint rn, rd; + // ORR rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc250() + { + uint rn, rd; + // ORR rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc251() + { + uint rn, rd; + // ORR rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc252() + { + uint rn, rd; + // ORR rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc253() + { + uint rn, rd; + // ORR rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc254() + { + uint rn, rd; + // ORRS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc255() + { + uint rn, rd; + // ORRS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc256() + { + uint rn, rd; + // ORRS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc257() + { + uint rn, rd; + // ORRS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc258() + { + uint rn, rd; + // ORRS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc259() + { + uint rn, rd; + // ORRS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc260() + { + uint rn, rd; + // ORRS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc261() + { + uint rn, rd; + // ORRS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn | BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc262() + { + uint rd; + // MOV rd, rm lsl immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc263() + { + uint rd; + // MOV rd, rm lsl rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc264() + { + uint rd; + // MOV rd, rm lsr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc265() + { + uint rd; + // MOV rd, rm lsr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc266() + { + uint rd; + // MOV rd, rm asr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc267() + { + uint rd; + // MOV rd, rm asr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc268() + { + uint rd; + // MOV rd, rm ror immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc269() + { + uint rd; + // MOV rd, rm ror rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc270() + { + uint rd; + // MOVS rd, rm lsl immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc271() + { + uint rd; + // MOVS rd, rm lsl rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc272() + { + uint rd; + // MOVS rd, rm lsr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc273() + { + uint rd; + // MOVS rd, rn, rm lsr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc274() + { + uint rd; + // MOVS rd, rn, rm asr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc275() + { + uint rd; + // MOVS rd, rn, rm asr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc276() + { + uint rd; + // MOVS rd, rn, rm ror immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc277() + { + uint rd; + // MOVS rd, rn, rm ror rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc278() + { + uint rn, rd; + // BIC rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc279() + { + uint rn, rd; + // BIC rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc280() + { + uint rn, rd; + // BIC rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc281() + { + uint rn, rd; + // BIC rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc282() + { + uint rn, rd; + // BIC rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc283() + { + uint rn, rd; + // BIC rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc284() + { + uint rn, rd; + // BIC rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc285() + { + uint rn, rd; + // BIC rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc286() + { + uint rn, rd; + // BICS rd, rn, rm lsl immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc287() + { + uint rn, rd; + // BICS rd, rn, rm lsl rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc288() + { + uint rn, rd; + // BICS rd, rn, rm lsr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc289() + { + uint rn, rd; + // BICS rd, rn, rm lsr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc290() + { + uint rn, rd; + // BICS rd, rn, rm asr immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc291() + { + uint rn, rd; + // BICS rd, rn, rm asr rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc292() + { + uint rn, rd; + // BICS rd, rn, rm ror immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc293() + { + uint rn, rd; + // BICS rd, rn, rm ror rs + rn = registers[(this.curInstruction >> 16) & 0xF]; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = rn & ~BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc294() + { + uint rd; + // MVN rd, rm lsl immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLslImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc295() + { + uint rd; + // MVN rd, rm lsl rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLslReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc296() + { + uint rd; + // MVN rd, rm lsr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc297() + { + uint rd; + // MVN rd, rm lsr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc298() + { + uint rd; + // MVN rd, rm asr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterAsrImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc299() + { + uint rd; + // MVN rd, rm asr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterAsrReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc300() + { + uint rd; + // MVN rd, rm ror immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterRorImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc301() + { + uint rd; + // MVN rd, rm ror rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterRorReg(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc302() + { + uint rd; + // MVNS rd, rm lsl immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLslImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc303() + { + uint rd; + // MVNS rd, rm lsl rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLslReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc304() + { + uint rd; + // MVNS rd, rm lsr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc305() + { + uint rd; + // MVNS rd, rn, rm lsr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterLsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc306() + { + uint rd; + // MVNS rd, rn, rm asr immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterAsrImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc307() + { + uint rd; + // MVNS rd, rn, rm asr rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterAsrReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc308() + { + uint rd; + // MVNS rd, rn, rm ror immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterRorImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc309() + { + uint rd; + // MVNS rd, rn, rm ror rs + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterRorReg(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc310() + { + uint rn, rd; + // AND rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn & BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc311() + { + uint rn, rd; + // ANDS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn & BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc312() + { + uint rn, rd; + // EOR rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn ^ BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc313() + { + uint rn, rd; + // EORS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn ^ BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc314() + { + uint rn, rd; + // SUB rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn - BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc315() + { + uint rn, rd, shifterOperand; + // SUBS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = rn - shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc316() + { + uint rn, rd; + // RSB rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = BarrelShifterImmed() - rn; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc317() + { + uint rn, rd, shifterOperand; + // RSBS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = shifterOperand - rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc318() + { + uint rn, rd; + // ADD rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn + BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc319() + { + uint rn, rd, shifterOperand; + // ADDS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = rn + shifterOperand; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc320() + { + uint rn, rd; + // ADC rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn + BarrelShifterImmed() + carry; + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc321() + { + uint rn, rd, shifterOperand; + // ADCS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = rn + shifterOperand + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc322() + { + uint rn, rd; + // SBC rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn - BarrelShifterImmed() - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc323() + { + uint rn, rd, shifterOperand; + // SBCS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = rn - shifterOperand - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc324() + { + uint rn, rd; + // RSC rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = BarrelShifterImmed() - rn - (1U - carry); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc325() + { + uint rn, rd, shifterOperand; + // RSCS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + registers[rd] = shifterOperand - rn - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(shifterOperand, rn, registers[rd]); + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc326() + { + uint rn, alu; + // TSTS rn, immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn & BarrelShifterImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc327() + { + uint rn, alu; + // TEQS rn, immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + alu = rn ^ BarrelShifterImmed(); + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + carry = this.shifterCarry; + } + private void DispatchFunc328() + { + uint rn, shifterOperand, alu; + // CMP rn, immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + alu = rn - shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(rn, shifterOperand, alu); + } + private void DispatchFunc329() + { + uint rn, shifterOperand, alu; + // CMN rn, immed + rn = registers[(this.curInstruction >> 16) & 0xF]; + + shifterOperand = BarrelShifterImmed(); + alu = rn + shifterOperand; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(rn, shifterOperand, alu); + } + private void DispatchFunc330() + { + uint rn, rd; + // ORR rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn | BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc331() + { + uint rn, rd; + // ORRS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn | BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc332() + { + uint rd; + // MOV rd, immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc333() + { + uint rd; + // MOVS rd, immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc334() + { + uint rn, rd; + // BIC rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn & ~BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc335() + { + uint rn, rd; + // BICS rd, rn, immed + rd = (this.curInstruction >> 12) & 0xF; + rn = registers[(this.curInstruction >> 16) & 0xF]; + + registers[rd] = rn & ~BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc336() + { + uint rd; + // MVN rd, immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterImmed(); + + if (rd == 15) + { + this.FlushQueue(); + } + } + private void DispatchFunc337() + { + uint rd; + // MVNS rd, immed + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = ~BarrelShifterImmed(); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + carry = this.shifterCarry; + + if (rd == 15) + { + this.DataProcessingWriteToR15(); + } + } + private void DispatchFunc338() + { + uint rn, rd, offset, alu; + // STR rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc339() + { + uint rn, rd, offset, alu; + // STRT rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc340() + { + uint rn, rd, offset, alu; + // STRB rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc341() + { + uint rn, rd, offset, alu; + // STRBT rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc342() + { + uint rn, rd, alu; + // STR rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.curInstruction & 0xFFF; + } + private void DispatchFunc343() + { + uint rn, rd, alu; + // STRT rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.curInstruction & 0xFFF; + } + private void DispatchFunc344() + { + uint rn, rd, alu; + // STRB rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.curInstruction & 0xFFF; + } + private void DispatchFunc345() + { + uint rn, rd, alu; + // STRBT rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.curInstruction & 0xFFF; + } + private void DispatchFunc346() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + } + private void DispatchFunc347() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc348() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + } + private void DispatchFunc349() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc350() + { + uint rn, rd, alu; + // STR rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + (this.curInstruction & 0xFFF), alu); + } + private void DispatchFunc351() + { + uint rn, rd, alu; + // STRT rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.curInstruction & 0xFFF; + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc352() + { + uint rn, rd, alu; + // STRB rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + (this.curInstruction & 0xFFF), (byte)(alu & 0xFF)); + } + private void DispatchFunc353() + { + uint rn, rd, alu; + // STRB rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.curInstruction & 0xFFF; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc354() + { + uint rn, rd, offset; + // LDR rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc355() + { + uint rn, rd, offset; + // LDRT rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc356() + { + uint rn, rd, offset; + // LDRB rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc357() + { + uint rn, rd, offset; + // LDRBT rd, rn, -immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc358() + { + uint rn, rd; + // LDR rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += this.curInstruction & 0xFFF; + } + private void DispatchFunc359() + { + uint rn, rd; + // LDRT rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += this.curInstruction & 0xFFF; + } + private void DispatchFunc360() + { + uint rn, rd; + // LDRB rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += this.curInstruction & 0xFFF; + } + private void DispatchFunc361() + { + uint rn, rd; + // LDRBT rd, rn, immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += this.curInstruction & 0xFFF; + } + private void DispatchFunc362() + { + uint rn, rd, offset; + // LDR rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc363() + { + uint rn, rd, offset; + // LDR rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc364() + { + uint rn, rd, offset; + // LDRB rd, [rn, -immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc365() + { + uint rn, rd, offset; + // LDRB rd, [rn, -immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.curInstruction & 0xFFF; + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc366() + { + uint rn, rd; + // LDR rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + (this.curInstruction & 0xFFF)); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc367() + { + uint rn, rd; + // LDR rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.curInstruction & 0xFFF; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc368() + { + uint rn, rd; + // LDRB rd, [rn, immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + (this.curInstruction & 0xFFF)); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc369() + { + uint rn, rd; + // LDRB rd, [rn, immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.curInstruction & 0xFFF; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc370() + { + uint rn, rd, offset, alu; + // STR rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc371() + { + uint rn, rd, offset, alu; + // STR rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc372() + { + uint rn, rd, offset, alu; + // STR rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc373() + { + uint rn, rd, offset, alu; + // STR rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc374() + { + uint rn, rd, offset, alu; + // STRT rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc375() + { + uint rn, rd, offset, alu; + // STRT rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc376() + { + uint rn, rd, offset, alu; + // STRT rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc377() + { + uint rn, rd, offset, alu; + // STRT rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += offset; + } + private void DispatchFunc378() + { + uint rn, rd, offset, alu; + // STRB rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc379() + { + uint rn, rd, offset, alu; + // STRB rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc380() + { + uint rn, rd, offset, alu; + // STRB rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc381() + { + uint rn, rd, offset, alu; + // STRB rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc382() + { + uint rn, rd, offset, alu; + // STRBT rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc383() + { + uint rn, rd, offset, alu; + // STRBT rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc384() + { + uint rn, rd, offset, alu; + // STRBT rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc385() + { + uint rn, rd, offset, alu; + // STRBT rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += offset; + } + private void DispatchFunc386() + { + uint rn, rd, alu; + // STR rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterLslImmed(); + } + private void DispatchFunc387() + { + uint rn, rd, alu; + // STR rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterLsrImmed(); + } + private void DispatchFunc388() + { + uint rn, rd, alu; + // STR rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterAsrImmed(); + } + private void DispatchFunc389() + { + uint rn, rd, alu; + // STR rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterRorImmed(); + } + private void DispatchFunc390() + { + uint rn, rd, alu; + // STRT rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterLslImmed(); + } + private void DispatchFunc391() + { + uint rn, rd, alu; + // STRT rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterLsrImmed(); + } + private void DispatchFunc392() + { + uint rn, rd, alu; + // STRT rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterAsrImmed(); + } + private void DispatchFunc393() + { + uint rn, rd, alu; + // STRT rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn], alu); + registers[rn] += this.BarrelShifterRorImmed(); + } + private void DispatchFunc394() + { + uint rn, rd, alu; + // STRB rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterLslImmed(); + } + private void DispatchFunc395() + { + uint rn, rd, alu; + // STRB rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterLsrImmed(); + } + private void DispatchFunc396() + { + uint rn, rd, alu; + // STRB rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterAsrImmed(); + } + private void DispatchFunc397() + { + uint rn, rd, alu; + // STRB rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterRorImmed(); + } + private void DispatchFunc398() + { + uint rn, rd, alu; + // STRBT rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterLslImmed(); + } + private void DispatchFunc399() + { + uint rn, rd, alu; + // STRBT rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterLsrImmed(); + } + private void DispatchFunc400() + { + uint rn, rd, alu; + // STRBT rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterAsrImmed(); + } + private void DispatchFunc401() + { + uint rn, rd, alu; + // STRBT rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + registers[rn] += this.BarrelShifterRorImmed(); + } + private void DispatchFunc402() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + } + private void DispatchFunc403() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + } + private void DispatchFunc404() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + } + private void DispatchFunc405() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + offset, alu); + } + private void DispatchFunc406() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc407() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc408() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc409() + { + uint rn, rd, offset, alu; + // STR rd, [rn, -rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc410() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + } + private void DispatchFunc411() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + } + private void DispatchFunc412() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + } + private void DispatchFunc413() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + offset, (byte)(alu & 0xFF)); + } + private void DispatchFunc414() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc415() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc416() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc417() + { + uint rn, rd, offset, alu; + // STRB rd, [rn, -rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += offset; + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc418() + { + uint rn, rd, alu; + // STR rd, [rn, rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + this.BarrelShifterLslImmed(), alu); + } + private void DispatchFunc419() + { + uint rn, rd, alu; + // STR rd, [rn, rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + this.BarrelShifterLsrImmed(), alu); + } + private void DispatchFunc420() + { + uint rn, rd, alu; + // STR rd, [rn, rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + this.BarrelShifterAsrImmed(), alu); + } + private void DispatchFunc421() + { + uint rn, rd, alu; + // STR rd, [rn, rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU32(registers[rn] + this.BarrelShifterRorImmed(), alu); + } + private void DispatchFunc422() + { + uint rn, rd, alu; + // STR rd, [rn, rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterLslImmed(); + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc423() + { + uint rn, rd, alu; + // STR rd, [rn, rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterLsrImmed(); + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc424() + { + uint rn, rd, alu; + // STR rd, [rn, rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterAsrImmed(); + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc425() + { + uint rn, rd, alu; + // STR rd, [rn, rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterRorImmed(); + this.memory.WriteU32(registers[rn], alu); + } + private void DispatchFunc426() + { + uint rn, rd, alu; + // STRB rd, [rn, rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + this.BarrelShifterLslImmed(), (byte)(alu & 0xFF)); + } + private void DispatchFunc427() + { + uint rn, rd, alu; + // STRB rd, [rn, rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + this.BarrelShifterLsrImmed(), (byte)(alu & 0xFF)); + } + private void DispatchFunc428() + { + uint rn, rd, alu; + // STRB rd, [rn, rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + this.BarrelShifterAsrImmed(), (byte)(alu & 0xFF)); + } + private void DispatchFunc429() + { + uint rn, rd, alu; + // STRB rd, [rn, rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + this.memory.WriteU8(registers[rn] + this.BarrelShifterRorImmed(), (byte)(alu & 0xFF)); + } + private void DispatchFunc430() + { + uint rn, rd, alu; + // STRB rd, [rn, rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterLslImmed(); + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc431() + { + uint rn, rd, alu; + // STRB rd, [rn, rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterLsrImmed(); + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc432() + { + uint rn, rd, alu; + // STRB rd, [rn, rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterAsrImmed(); + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc433() + { + uint rn, rd, alu; + // STRB rd, [rn, rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + alu = registers[rd]; + if (rd == 15) alu += 4; + + registers[rn] += this.BarrelShifterRorImmed(); + this.memory.WriteU8(registers[rn], (byte)(alu & 0xFF)); + } + private void DispatchFunc434() + { + uint rn, rd, offset; + // LDR rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc435() + { + uint rn, rd, offset; + // LDR rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc436() + { + uint rn, rd, offset; + // LDR rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc437() + { + uint rn, rd, offset; + // LDR rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc438() + { + uint rn, rd, offset; + // LDRT rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc439() + { + uint rn, rd, offset; + // LDRT rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc440() + { + uint rn, rd, offset; + // LDRT rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc441() + { + uint rn, rd, offset; + // LDRT rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc442() + { + uint rn, rd, offset; + // LDRB rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc443() + { + uint rn, rd, offset; + // LDRB rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc444() + { + uint rn, rd, offset; + // LDRB rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc445() + { + uint rn, rd, offset; + // LDRB rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc446() + { + uint rn, rd, offset; + // LDRBT rd, rn, -rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc447() + { + uint rn, rd, offset; + // LDRBT rd, rn, -rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc448() + { + uint rn, rd, offset; + // LDRBT rd, rn, -rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc449() + { + uint rn, rd, offset; + // LDRBT rd, rn, -rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc450() + { + uint rn, rd, offset; + // LDR rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc451() + { + uint rn, rd, offset; + // LDR rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc452() + { + uint rn, rd, offset; + // LDR rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc453() + { + uint rn, rd, offset; + // LDR rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc454() + { + uint rn, rd, offset; + // LDRT rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc455() + { + uint rn, rd, offset; + // LDRT rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc456() + { + uint rn, rd, offset; + // LDRT rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc457() + { + uint rn, rd, offset; + // LDRT rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc458() + { + uint rn, rd, offset; + // LDRB rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc459() + { + uint rn, rd, offset; + // LDRB rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc460() + { + uint rn, rd, offset; + // LDRB rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc461() + { + uint rn, rd, offset; + // LDRB rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc462() + { + uint rn, rd, offset; + // LDRBT rd, rn, rm lsl immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc463() + { + uint rn, rd, offset; + // LDRBT rd, rn, rm lsr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc464() + { + uint rn, rd, offset; + // LDRBT rd, rn, rm asr immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc465() + { + uint rn, rd, offset; + // LDRBT rd, rn, rm ror immed + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + + if (rn != rd) + registers[rn] += offset; + } + private void DispatchFunc466() + { + uint rn, rd, offset; + // LDR rd, [rn, -rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc467() + { + uint rn, rd, offset; + // LDR rd, [rn, -rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc468() + { + uint rn, rd, offset; + // LDR rd, [rn, -rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc469() + { + uint rn, rd, offset; + // LDR rd, [rn, -rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU32(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc470() + { + uint rn, rd, offset; + // LDR rd, [rn, -rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc471() + { + uint rn, rd, offset; + // LDR rd, [rn, -rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc472() + { + uint rn, rd, offset; + // LDR rd, [rn, -rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc473() + { + uint rn, rd, offset; + // LDR rd, [rn, -rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc474() + { + uint rn, rd, offset; + // LDRB rd, [rn, -rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc475() + { + uint rn, rd, offset; + // LDRB rd, [rn, -rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc476() + { + uint rn, rd, offset; + // LDRB rd, [rn, -rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc477() + { + uint rn, rd, offset; + // LDRB rd, [rn, -rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rd] = this.memory.ReadU8(registers[rn] + offset); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc478() + { + uint rn, rd, offset; + // LDRB rd, [rn, -rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLslImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc479() + { + uint rn, rd, offset; + // LDRB rd, [rn, -rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterLsrImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc480() + { + uint rn, rd, offset; + // LDRB rd, [rn, -rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterAsrImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc481() + { + uint rn, rd, offset; + // LDRB rd, [rn, -rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + offset = this.BarrelShifterRorImmed(); + offset = (uint)-offset; + + registers[rn] += offset; + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc482() + { + uint rn, rd; + // LDR rd, [rn, rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + this.BarrelShifterLslImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc483() + { + uint rn, rd; + // LDR rd, [rn, rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + this.BarrelShifterLsrImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc484() + { + uint rn, rd; + // LDR rd, [rn, rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + this.BarrelShifterAsrImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc485() + { + uint rn, rd; + // LDR rd, [rn, rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU32(registers[rn] + this.BarrelShifterRorImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc486() + { + uint rn, rd; + // LDR rd, [rn, rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc487() + { + uint rn, rd; + // LDR rd, [rn, rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc488() + { + uint rn, rd; + // LDR rd, [rn, rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc489() + { + uint rn, rd; + // LDR rd, [rn, rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU32(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc490() + { + uint rn, rd; + // LDRB rd, [rn, rm lsl immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + this.BarrelShifterLslImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc491() + { + uint rn, rd; + // LDRB rd, [rn, rm lsr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + this.BarrelShifterLsrImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc492() + { + uint rn, rd; + // LDRB rd, [rn, rm asr immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + this.BarrelShifterAsrImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc493() + { + uint rn, rd; + // LDRB rd, [rn, rm ror immed] + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rd] = this.memory.ReadU8(registers[rn] + this.BarrelShifterRorImmed()); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc494() + { + uint rn, rd; + // LDRB rd, [rn, rm lsl immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterLslImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc495() + { + uint rn, rd; + // LDRB rd, [rn, rm lsr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterLsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc496() + { + uint rn, rd; + // LDRB rd, [rn, rm asr immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterAsrImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc497() + { + uint rn, rd; + // LDRB rd, [rn, rm ror immed]! + rn = (this.curInstruction >> 16) & 0xF; + rd = (this.curInstruction >> 12) & 0xF; + + registers[rn] += this.BarrelShifterRorImmed(); + registers[rd] = this.memory.ReadU8(registers[rn]); + + if (rd == 15) + { + registers[rd] &= ~3U; + this.FlushQueue(); + } + } + private void DispatchFunc498() + { + { + uint branchOffset = this.curInstruction & 0x00FFFFFF; + if (branchOffset >> 23 == 1) branchOffset |= 0xFF000000; + + this.registers[15] += branchOffset << 2; + + this.FlushQueue(); + } + } + private void DispatchFunc499() + { + { + uint branchOffset = this.curInstruction & 0x00FFFFFF; + if (branchOffset >> 23 == 1) branchOffset |= 0xFF000000; + + this.registers[14] = this.registers[15] - 4U; + this.registers[15] += branchOffset << 2; + + this.FlushQueue(); + } + } + private void DispatchFunc500() + { + this.registers[15] -= 4U; + this.parent.EnterException(Arm7Processor.SVC, 0x8, false, false); + } + private void DefaultDispatchFunc() { this.NormalOps[(curInstruction >> 25) & 0x7](); } + private ExecuteInstructionDelegate[] fastDispatch = null; + private void InitializeDispatchFunc() + { + this.fastDispatch = new ExecuteInstructionDelegate[] { +DispatchFunc0,DispatchFunc1,DispatchFunc2,DispatchFunc3,DispatchFunc4,DispatchFunc5,DispatchFunc6,DispatchFunc7,DispatchFunc0,DispatchFunc17,DispatchFunc2,DispatchFunc29,DispatchFunc4,DispatchFunc16,DispatchFunc6,DispatchFunc16,DispatchFunc8,DispatchFunc9,DispatchFunc10,DispatchFunc11,DispatchFunc12 +,DispatchFunc13,DispatchFunc14,DispatchFunc15,DispatchFunc8,DispatchFunc18,DispatchFunc10,DispatchFunc45,DispatchFunc12,DispatchFunc61,DispatchFunc14,DispatchFunc77,DispatchFunc93,DispatchFunc94,DispatchFunc95,DispatchFunc96,DispatchFunc97,DispatchFunc98,DispatchFunc99,DispatchFunc100,DispatchFunc93 +,DispatchFunc19,DispatchFunc95,DispatchFunc30,DispatchFunc97,DispatchFunc16,DispatchFunc99,DispatchFunc16,DispatchFunc101,DispatchFunc102,DispatchFunc103,DispatchFunc104,DispatchFunc105,DispatchFunc106,DispatchFunc107,DispatchFunc108,DispatchFunc101,DispatchFunc20,DispatchFunc103,DispatchFunc46,DispatchFunc105 +,DispatchFunc62,DispatchFunc107,DispatchFunc78,DispatchFunc109,DispatchFunc110,DispatchFunc111,DispatchFunc112,DispatchFunc113,DispatchFunc114,DispatchFunc115,DispatchFunc116,DispatchFunc109,DefaultDispatchFunc,DispatchFunc111,DispatchFunc31,DispatchFunc113,DispatchFunc16,DispatchFunc115,DispatchFunc16,DispatchFunc117 +,DispatchFunc118,DispatchFunc119,DispatchFunc120,DispatchFunc121,DispatchFunc122,DispatchFunc123,DispatchFunc124,DispatchFunc117,DefaultDispatchFunc,DispatchFunc119,DispatchFunc47,DispatchFunc121,DispatchFunc63,DispatchFunc123,DispatchFunc79,DispatchFunc125,DispatchFunc126,DispatchFunc127,DispatchFunc128,DispatchFunc129 +,DispatchFunc130,DispatchFunc131,DispatchFunc132,DispatchFunc125,DefaultDispatchFunc,DispatchFunc127,DispatchFunc32,DispatchFunc129,DispatchFunc16,DispatchFunc131,DispatchFunc16,DispatchFunc133,DispatchFunc134,DispatchFunc135,DispatchFunc136,DispatchFunc137,DispatchFunc138,DispatchFunc139,DispatchFunc140,DispatchFunc133 +,DefaultDispatchFunc,DispatchFunc135,DispatchFunc48,DispatchFunc137,DispatchFunc64,DispatchFunc139,DispatchFunc80,DispatchFunc141,DispatchFunc142,DispatchFunc143,DispatchFunc144,DispatchFunc145,DispatchFunc146,DispatchFunc147,DispatchFunc148,DispatchFunc141,DispatchFunc21,DispatchFunc143,DispatchFunc33,DispatchFunc145 +,DispatchFunc16,DispatchFunc147,DispatchFunc16,DispatchFunc149,DispatchFunc150,DispatchFunc151,DispatchFunc152,DispatchFunc153,DispatchFunc154,DispatchFunc155,DispatchFunc156,DispatchFunc149,DispatchFunc22,DispatchFunc151,DispatchFunc49,DispatchFunc153,DispatchFunc65,DispatchFunc155,DispatchFunc81,DispatchFunc157 +,DispatchFunc158,DispatchFunc159,DispatchFunc160,DispatchFunc161,DispatchFunc162,DispatchFunc163,DispatchFunc164,DispatchFunc157,DispatchFunc23,DispatchFunc159,DispatchFunc34,DispatchFunc161,DispatchFunc16,DispatchFunc163,DispatchFunc16,DispatchFunc165,DispatchFunc166,DispatchFunc167,DispatchFunc168,DispatchFunc169 +,DispatchFunc170,DispatchFunc171,DispatchFunc172,DispatchFunc165,DispatchFunc24,DispatchFunc167,DispatchFunc50,DispatchFunc169,DispatchFunc66,DispatchFunc171,DispatchFunc82,DispatchFunc173,DispatchFunc174,DispatchFunc175,DispatchFunc176,DispatchFunc177,DispatchFunc178,DispatchFunc179,DispatchFunc180,DispatchFunc173 +,DispatchFunc25,DispatchFunc175,DispatchFunc35,DispatchFunc177,DispatchFunc16,DispatchFunc179,DispatchFunc16,DispatchFunc181,DispatchFunc182,DispatchFunc183,DispatchFunc184,DispatchFunc185,DispatchFunc186,DispatchFunc187,DispatchFunc188,DispatchFunc181,DispatchFunc26,DispatchFunc183,DispatchFunc51,DispatchFunc185 +,DispatchFunc67,DispatchFunc187,DispatchFunc83,DispatchFunc189,DispatchFunc190,DispatchFunc191,DispatchFunc192,DispatchFunc193,DispatchFunc194,DispatchFunc195,DispatchFunc196,DispatchFunc189,DispatchFunc27,DispatchFunc191,DispatchFunc36,DispatchFunc193,DispatchFunc16,DispatchFunc195,DispatchFunc16,DispatchFunc197 +,DispatchFunc198,DispatchFunc199,DispatchFunc200,DispatchFunc201,DispatchFunc202,DispatchFunc203,DispatchFunc204,DispatchFunc197,DispatchFunc28,DispatchFunc199,DispatchFunc52,DispatchFunc201,DispatchFunc68,DispatchFunc203,DispatchFunc84,DispatchFunc207,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc205,DefaultDispatchFunc,DispatchFunc37,DefaultDispatchFunc,DispatchFunc16,DefaultDispatchFunc,DispatchFunc16,DispatchFunc214,DispatchFunc215,DispatchFunc216,DispatchFunc217,DispatchFunc218,DispatchFunc219,DispatchFunc220,DispatchFunc221,DispatchFunc214 +,DefaultDispatchFunc,DispatchFunc216,DispatchFunc53,DispatchFunc218,DispatchFunc69,DispatchFunc220,DispatchFunc85,DispatchFunc209,DispatchFunc213,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc38,DefaultDispatchFunc +,DispatchFunc16,DefaultDispatchFunc,DispatchFunc16,DispatchFunc222,DispatchFunc223,DispatchFunc224,DispatchFunc225,DispatchFunc226,DispatchFunc227,DispatchFunc228,DispatchFunc229,DispatchFunc222,DefaultDispatchFunc,DispatchFunc224,DispatchFunc54,DispatchFunc226,DispatchFunc70,DispatchFunc228,DispatchFunc86,DispatchFunc208 +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc206,DefaultDispatchFunc,DispatchFunc39,DefaultDispatchFunc,DispatchFunc16,DefaultDispatchFunc,DispatchFunc16,DispatchFunc230,DispatchFunc231,DispatchFunc232,DispatchFunc233,DispatchFunc234 +,DispatchFunc235,DispatchFunc236,DispatchFunc237,DispatchFunc230,DefaultDispatchFunc,DispatchFunc232,DispatchFunc55,DispatchFunc234,DispatchFunc71,DispatchFunc236,DispatchFunc87,DispatchFunc210,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc40,DefaultDispatchFunc,DispatchFunc16,DefaultDispatchFunc,DispatchFunc16,DispatchFunc238,DispatchFunc239,DispatchFunc240,DispatchFunc241,DispatchFunc242,DispatchFunc243,DispatchFunc244,DispatchFunc245,DispatchFunc238,DefaultDispatchFunc,DispatchFunc240,DispatchFunc56,DispatchFunc242 +,DispatchFunc72,DispatchFunc244,DispatchFunc88,DispatchFunc246,DispatchFunc247,DispatchFunc248,DispatchFunc249,DispatchFunc250,DispatchFunc251,DispatchFunc252,DispatchFunc253,DispatchFunc246,DefaultDispatchFunc,DispatchFunc248,DispatchFunc41,DispatchFunc250,DispatchFunc16,DispatchFunc252,DispatchFunc16,DispatchFunc254 +,DispatchFunc255,DispatchFunc256,DispatchFunc257,DispatchFunc258,DispatchFunc259,DispatchFunc260,DispatchFunc261,DispatchFunc254,DefaultDispatchFunc,DispatchFunc256,DispatchFunc57,DispatchFunc258,DispatchFunc73,DispatchFunc260,DispatchFunc89,DispatchFunc262,DispatchFunc263,DispatchFunc264,DispatchFunc265,DispatchFunc266 +,DispatchFunc267,DispatchFunc268,DispatchFunc269,DispatchFunc262,DefaultDispatchFunc,DispatchFunc264,DispatchFunc42,DispatchFunc266,DispatchFunc16,DispatchFunc268,DispatchFunc16,DispatchFunc270,DispatchFunc271,DispatchFunc272,DispatchFunc273,DispatchFunc274,DispatchFunc275,DispatchFunc276,DispatchFunc277,DispatchFunc270 +,DefaultDispatchFunc,DispatchFunc272,DispatchFunc58,DispatchFunc274,DispatchFunc74,DispatchFunc276,DispatchFunc90,DispatchFunc278,DispatchFunc279,DispatchFunc280,DispatchFunc281,DispatchFunc282,DispatchFunc283,DispatchFunc284,DispatchFunc285,DispatchFunc278,DefaultDispatchFunc,DispatchFunc280,DispatchFunc43,DispatchFunc282 +,DispatchFunc16,DispatchFunc284,DispatchFunc16,DispatchFunc286,DispatchFunc287,DispatchFunc288,DispatchFunc289,DispatchFunc290,DispatchFunc291,DispatchFunc292,DispatchFunc293,DispatchFunc286,DefaultDispatchFunc,DispatchFunc288,DispatchFunc59,DispatchFunc290,DispatchFunc75,DispatchFunc292,DispatchFunc91,DispatchFunc294 +,DispatchFunc295,DispatchFunc296,DispatchFunc297,DispatchFunc298,DispatchFunc299,DispatchFunc300,DispatchFunc301,DispatchFunc294,DefaultDispatchFunc,DispatchFunc296,DispatchFunc44,DispatchFunc298,DispatchFunc16,DispatchFunc300,DispatchFunc16,DispatchFunc302,DispatchFunc303,DispatchFunc304,DispatchFunc305,DispatchFunc306 +,DispatchFunc307,DispatchFunc308,DispatchFunc309,DispatchFunc302,DefaultDispatchFunc,DispatchFunc304,DispatchFunc60,DispatchFunc306,DispatchFunc76,DispatchFunc308,DispatchFunc92,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310 +,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc310,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc311 +,DispatchFunc311,DispatchFunc311,DispatchFunc311,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc312,DispatchFunc313 +,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc313,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314 +,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc314,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315 +,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc315,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc316 +,DispatchFunc316,DispatchFunc316,DispatchFunc316,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc317,DispatchFunc318 +,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc318,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319 +,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc319,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320 +,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc320,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc321 +,DispatchFunc321,DispatchFunc321,DispatchFunc321,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc322,DispatchFunc323 +,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc323,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324 +,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc324,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325 +,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DispatchFunc325,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc326,DispatchFunc211 +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327 +,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DispatchFunc327,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc328 +,DispatchFunc328,DispatchFunc328,DispatchFunc328,DispatchFunc212,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc329 +,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc329,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330 +,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc330,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331 +,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc331,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc332 +,DispatchFunc332,DispatchFunc332,DispatchFunc332,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc333,DispatchFunc334 +,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc334,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335 +,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc335,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336 +,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc336,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc337 +,DispatchFunc337,DispatchFunc337,DispatchFunc337,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc338,DispatchFunc354 +,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc354,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339 +,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc339,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355 +,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc355,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc340 +,DispatchFunc340,DispatchFunc340,DispatchFunc340,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc356,DispatchFunc341 +,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc341,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357 +,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc357,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342 +,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc342,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc358 +,DispatchFunc358,DispatchFunc358,DispatchFunc358,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc343,DispatchFunc359 +,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc359,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344 +,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc344,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360 +,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc360,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc345 +,DispatchFunc345,DispatchFunc345,DispatchFunc345,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc361,DispatchFunc346 +,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc346,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362 +,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc362,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347 +,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc347,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc363 +,DispatchFunc363,DispatchFunc363,DispatchFunc363,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc348,DispatchFunc364 +,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc364,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349 +,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc349,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365 +,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc365,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc350 +,DispatchFunc350,DispatchFunc350,DispatchFunc350,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc366,DispatchFunc351 +,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc351,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367 +,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc367,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352 +,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc352,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc368 +,DispatchFunc368,DispatchFunc368,DispatchFunc368,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc353,DispatchFunc369 +,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc369,DispatchFunc370,DefaultDispatchFunc,DispatchFunc371,DefaultDispatchFunc,DispatchFunc372 +,DefaultDispatchFunc,DispatchFunc373,DefaultDispatchFunc,DispatchFunc370,DefaultDispatchFunc,DispatchFunc371,DefaultDispatchFunc,DispatchFunc372,DefaultDispatchFunc,DispatchFunc373,DefaultDispatchFunc,DispatchFunc434,DefaultDispatchFunc,DispatchFunc435,DefaultDispatchFunc,DispatchFunc436,DefaultDispatchFunc,DispatchFunc437,DefaultDispatchFunc,DispatchFunc434 +,DefaultDispatchFunc,DispatchFunc435,DefaultDispatchFunc,DispatchFunc436,DefaultDispatchFunc,DispatchFunc437,DefaultDispatchFunc,DispatchFunc374,DefaultDispatchFunc,DispatchFunc375,DefaultDispatchFunc,DispatchFunc376,DefaultDispatchFunc,DispatchFunc377,DefaultDispatchFunc,DispatchFunc374,DefaultDispatchFunc,DispatchFunc375,DefaultDispatchFunc,DispatchFunc376 +,DefaultDispatchFunc,DispatchFunc377,DefaultDispatchFunc,DispatchFunc438,DefaultDispatchFunc,DispatchFunc439,DefaultDispatchFunc,DispatchFunc440,DefaultDispatchFunc,DispatchFunc441,DefaultDispatchFunc,DispatchFunc438,DefaultDispatchFunc,DispatchFunc439,DefaultDispatchFunc,DispatchFunc440,DefaultDispatchFunc,DispatchFunc441,DefaultDispatchFunc,DispatchFunc378 +,DefaultDispatchFunc,DispatchFunc379,DefaultDispatchFunc,DispatchFunc380,DefaultDispatchFunc,DispatchFunc381,DefaultDispatchFunc,DispatchFunc378,DefaultDispatchFunc,DispatchFunc379,DefaultDispatchFunc,DispatchFunc380,DefaultDispatchFunc,DispatchFunc381,DefaultDispatchFunc,DispatchFunc442,DefaultDispatchFunc,DispatchFunc443,DefaultDispatchFunc,DispatchFunc444 +,DefaultDispatchFunc,DispatchFunc445,DefaultDispatchFunc,DispatchFunc442,DefaultDispatchFunc,DispatchFunc443,DefaultDispatchFunc,DispatchFunc444,DefaultDispatchFunc,DispatchFunc445,DefaultDispatchFunc,DispatchFunc382,DefaultDispatchFunc,DispatchFunc383,DefaultDispatchFunc,DispatchFunc384,DefaultDispatchFunc,DispatchFunc385,DefaultDispatchFunc,DispatchFunc382 +,DefaultDispatchFunc,DispatchFunc383,DefaultDispatchFunc,DispatchFunc384,DefaultDispatchFunc,DispatchFunc385,DefaultDispatchFunc,DispatchFunc446,DefaultDispatchFunc,DispatchFunc447,DefaultDispatchFunc,DispatchFunc448,DefaultDispatchFunc,DispatchFunc449,DefaultDispatchFunc,DispatchFunc446,DefaultDispatchFunc,DispatchFunc447,DefaultDispatchFunc,DispatchFunc448 +,DefaultDispatchFunc,DispatchFunc449,DefaultDispatchFunc,DispatchFunc386,DefaultDispatchFunc,DispatchFunc387,DefaultDispatchFunc,DispatchFunc388,DefaultDispatchFunc,DispatchFunc389,DefaultDispatchFunc,DispatchFunc386,DefaultDispatchFunc,DispatchFunc387,DefaultDispatchFunc,DispatchFunc388,DefaultDispatchFunc,DispatchFunc389,DefaultDispatchFunc,DispatchFunc450 +,DefaultDispatchFunc,DispatchFunc451,DefaultDispatchFunc,DispatchFunc452,DefaultDispatchFunc,DispatchFunc453,DefaultDispatchFunc,DispatchFunc450,DefaultDispatchFunc,DispatchFunc451,DefaultDispatchFunc,DispatchFunc452,DefaultDispatchFunc,DispatchFunc453,DefaultDispatchFunc,DispatchFunc390,DefaultDispatchFunc,DispatchFunc390,DefaultDispatchFunc,DispatchFunc391 +,DefaultDispatchFunc,DispatchFunc391,DefaultDispatchFunc,DispatchFunc392,DefaultDispatchFunc,DispatchFunc392,DefaultDispatchFunc,DispatchFunc393,DefaultDispatchFunc,DispatchFunc393,DefaultDispatchFunc,DispatchFunc454,DefaultDispatchFunc,DispatchFunc455,DefaultDispatchFunc,DispatchFunc456,DefaultDispatchFunc,DispatchFunc457,DefaultDispatchFunc,DispatchFunc454 +,DefaultDispatchFunc,DispatchFunc455,DefaultDispatchFunc,DispatchFunc456,DefaultDispatchFunc,DispatchFunc457,DefaultDispatchFunc,DispatchFunc394,DefaultDispatchFunc,DispatchFunc395,DefaultDispatchFunc,DispatchFunc396,DefaultDispatchFunc,DispatchFunc397,DefaultDispatchFunc,DispatchFunc394,DefaultDispatchFunc,DispatchFunc395,DefaultDispatchFunc,DispatchFunc396 +,DefaultDispatchFunc,DispatchFunc397,DefaultDispatchFunc,DispatchFunc458,DefaultDispatchFunc,DispatchFunc459,DefaultDispatchFunc,DispatchFunc460,DefaultDispatchFunc,DispatchFunc461,DefaultDispatchFunc,DispatchFunc458,DefaultDispatchFunc,DispatchFunc459,DefaultDispatchFunc,DispatchFunc460,DefaultDispatchFunc,DispatchFunc461,DefaultDispatchFunc,DispatchFunc398 +,DefaultDispatchFunc,DispatchFunc399,DefaultDispatchFunc,DispatchFunc400,DefaultDispatchFunc,DispatchFunc401,DefaultDispatchFunc,DispatchFunc398,DefaultDispatchFunc,DispatchFunc399,DefaultDispatchFunc,DispatchFunc400,DefaultDispatchFunc,DispatchFunc401,DefaultDispatchFunc,DispatchFunc462,DefaultDispatchFunc,DispatchFunc463,DefaultDispatchFunc,DispatchFunc464 +,DefaultDispatchFunc,DispatchFunc465,DefaultDispatchFunc,DispatchFunc462,DefaultDispatchFunc,DispatchFunc463,DefaultDispatchFunc,DispatchFunc464,DefaultDispatchFunc,DispatchFunc465,DefaultDispatchFunc,DispatchFunc402,DefaultDispatchFunc,DispatchFunc403,DefaultDispatchFunc,DispatchFunc404,DefaultDispatchFunc,DispatchFunc405,DefaultDispatchFunc,DispatchFunc402 +,DefaultDispatchFunc,DispatchFunc403,DefaultDispatchFunc,DispatchFunc404,DefaultDispatchFunc,DispatchFunc405,DefaultDispatchFunc,DispatchFunc466,DefaultDispatchFunc,DispatchFunc467,DefaultDispatchFunc,DispatchFunc468,DefaultDispatchFunc,DispatchFunc469,DefaultDispatchFunc,DispatchFunc466,DefaultDispatchFunc,DispatchFunc467,DefaultDispatchFunc,DispatchFunc468 +,DefaultDispatchFunc,DispatchFunc469,DefaultDispatchFunc,DispatchFunc406,DefaultDispatchFunc,DispatchFunc407,DefaultDispatchFunc,DispatchFunc408,DefaultDispatchFunc,DispatchFunc409,DefaultDispatchFunc,DispatchFunc406,DefaultDispatchFunc,DispatchFunc407,DefaultDispatchFunc,DispatchFunc408,DefaultDispatchFunc,DispatchFunc409,DefaultDispatchFunc,DispatchFunc470 +,DefaultDispatchFunc,DispatchFunc471,DefaultDispatchFunc,DispatchFunc472,DefaultDispatchFunc,DispatchFunc473,DefaultDispatchFunc,DispatchFunc470,DefaultDispatchFunc,DispatchFunc471,DefaultDispatchFunc,DispatchFunc472,DefaultDispatchFunc,DispatchFunc473,DefaultDispatchFunc,DispatchFunc410,DefaultDispatchFunc,DispatchFunc411,DefaultDispatchFunc,DispatchFunc412 +,DefaultDispatchFunc,DispatchFunc413,DefaultDispatchFunc,DispatchFunc410,DefaultDispatchFunc,DispatchFunc411,DefaultDispatchFunc,DispatchFunc412,DefaultDispatchFunc,DispatchFunc413,DefaultDispatchFunc,DispatchFunc474,DefaultDispatchFunc,DispatchFunc475,DefaultDispatchFunc,DispatchFunc476,DefaultDispatchFunc,DispatchFunc477,DefaultDispatchFunc,DispatchFunc474 +,DefaultDispatchFunc,DispatchFunc475,DefaultDispatchFunc,DispatchFunc476,DefaultDispatchFunc,DispatchFunc477,DefaultDispatchFunc,DispatchFunc414,DefaultDispatchFunc,DispatchFunc415,DefaultDispatchFunc,DispatchFunc416,DefaultDispatchFunc,DispatchFunc417,DefaultDispatchFunc,DispatchFunc414,DefaultDispatchFunc,DispatchFunc415,DefaultDispatchFunc,DispatchFunc416 +,DefaultDispatchFunc,DispatchFunc417,DefaultDispatchFunc,DispatchFunc478,DefaultDispatchFunc,DispatchFunc479,DefaultDispatchFunc,DispatchFunc480,DefaultDispatchFunc,DispatchFunc481,DefaultDispatchFunc,DispatchFunc478,DefaultDispatchFunc,DispatchFunc479,DefaultDispatchFunc,DispatchFunc480,DefaultDispatchFunc,DispatchFunc481,DefaultDispatchFunc,DispatchFunc418 +,DefaultDispatchFunc,DispatchFunc419,DefaultDispatchFunc,DispatchFunc420,DefaultDispatchFunc,DispatchFunc421,DefaultDispatchFunc,DispatchFunc418,DefaultDispatchFunc,DispatchFunc419,DefaultDispatchFunc,DispatchFunc420,DefaultDispatchFunc,DispatchFunc421,DefaultDispatchFunc,DispatchFunc482,DefaultDispatchFunc,DispatchFunc483,DefaultDispatchFunc,DispatchFunc484 +,DefaultDispatchFunc,DispatchFunc485,DefaultDispatchFunc,DispatchFunc482,DefaultDispatchFunc,DispatchFunc483,DefaultDispatchFunc,DispatchFunc484,DefaultDispatchFunc,DispatchFunc485,DefaultDispatchFunc,DispatchFunc422,DefaultDispatchFunc,DispatchFunc423,DefaultDispatchFunc,DispatchFunc424,DefaultDispatchFunc,DispatchFunc425,DefaultDispatchFunc,DispatchFunc422 +,DefaultDispatchFunc,DispatchFunc423,DefaultDispatchFunc,DispatchFunc424,DefaultDispatchFunc,DispatchFunc425,DefaultDispatchFunc,DispatchFunc486,DefaultDispatchFunc,DispatchFunc487,DefaultDispatchFunc,DispatchFunc488,DefaultDispatchFunc,DispatchFunc489,DefaultDispatchFunc,DispatchFunc486,DefaultDispatchFunc,DispatchFunc487,DefaultDispatchFunc,DispatchFunc488 +,DefaultDispatchFunc,DispatchFunc489,DefaultDispatchFunc,DispatchFunc426,DefaultDispatchFunc,DispatchFunc427,DefaultDispatchFunc,DispatchFunc428,DefaultDispatchFunc,DispatchFunc429,DefaultDispatchFunc,DispatchFunc426,DefaultDispatchFunc,DispatchFunc427,DefaultDispatchFunc,DispatchFunc428,DefaultDispatchFunc,DispatchFunc429,DefaultDispatchFunc,DispatchFunc490 +,DefaultDispatchFunc,DispatchFunc491,DefaultDispatchFunc,DispatchFunc492,DefaultDispatchFunc,DispatchFunc493,DefaultDispatchFunc,DispatchFunc490,DefaultDispatchFunc,DispatchFunc491,DefaultDispatchFunc,DispatchFunc492,DefaultDispatchFunc,DispatchFunc493,DefaultDispatchFunc,DispatchFunc430,DefaultDispatchFunc,DispatchFunc431,DefaultDispatchFunc,DispatchFunc432 +,DefaultDispatchFunc,DispatchFunc433,DefaultDispatchFunc,DispatchFunc430,DefaultDispatchFunc,DispatchFunc431,DefaultDispatchFunc,DispatchFunc432,DefaultDispatchFunc,DispatchFunc433,DefaultDispatchFunc,DispatchFunc494,DefaultDispatchFunc,DispatchFunc495,DefaultDispatchFunc,DispatchFunc496,DefaultDispatchFunc,DispatchFunc497,DefaultDispatchFunc,DispatchFunc494 +,DefaultDispatchFunc,DispatchFunc495,DefaultDispatchFunc,DispatchFunc496,DefaultDispatchFunc,DispatchFunc497,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498 +,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc498,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499 +,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DispatchFunc499,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc +,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DefaultDispatchFunc,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500,DispatchFunc500 +}; + } + #endregion Delegate Dispatcher + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/GbaManager.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/GbaManager.cs new file mode 100644 index 0000000000..f7236b5aeb --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/GbaManager.cs @@ -0,0 +1,482 @@ +//#define ARM_DEBUG + +namespace GarboDev +{ + using System; + using System.IO; + using System.Threading; + using System.Timers; + using System.Windows.Forms; + using System.Collections.Generic; + + using BizHawk.Emulation; + using BizHawk; + + public class GbaManager : IEmulator, ISoundProvider, IVideoProvider + { + public const int cpuFreq = 16 * 1024 * 1024; + + private int framesRendered; + + private Arm7Processor arm7 = null; + private Memory memory = null; + private VideoManager videoManager = null; + private SoundManager soundManager = null; + + private bool skipBios = false; + + public delegate void CpuUpdateDelegate(Arm7Processor processor, Memory memory); + private event CpuUpdateDelegate onCpuUpdate = null; + + public Arm7Processor Arm7 + { + get { return this.arm7; } + } + + public VideoManager VideoManager + { + get { return this.videoManager; } + } + + public SoundManager SoundManager + { + get { return this.soundManager; } + } + + public Memory Memory + { + get { return this.memory; } + } + + public Dictionary Breakpoints + { + get { return this.arm7.Breakpoints; } + } + + public ushort KeyState + { + get + { + if (this.memory != null) + { + return this.memory.KeyState; + } + + return 0x3FF; + } + + set + { + this.arm7.KeyState = value; + } + } + + public int FramesRendered + { + get { return this.framesRendered; } + set { this.framesRendered = value; } + } + + public event CpuUpdateDelegate OnCpuUpdate + { + add + { + this.onCpuUpdate += value; + this.onCpuUpdate(this.arm7, this.memory); + } + remove + { + this.onCpuUpdate -= value; + } + } + + public bool SkipBios + { + get { return this.skipBios; } + set { this.skipBios = value; } + } + + public GbaManager(CoreComm comm) + { + _corecomm = comm; + + this.memory = new Memory(); + this.arm7 = new Arm7Processor(this.memory); + this.videoManager = new VideoManager(this); + this.videoManager.Memory = this.memory; + this.soundManager = new SoundManager(this.memory, 44100); + + this.framesRendered = 0; + Renderer renderer = new Renderer(); + renderer.Initialize(null); + VideoManager.Renderer = renderer; + + videoManager.Presenter = delegate(uint[] data) + { + Buffer.BlockCopy(data, 0, this.vbuf, 0, 240 * 160 * 4); + }; + } + + public void Load(byte[] rom, byte[] bios) + { + LoadBios(bios); + LoadRom(rom); + } + + public void Reset() + { + //this.Halt(); + + this.arm7.Reset(this.skipBios); + this.memory.Reset(); + this.videoManager.Reset(); + } + + public void AudioMixerStereo(short[] buffer, int length) + { + // even = left, odd = right + if (this.soundManager.SamplesMixed > Math.Max(500, length)) + { + this.soundManager.GetSamples(buffer, length); + } + } + + public void LoadState(BinaryReader state) + { + } + + public void SaveState(BinaryWriter state) + { + state.Write("GARB"); + } + + public void LoadBios(byte[] biosRom) + { + this.memory.LoadBios(biosRom); + + if (this.onCpuUpdate != null) + { + this.onCpuUpdate(this.arm7, this.memory); + } + } + + public void LoadRom(byte[] cartRom) + { + //this.Halt(); + + /* + byte[] logo = new byte[] + { + 0x24,0xff,0xae,0x51,0x69,0x9a,0xa2,0x21, + 0x3d,0x84,0x82,0x0a,0x84,0xe4,0x09,0xad, + 0x11,0x24,0x8b,0x98,0xc0,0x81,0x7f,0x21, + 0xa3,0x52,0xbe,0x19,0x93,0x09,0xce,0x20, + 0x10,0x46,0x4a,0x4a,0xf8,0x27,0x31,0xec, + 0x58,0xc7,0xe8,0x33,0x82,0xe3,0xce,0xbf, + 0x85,0xf4,0xdf,0x94,0xce,0x4b,0x09,0xc1, + 0x94,0x56,0x8a,0xc0,0x13,0x72,0xa7,0xfc, + 0x9f,0x84,0x4d,0x73,0xa3,0xca,0x9a,0x61, + 0x58,0x97,0xa3,0x27,0xfc,0x03,0x98,0x76, + 0x23,0x1d,0xc7,0x61,0x03,0x04,0xae,0x56, + 0xbf,0x38,0x84,0x00,0x40,0xa7,0x0e,0xfd, + 0xff,0x52,0xfe,0x03,0x6f,0x95,0x30,0xf1, + 0x97,0xfb,0xc0,0x85,0x60,0xd6,0x80,0x25, + 0xa9,0x63,0xbe,0x03,0x01,0x4e,0x38,0xe2, + 0xf9,0xa2,0x34,0xff,0xbb,0x3e,0x03,0x44, + 0x78,0x00,0x90,0xcb,0x88,0x11,0x3a,0x94, + 0x65,0xc0,0x7c,0x63,0x87,0xf0,0x3c,0xaf, + 0xd6,0x25,0xe4,0x8b,0x38,0x0a,0xac,0x72, + 0x21,0xd4,0xf8,0x07 + }; + + Array.Copy(logo, 0, cartRom, 4, logo.Length); + cartRom[0xB2] = 0x96; + cartRom[0xBD] = 0; + for (int i = 0xA0; i <= 0xBC; i++) cartRom[0xBD] = (byte)(cartRom[0xBD] - cartRom[i]); + cartRom[0xBD] = (byte)((cartRom[0xBD] - 0x19) & 0xFF); + */ + this.memory.LoadCartridge(cartRom); + + this.Reset(); + + if (this.onCpuUpdate != null) + { + this.onCpuUpdate(this.arm7, this.memory); + } + } + + public void Step() + { + //this.Halt(); + + this.arm7.Step(); + + if (this.onCpuUpdate != null) + { + this.onCpuUpdate(this.arm7, this.memory); + } + } + + public void StepScanline() + { + //this.Halt(); + + this.arm7.Execute(960); + this.videoManager.RenderLine(); + this.videoManager.EnterHBlank(this.arm7); + this.arm7.Execute(272); + this.videoManager.LeaveHBlank(this.arm7); + + if (this.onCpuUpdate != null) + { + this.onCpuUpdate(this.arm7, this.memory); + } + } + + void UpdateInputState() + { + ushort ret = 0; + if (_controller["Up"]) ret |= 64; + if (_controller["Down"]) ret |= 128; + if (_controller["Left"]) ret |= 32; + if (_controller["Right"]) ret |= 16; + if (_controller["Select"]) ret |= 4; + if (_controller["Start"]) ret |= 8; + if (_controller["B"]) ret |= 2; + if (_controller["A"]) ret |= 1; + if (_controller["L"]) ret |= 512; + if (_controller["R"]) ret |= 256; + ret ^= 0x3ff; + KeyState = ret; + } + + private void StepFrame() + { + UpdateInputState(); + if (_controller["Power"]) + Reset(); + + int vramCycles = 0; + bool inHblank = false; + + //HighPerformanceTimer profileTimer = new HighPerformanceTimer(); + + while (true) + { + + + const int cycleStep = 123; + + + if (vramCycles <= 0) + { + if (inHblank) + { + vramCycles += 960; + bool HitVBlank = this.videoManager.LeaveHBlank(this.arm7); + inHblank = false; + if (HitVBlank) + break; + } + else + { + vramCycles += 272; + this.videoManager.RenderLine(); + this.videoManager.EnterHBlank(this.arm7); + inHblank = true; + } + } + + this.arm7.Execute(cycleStep); +#if ARM_DEBUG + if (this.arm7.BreakpointHit) + { + this.waitingToHalt = true; + Monitor.Wait(this); + } +#endif + vramCycles -= cycleStep; + this.arm7.FireIrq(); + } + + + } + + IVideoProvider IEmulator.VideoProvider + { + get { return this; } + } + + ISoundProvider IEmulator.SoundProvider + { + get { return this; } + } + + ISyncSoundProvider IEmulator.SyncSoundProvider + { + get { return new FakeSyncSound(this, 735); } + } + + bool IEmulator.StartAsyncSound() + { + return false; + } + + void IEmulator.EndAsyncSound() + { + + } + + ControllerDefinition IEmulator.ControllerDefinition + { + get { return BizHawk.Emulation.Consoles.Nintendo.GBA.GBA.GBAController; } + } + + IController _controller; + + IController IEmulator.Controller + { + get { return _controller; } + set { _controller = value; } + } + + void IEmulator.FrameAdvance(bool render, bool rendersound) + { + StepFrame(); + } + + int IEmulator.Frame + { + get { return 0; } + } + + int IEmulator.LagCount + { + get + { + return 0; + } + set + { + + } + } + + bool IEmulator.IsLagFrame + { + get { return false; } + } + + string IEmulator.SystemId + { + get { return "GBA"; } + } + + bool IEmulator.DeterministicEmulation + { + get { return true; } + } + + byte[] IEmulator.ReadSaveRam() + { + return new byte[0]; + } + + void IEmulator.StoreSaveRam(byte[] data) + { + + } + + void IEmulator.ClearSaveRam() + { + + } + + bool IEmulator.SaveRamModified + { + get + { + return false; + } + set + { + + } + } + + void IEmulator.ResetFrameCounter() + { + + } + + void IEmulator.SaveStateText(TextWriter writer) + { + + } + + void IEmulator.LoadStateText(TextReader reader) + { + + } + + void IEmulator.SaveStateBinary(BinaryWriter writer) + { + + } + + void IEmulator.LoadStateBinary(BinaryReader reader) + { + + } + + byte[] IEmulator.SaveStateBinary() + { + return new byte[16]; + } + + CoreComm _corecomm; + + CoreComm IEmulator.CoreComm + { + get { return _corecomm; } + } + + IList IEmulator.MemoryDomains + { + get { return new List(); } + } + + MemoryDomain IEmulator.MainMemory + { + get { return null; } + } + + void IDisposable.Dispose() + { + + } + + void ISoundProvider.GetSamples(short[] samples) + { + AudioMixerStereo(samples, samples.Length); + } + + void ISoundProvider.DiscardSamples() + { + + } + + int ISoundProvider.MaxVolume + { + get; + set; + } + + int[] vbuf = new int[240 * 160]; + int[] IVideoProvider.GetVideoBuffer() { return vbuf; } + int IVideoProvider.VirtualWidth { get { return 240; } } + int IVideoProvider.BufferWidth { get { return 240; } } + int IVideoProvider.BufferHeight { get { return 160; } } + int IVideoProvider.BackgroundColor { get { return unchecked((int)0xff000000); } } + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/IRenderer.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/IRenderer.cs new file mode 100644 index 0000000000..20fce17447 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/IRenderer.cs @@ -0,0 +1,13 @@ +namespace GarboDev +{ + using System; + + public interface IRenderer + { + Memory Memory { set; } + void Initialize(object data); + void Reset(); + void RenderLine(int line); + uint[] ShowFrame(); + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Memory.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Memory.cs new file mode 100644 index 0000000000..23101aff05 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Memory.cs @@ -0,0 +1,1981 @@ +namespace GarboDev +{ + using System; + using System.Collections.Generic; + + public class Memory + { + public const uint REG_BASE = 0x4000000; + public const uint PAL_BASE = 0x5000000; + public const uint VRAM_BASE = 0x6000000; + public const uint OAM_BASE = 0x7000000; + + public const uint DISPCNT = 0x0; + public const uint DISPSTAT = 0x4; + public const uint VCOUNT = 0x6; + + public const uint BG0CNT = 0x8; + public const uint BG1CNT = 0xA; + public const uint BG2CNT = 0xC; + public const uint BG3CNT = 0xE; + + public const uint BG0HOFS = 0x10; + public const uint BG0VOFS = 0x12; + public const uint BG1HOFS = 0x14; + public const uint BG1VOFS = 0x16; + public const uint BG2HOFS = 0x18; + public const uint BG2VOFS = 0x1A; + public const uint BG3HOFS = 0x1C; + public const uint BG3VOFS = 0x1E; + + public const uint BG2PA = 0x20; + public const uint BG2PB = 0x22; + public const uint BG2PC = 0x24; + public const uint BG2PD = 0x26; + public const uint BG2X_L = 0x28; + public const uint BG2X_H = 0x2A; + public const uint BG2Y_L = 0x2C; + public const uint BG2Y_H = 0x2E; + public const uint BG3PA = 0x30; + public const uint BG3PB = 0x32; + public const uint BG3PC = 0x34; + public const uint BG3PD = 0x36; + public const uint BG3X_L = 0x38; + public const uint BG3X_H = 0x3A; + public const uint BG3Y_L = 0x3C; + public const uint BG3Y_H = 0x3E; + + public const uint WIN0H = 0x40; + public const uint WIN1H = 0x42; + public const uint WIN0V = 0x44; + public const uint WIN1V = 0x46; + public const uint WININ = 0x48; + public const uint WINOUT = 0x4A; + + public const uint BLDCNT = 0x50; + public const uint BLDALPHA = 0x52; + public const uint BLDY = 0x54; + + public const uint SOUNDCNT_L = 0x80; + public const uint SOUNDCNT_H = 0x82; + public const uint SOUNDCNT_X = 0x84; + + public const uint FIFO_A_L = 0xA0; + public const uint FIFO_A_H = 0xA2; + public const uint FIFO_B_L = 0xA4; + public const uint FIFO_B_H = 0xA6; + + public const uint DMA0SAD = 0xB0; + public const uint DMA0DAD = 0xB4; + public const uint DMA0CNT_L = 0xB8; + public const uint DMA0CNT_H = 0xBA; + public const uint DMA1SAD = 0xBC; + public const uint DMA1DAD = 0xC0; + public const uint DMA1CNT_L = 0xC4; + public const uint DMA1CNT_H = 0xC6; + public const uint DMA2SAD = 0xC8; + public const uint DMA2DAD = 0xCC; + public const uint DMA2CNT_L = 0xD0; + public const uint DMA2CNT_H = 0xD2; + public const uint DMA3SAD = 0xD4; + public const uint DMA3DAD = 0xD8; + public const uint DMA3CNT_L = 0xDC; + public const uint DMA3CNT_H = 0xDE; + + public const uint TM0D = 0x100; + public const uint TM0CNT = 0x102; + public const uint TM1D = 0x104; + public const uint TM1CNT = 0x106; + public const uint TM2D = 0x108; + public const uint TM2CNT = 0x10A; + public const uint TM3D = 0x10C; + public const uint TM3CNT = 0x10E; + + public const uint KEYINPUT = 0x130; + public const uint KEYCNT = 0x132; + public const uint IE = 0x200; + public const uint IF = 0x202; + public const uint IME = 0x208; + + public const uint HALTCNT = 0x300; + + private const uint biosRamMask = 0x3FFF; + private const uint ewRamMask = 0x3FFFF; + private const uint iwRamMask = 0x7FFF; + private const uint ioRegMask = 0x4FF; + private const uint vRamMask = 0x1FFFF; + private const uint palRamMask = 0x3FF; + private const uint oamRamMask = 0x3FF; + private const uint sRamMask = 0xFFFF; + + private byte[] biosRam = new byte[Memory.biosRamMask + 1]; + private byte[] ewRam = new byte[Memory.ewRamMask + 1]; + private byte[] iwRam = new byte[Memory.iwRamMask + 1]; + private byte[] ioReg = new byte[Memory.ioRegMask + 1]; + private byte[] vRam = new byte[Memory.vRamMask + 1]; + private byte[] palRam = new byte[Memory.palRamMask + 1]; + private byte[] oamRam = new byte[Memory.oamRamMask + 1]; + private byte[] sRam = new byte[Memory.sRamMask + 1]; + + public byte[] VideoRam + { + get + { + return this.vRam; + } + } + + public byte[] PaletteRam + { + get + { + return this.palRam; + } + } + + public byte[] OamRam + { + get + { + return this.oamRam; + } + } + + public byte[] IORam + { + get + { + return this.ioReg; + } + } + + private ushort keyState = 0x3FF; + + public ushort KeyState + { + get { return this.keyState; } + set { this.keyState = value; } + } + + private Arm7Processor processor = null; + public Arm7Processor Processor + { + get { return this.processor; } + set { this.processor = value; } + } + + private SoundManager soundManager = null; + public SoundManager SoundManager + { + get { return this.soundManager; } + set { this.soundManager = value; } + } + + private byte[] romBank1 = null; + private byte[] romBank2 = null; + private uint romBank1Mask = 0; + private uint romBank2Mask = 0; + + private int[] bankSTimes = new int[0x10]; + private int[] bankNTimes = new int[0x10]; + + private int waitCycles = 0; + + public int WaitCycles + { + get { int tmp = this.waitCycles; this.waitCycles = 0; return tmp; } + } + + private bool inUnreadable = false; + + private delegate byte ReadU8Delegate(uint address); + private delegate void WriteU8Delegate(uint address, byte value); + private delegate ushort ReadU16Delegate(uint address); + private delegate void WriteU16Delegate(uint address, ushort value); + private delegate uint ReadU32Delegate(uint address); + private delegate void WriteU32Delegate(uint address, uint value); + + private ReadU8Delegate[] ReadU8Funcs = null; + private WriteU8Delegate[] WriteU8Funcs = null; + private ReadU16Delegate[] ReadU16Funcs = null; + private WriteU16Delegate[] WriteU16Funcs = null; + private ReadU32Delegate[] ReadU32Funcs = null; + private WriteU32Delegate[] WriteU32Funcs = null; + + private uint[,] dmaRegs = new uint[4, 4]; + private uint[] timerCnt = new uint[4]; + private int[] bgx = new int[2], bgy = new int[2]; + + public uint[] TimerCnt + { + get { return this.timerCnt; } + } + + public int[] Bgx + { + get { return this.bgx; } + } + + public int[] Bgy + { + get { return this.bgy; } + } + + public Memory() + { + this.ReadU8Funcs = new ReadU8Delegate[] + { + this.ReadBiosRam8, + this.ReadNop8, + this.ReadEwRam8, + this.ReadIwRam8, + this.ReadIO8, + this.ReadPalRam8, + this.ReadVRam8, + this.ReadOamRam8, + this.ReadNop8, + this.ReadNop8, + this.ReadNop8, + this.ReadNop8, + this.ReadNop8, + this.ReadNop8, + this.ReadSRam8, + this.ReadNop8 + }; + + this.WriteU8Funcs = new WriteU8Delegate[] + { + this.WriteNop8, + this.WriteNop8, + this.WriteEwRam8, + this.WriteIwRam8, + this.WriteIO8, + this.WritePalRam8, + this.WriteVRam8, + this.WriteOamRam8, + this.WriteNop8, + this.WriteNop8, + this.WriteNop8, + this.WriteNop8, + this.WriteNop8, + this.WriteNop8, + this.WriteSRam8, + this.WriteNop8 + }; + + this.ReadU16Funcs = new ReadU16Delegate[] + { + this.ReadBiosRam16, + this.ReadNop16, + this.ReadEwRam16, + this.ReadIwRam16, + this.ReadIO16, + this.ReadPalRam16, + this.ReadVRam16, + this.ReadOamRam16, + this.ReadNop16, + this.ReadNop16, + this.ReadNop16, + this.ReadNop16, + this.ReadNop16, + this.ReadNop16, + this.ReadSRam16, + this.ReadNop16 + }; + + this.WriteU16Funcs = new WriteU16Delegate[] + { + this.WriteNop16, + this.WriteNop16, + this.WriteEwRam16, + this.WriteIwRam16, + this.WriteIO16, + this.WritePalRam16, + this.WriteVRam16, + this.WriteOamRam16, + this.WriteNop16, + this.WriteNop16, + this.WriteNop16, + this.WriteNop16, + this.WriteNop16, + this.WriteNop16, + this.WriteSRam16, + this.WriteNop16 + }; + + this.ReadU32Funcs = new ReadU32Delegate[] + { + this.ReadBiosRam32, + this.ReadNop32, + this.ReadEwRam32, + this.ReadIwRam32, + this.ReadIO32, + this.ReadPalRam32, + this.ReadVRam32, + this.ReadOamRam32, + this.ReadNop32, + this.ReadNop32, + this.ReadNop32, + this.ReadNop32, + this.ReadNop32, + this.ReadNop32, + this.ReadSRam32, + this.ReadNop32 + }; + + this.WriteU32Funcs = new WriteU32Delegate[] + { + this.WriteNop32, + this.WriteNop32, + this.WriteEwRam32, + this.WriteIwRam32, + this.WriteIO32, + this.WritePalRam32, + this.WriteVRam32, + this.WriteOamRam32, + this.WriteNop32, + this.WriteNop32, + this.WriteNop32, + this.WriteNop32, + this.WriteNop32, + this.WriteNop32, + this.WriteSRam32, + this.WriteNop32 + }; + } + + public void Reset() + { + Array.Clear(this.ewRam, 0, this.ewRam.Length); + Array.Clear(this.iwRam, 0, this.iwRam.Length); + Array.Clear(this.ioReg, 0, this.ioReg.Length); + Array.Clear(this.vRam, 0, this.vRam.Length); + Array.Clear(this.palRam, 0, this.palRam.Length); + Array.Clear(this.oamRam, 0, this.oamRam.Length); + Array.Clear(this.sRam, 0, this.sRam.Length); + + Memory.WriteU16(this.ioReg, Memory.BG2PA, 0x0100); + Memory.WriteU16(this.ioReg, Memory.BG2PD, 0x0100); + Memory.WriteU16(this.ioReg, Memory.BG3PA, 0x0100); + Memory.WriteU16(this.ioReg, Memory.BG3PD, 0x0100); + } + + public void HBlankDma() + { + for (int i = 0; i < 4; i++) + { + if (((this.dmaRegs[i, 3] >> 12) & 0x3) == 2) + { + this.DmaTransfer(i); + } + } + } + + public void VBlankDma() + { + for (int i = 0; i < 4; i++) + { + if (((this.dmaRegs[i, 3] >> 12) & 0x3) == 1) + { + this.DmaTransfer(i); + } + } + } + + public void FifoDma(int channel) + { + if (((this.dmaRegs[channel, 3] >> 12) & 0x3) == 0x3) + { + this.DmaTransfer(channel); + } + } + + public void DmaTransfer(int channel) + { + // Check if DMA is enabled + if ((this.dmaRegs[channel, 3] & (1 << 15)) != 0) + { + bool wideTransfer = (this.dmaRegs[channel, 3] & (1 << 10)) != 0; + + uint srcDirection = 0, destDirection = 0; + bool reload = false; + + switch ((this.dmaRegs[channel, 3] >> 5) & 0x3) + { + case 0: destDirection = 1; break; + case 1: destDirection = 0xFFFFFFFF; break; + case 2: destDirection = 0; break; + case 3: destDirection = 1; reload = true; break; + } + + switch ((this.dmaRegs[channel, 3] >> 7) & 0x3) + { + case 0: srcDirection = 1; break; + case 1: srcDirection = 0xFFFFFFFF; break; + case 2: srcDirection = 0; break; + case 3: if (channel == 3) + { + // TODO + return; + } + throw new Exception("Unhandled DMA mode."); + } + + int numElements = (int)this.dmaRegs[channel, 2]; + if (numElements == 0) numElements = 0x4000; + + if (((this.dmaRegs[channel, 3] >> 12) & 0x3) == 0x3) + { + // Sound FIFO mode + wideTransfer = true; + destDirection = 0; + numElements = 4; + reload = false; + } + + if (wideTransfer) + { + srcDirection *= 4; + destDirection *= 4; + while (numElements-- > 0) + { + this.WriteU32(this.dmaRegs[channel, 1], this.ReadU32(this.dmaRegs[channel, 0])); + this.dmaRegs[channel, 1] += destDirection; + this.dmaRegs[channel, 0] += srcDirection; + } + } + else + { + srcDirection *= 2; + destDirection *= 2; + while (numElements-- > 0) + { + this.WriteU16(this.dmaRegs[channel, 1], this.ReadU16(this.dmaRegs[channel, 0])); + this.dmaRegs[channel, 1] += destDirection; + this.dmaRegs[channel, 0] += srcDirection; + } + } + + // If not a repeating DMA, then disable the DMA + if ((this.dmaRegs[channel, 3] & (1 << 9)) == 0) + { + this.dmaRegs[channel, 3] &= 0x7FFF; + } + else + { + // Reload dest and count + switch (channel) + { + case 0: + if (reload) this.dmaRegs[0, 1] = Memory.ReadU32(this.ioReg, Memory.DMA0DAD) & 0x07FFFFFF; + this.dmaRegs[0, 2] = Memory.ReadU16(this.ioReg, Memory.DMA0CNT_L); + break; + case 1: + if (reload) this.dmaRegs[1, 1] = Memory.ReadU32(this.ioReg, Memory.DMA1DAD) & 0x07FFFFFF; + this.dmaRegs[1, 2] = Memory.ReadU16(this.ioReg, Memory.DMA1CNT_L); + break; + case 2: + if (reload) this.dmaRegs[2, 1] = Memory.ReadU32(this.ioReg, Memory.DMA2DAD) & 0x07FFFFFF; + this.dmaRegs[2, 2] = Memory.ReadU16(this.ioReg, Memory.DMA2CNT_L); + break; + case 3: + if (reload) this.dmaRegs[3, 1] = Memory.ReadU32(this.ioReg, Memory.DMA3DAD) & 0x0FFFFFFF; + this.dmaRegs[3, 2] = Memory.ReadU16(this.ioReg, Memory.DMA3CNT_L); + break; + } + } + + if ((this.dmaRegs[channel, 3] & (1 << 14)) != 0) + { + this.processor.RequestIrq(8 + channel); + } + } + } + + public void WriteDmaControl(int channel) + { + switch (channel) + { + case 0: + if (((this.dmaRegs[0, 3] ^ Memory.ReadU16(this.ioReg, Memory.DMA0CNT_H)) & (1 << 15)) == 0) return; + this.dmaRegs[0, 0] = Memory.ReadU32(this.ioReg, Memory.DMA0SAD) & 0x07FFFFFF; + this.dmaRegs[0, 1] = Memory.ReadU32(this.ioReg, Memory.DMA0DAD) & 0x07FFFFFF; + this.dmaRegs[0, 2] = Memory.ReadU16(this.ioReg, Memory.DMA0CNT_L); + this.dmaRegs[0, 3] = Memory.ReadU16(this.ioReg, Memory.DMA0CNT_H); + break; + case 1: + if (((this.dmaRegs[1, 3] ^ Memory.ReadU16(this.ioReg, Memory.DMA1CNT_H)) & (1 << 15)) == 0) return; + this.dmaRegs[1, 0] = Memory.ReadU32(this.ioReg, Memory.DMA1SAD) & 0x0FFFFFFF; + this.dmaRegs[1, 1] = Memory.ReadU32(this.ioReg, Memory.DMA1DAD) & 0x07FFFFFF; + this.dmaRegs[1, 2] = Memory.ReadU16(this.ioReg, Memory.DMA1CNT_L); + this.dmaRegs[1, 3] = Memory.ReadU16(this.ioReg, Memory.DMA1CNT_H); + break; + case 2: + if (((this.dmaRegs[2, 3] ^ Memory.ReadU16(this.ioReg, Memory.DMA2CNT_H)) & (1 << 15)) == 0) return; + this.dmaRegs[2, 0] = Memory.ReadU32(this.ioReg, Memory.DMA2SAD) & 0x0FFFFFFF; + this.dmaRegs[2, 1] = Memory.ReadU32(this.ioReg, Memory.DMA2DAD) & 0x07FFFFFF; + this.dmaRegs[2, 2] = Memory.ReadU16(this.ioReg, Memory.DMA2CNT_L); + this.dmaRegs[2, 3] = Memory.ReadU16(this.ioReg, Memory.DMA2CNT_H); + break; + case 3: + if (((this.dmaRegs[3, 3] ^ Memory.ReadU16(this.ioReg, Memory.DMA3CNT_H)) & (1 << 15)) == 0) return; + this.dmaRegs[3, 0] = Memory.ReadU32(this.ioReg, Memory.DMA3SAD) & 0x0FFFFFFF; + this.dmaRegs[3, 1] = Memory.ReadU32(this.ioReg, Memory.DMA3DAD) & 0x0FFFFFFF; + this.dmaRegs[3, 2] = Memory.ReadU16(this.ioReg, Memory.DMA3CNT_L); + this.dmaRegs[3, 3] = Memory.ReadU16(this.ioReg, Memory.DMA3CNT_H); + break; + } + + // Channel start timing + switch ((this.dmaRegs[channel, 3] >> 12) & 0x3) + { + case 0: + // Start immediately + this.DmaTransfer(channel); + break; + case 1: + case 2: + // Hblank and Vblank DMA's + break; + case 3: + // TODO (DMA sound) + return; + } + } + + private void WriteTimerControl(int timer, ushort newCnt) + { + ushort control = Memory.ReadU16(this.ioReg, Memory.TM0CNT + (uint)(timer * 4)); + uint count = Memory.ReadU16(this.ioReg, Memory.TM0D + (uint)(timer * 4)); + + if ((newCnt & (1 << 7)) != 0 && (control & (1 << 7)) == 0) + { + this.timerCnt[timer] = count << 10; + } + } + + #region Read/Write Helpers + public static ushort ReadU16(byte[] array, uint position) + { + return (ushort)(array[position] | (array[position + 1] << 8)); + } + + public static uint ReadU32(byte[] array, uint position) + { + return (uint)(array[position] | (array[position + 1] << 8) | + (array[position + 2] << 16) | (array[position + 3] << 24)); + } + + public static void WriteU16(byte[] array, uint position, ushort value) + { + array[position] = (byte)(value & 0xff); + array[position + 1] = (byte)(value >> 8); + } + + public static void WriteU32(byte[] array, uint position, uint value) + { + array[position] = (byte)(value & 0xff); + array[position + 1] = (byte)((value >> 8) & 0xff); + array[position + 2] = (byte)((value >> 16) & 0xff); + array[position + 3] = (byte)(value >> 24); + } + #endregion + + #region Memory Reads + private uint ReadUnreadable() + { + if (this.inUnreadable) + { + return 0; + } + + this.inUnreadable = true; + + uint res; + + if (this.processor.ArmState) + { + res = this.ReadU32(this.processor.Registers[15]); + } + else + { + ushort val = this.ReadU16(this.processor.Registers[15]); + res = (uint)(val | (val << 16)); + } + + this.inUnreadable = false; + + return res; + } + + private byte ReadNop8(uint address) + { + return (byte)(this.ReadUnreadable() & 0xFF); + } + + private ushort ReadNop16(uint address) + { + return (ushort)(this.ReadUnreadable() & 0xFFFF); + } + + private uint ReadNop32(uint address) + { + return this.ReadUnreadable(); + } + + private byte ReadBiosRam8(uint address) + { + this.waitCycles++; + if (this.processor.Registers[15] < 0x01000000) + { + return this.biosRam[address & Memory.biosRamMask]; + } + return (byte)(this.ReadUnreadable() & 0xFF); + } + + private ushort ReadBiosRam16(uint address) + { + this.waitCycles++; + if (this.processor.Registers[15] < 0x01000000) + { + return Memory.ReadU16(this.biosRam, address & Memory.biosRamMask); + } + return (ushort)(this.ReadUnreadable() & 0xFFFF); + } + + private uint ReadBiosRam32(uint address) + { + this.waitCycles++; + if (this.processor.Registers[15] < 0x01000000) + { + return Memory.ReadU32(this.biosRam, address & Memory.biosRamMask); + } + return this.ReadUnreadable(); + } + + private byte ReadEwRam8(uint address) + { + this.waitCycles += 3; + return this.ewRam[address & Memory.ewRamMask]; + } + + private ushort ReadEwRam16(uint address) + { + this.waitCycles += 3; + return Memory.ReadU16(this.ewRam, address & Memory.ewRamMask); + } + + private uint ReadEwRam32(uint address) + { + this.waitCycles += 6; + return Memory.ReadU32(this.ewRam, address & Memory.ewRamMask); + } + + private byte ReadIwRam8(uint address) + { + this.waitCycles++; + return this.iwRam[address & Memory.iwRamMask]; + } + + private ushort ReadIwRam16(uint address) + { + this.waitCycles++; + return Memory.ReadU16(this.iwRam, address & Memory.iwRamMask); + } + + private uint ReadIwRam32(uint address) + { + this.waitCycles++; + return Memory.ReadU32(this.iwRam, address & Memory.iwRamMask); + } + + private byte ReadIO8(uint address) + { + this.waitCycles++; + address &= 0xFFFFFF; + if (address >= Memory.ioRegMask) return 0; + + switch (address) + { + case KEYINPUT: + return (byte)(this.keyState & 0xFF); + case KEYINPUT + 1: + return (byte)(this.keyState >> 8); + + case DMA0CNT_H: + return (byte)(this.dmaRegs[0, 3] & 0xFF); + case DMA0CNT_H + 1: + return (byte)(this.dmaRegs[0, 3] >> 8); + case DMA1CNT_H: + return (byte)(this.dmaRegs[1, 3] & 0xFF); + case DMA1CNT_H + 1: + return (byte)(this.dmaRegs[1, 3] >> 8); + case DMA2CNT_H: + return (byte)(this.dmaRegs[2, 3] & 0xFF); + case DMA2CNT_H + 1: + return (byte)(this.dmaRegs[2, 3] >> 8); + case DMA3CNT_H: + return (byte)(this.dmaRegs[3, 3] & 0xFF); + case DMA3CNT_H + 1: + return (byte)(this.dmaRegs[3, 3] >> 8); + + case TM0D: + this.processor.UpdateTimers(); + return (byte)((this.timerCnt[0] >> 10) & 0xFF); + case TM0D + 1: + this.processor.UpdateTimers(); + return (byte)((this.timerCnt[0] >> 10) >> 8); + case TM1D: + this.processor.UpdateTimers(); + return (byte)((this.timerCnt[1] >> 10) & 0xFF); + case TM1D + 1: + this.processor.UpdateTimers(); + return (byte)((this.timerCnt[1] >> 10) >> 8); + case TM2D: + this.processor.UpdateTimers(); + return (byte)((this.timerCnt[2] >> 10) & 0xFF); + case TM2D + 1: + this.processor.UpdateTimers(); + return (byte)((this.timerCnt[2] >> 10) >> 8); + case TM3D: + this.processor.UpdateTimers(); + return (byte)((this.timerCnt[3] >> 10) & 0xFF); + case TM3D + 1: + this.processor.UpdateTimers(); + return (byte)((this.timerCnt[3] >> 10) >> 8); + + default: + return this.ioReg[address]; + } + } + + private ushort ReadIO16(uint address) + { + this.waitCycles++; + address &= 0xFFFFFF; + if (address >= Memory.ioRegMask) return 0; + + switch (address) + { + case KEYINPUT: + return this.keyState; + + case DMA0CNT_H: + return (ushort)this.dmaRegs[0, 3]; + case DMA1CNT_H: + return (ushort)this.dmaRegs[1, 3]; + case DMA2CNT_H: + return (ushort)this.dmaRegs[2, 3]; + case DMA3CNT_H: + return (ushort)this.dmaRegs[3, 3]; + + case TM0D: + this.processor.UpdateTimers(); + return (ushort)((this.timerCnt[0] >> 10) & 0xFFFF); + case TM1D: + this.processor.UpdateTimers(); + return (ushort)((this.timerCnt[1] >> 10) & 0xFFFF); + case TM2D: + this.processor.UpdateTimers(); + return (ushort)((this.timerCnt[2] >> 10) & 0xFFFF); + case TM3D: + this.processor.UpdateTimers(); + return (ushort)((this.timerCnt[3] >> 10) & 0xFFFF); + + default: + return Memory.ReadU16(this.ioReg, address); + } + } + + private uint ReadIO32(uint address) + { + this.waitCycles++; + address &= 0xFFFFFF; + if (address >= Memory.ioRegMask) return 0; + + switch (address) + { + case KEYINPUT: + return this.keyState | ((uint)Memory.ReadU16(this.ioReg, address + 0x2) << 16); + + case DMA0CNT_L: + return (uint)Memory.ReadU16(this.ioReg, address) | (this.dmaRegs[0, 3] << 16); + case DMA1CNT_L: + return (uint)Memory.ReadU16(this.ioReg, address) | (this.dmaRegs[1, 3] << 16); + case DMA2CNT_L: + return (uint)Memory.ReadU16(this.ioReg, address) | (this.dmaRegs[2, 3] << 16); + case DMA3CNT_L: + return (uint)Memory.ReadU16(this.ioReg, address) | (this.dmaRegs[3, 3] << 16); + + case TM0D: + this.processor.UpdateTimers(); + return (uint)(((this.timerCnt[0] >> 10) & 0xFFFF) | (uint)(Memory.ReadU16(this.ioReg, address + 2) << 16)); + case TM1D: + this.processor.UpdateTimers(); + return (uint)(((this.timerCnt[1] >> 10) & 0xFFFF) | (uint)(Memory.ReadU16(this.ioReg, address + 2) << 16)); + case TM2D: + this.processor.UpdateTimers(); + return (uint)(((this.timerCnt[2] >> 10) & 0xFFFF) | (uint)(Memory.ReadU16(this.ioReg, address + 2) << 16)); + case TM3D: + this.processor.UpdateTimers(); + return (uint)(((this.timerCnt[3] >> 10) & 0xFFFF) | (uint)(Memory.ReadU16(this.ioReg, address + 2) << 16)); + + default: + return Memory.ReadU32(this.ioReg, address); + } + } + + private byte ReadPalRam8(uint address) + { + this.waitCycles++; + return this.palRam[address & Memory.palRamMask]; + } + + private ushort ReadPalRam16(uint address) + { + this.waitCycles++; + return Memory.ReadU16(this.palRam, address & Memory.palRamMask); + } + + private uint ReadPalRam32(uint address) + { + this.waitCycles += 2; + return Memory.ReadU32(this.palRam, address & Memory.palRamMask); + } + + private byte ReadVRam8(uint address) + { + this.waitCycles++; + address &= Memory.vRamMask; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + return this.vRam[address]; + } + + private ushort ReadVRam16(uint address) + { + this.waitCycles++; + address &= Memory.vRamMask; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + return Memory.ReadU16(this.vRam, address); + } + + private uint ReadVRam32(uint address) + { + this.waitCycles += 2; + address &= Memory.vRamMask; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + return Memory.ReadU32(this.vRam, address & Memory.vRamMask); + } + + private byte ReadOamRam8(uint address) + { + this.waitCycles++; + return this.oamRam[address & Memory.oamRamMask]; + } + + private ushort ReadOamRam16(uint address) + { + this.waitCycles++; + return Memory.ReadU16(this.oamRam, address & Memory.oamRamMask); + } + + private uint ReadOamRam32(uint address) + { + this.waitCycles++; + return Memory.ReadU32(this.oamRam, address & Memory.oamRamMask); + } + + private byte ReadRom1_8(uint address) + { + this.waitCycles += this.bankSTimes[(address >> 24) & 0xf]; + return this.romBank1[address & this.romBank1Mask]; + } + + private ushort ReadRom1_16(uint address) + { + this.waitCycles += this.bankSTimes[(address >> 24) & 0xf]; + return Memory.ReadU16(this.romBank1, address & this.romBank1Mask); + } + + private uint ReadRom1_32(uint address) + { + this.waitCycles += this.bankSTimes[(address >> 24) & 0xf] * 2 + 1; + return Memory.ReadU32(this.romBank1, address & this.romBank1Mask); + } + + private byte ReadRom2_8(uint address) + { + this.waitCycles += this.bankSTimes[(address >> 24) & 0xf]; + return this.romBank2[address & this.romBank2Mask]; + } + + private ushort ReadRom2_16(uint address) + { + this.waitCycles += this.bankSTimes[(address >> 24) & 0xf]; + return Memory.ReadU16(this.romBank2, address & this.romBank2Mask); + } + + private uint ReadRom2_32(uint address) + { + this.waitCycles += this.bankSTimes[(address >> 24) & 0xf] * 2 + 1; + return Memory.ReadU32(this.romBank2, address & this.romBank2Mask); + } + + private byte ReadSRam8(uint address) + { + return this.sRam[address & Memory.sRamMask]; + } + + private ushort ReadSRam16(uint address) + { + // TODO + return 0; + } + + private uint ReadSRam32(uint address) + { + // TODO + return 0; + } + #endregion + + #region Memory Writes + private void WriteNop8(uint address, byte value) + { + } + + private void WriteNop16(uint address, ushort value) + { + } + + private void WriteNop32(uint address, uint value) + { + } + + private void WriteEwRam8(uint address, byte value) + { + this.waitCycles += 3; + this.ewRam[address & Memory.ewRamMask] = value; + } + + private void WriteEwRam16(uint address, ushort value) + { + this.waitCycles += 3; + Memory.WriteU16(this.ewRam, address & Memory.ewRamMask, value); + } + + private void WriteEwRam32(uint address, uint value) + { + this.waitCycles += 6; + Memory.WriteU32(this.ewRam, address & Memory.ewRamMask, value); + } + + private void WriteIwRam8(uint address, byte value) + { + this.waitCycles++; + this.iwRam[address & Memory.iwRamMask] = value; + } + + private void WriteIwRam16(uint address, ushort value) + { + this.waitCycles++; + Memory.WriteU16(this.iwRam, address & Memory.iwRamMask, value); + } + + private void WriteIwRam32(uint address, uint value) + { + this.waitCycles++; + Memory.WriteU32(this.iwRam, address & Memory.iwRamMask, value); + } + + private void WriteIO8(uint address, byte value) + { + this.waitCycles++; + address &= 0xFFFFFF; + if (address >= Memory.ioRegMask) return; + + switch (address) + { + case BG2X_L: + case BG2X_L + 1: + case BG2X_L + 2: + case BG2X_L + 3: + { + this.ioReg[address] = value; + uint tmp = Memory.ReadU32(this.ioReg, BG2X_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG2X_L, tmp); + + this.bgx[0] = (int)tmp; + } + break; + + case BG3X_L: + case BG3X_L + 1: + case BG3X_L + 2: + case BG3X_L + 3: + { + this.ioReg[address] = value; + uint tmp = Memory.ReadU32(this.ioReg, BG3X_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG3X_L, tmp); + + this.bgx[1] = (int)tmp; + } + break; + + case BG2Y_L: + case BG2Y_L + 1: + case BG2Y_L + 2: + case BG2Y_L + 3: + { + this.ioReg[address] = value; + uint tmp = Memory.ReadU32(this.ioReg, BG2Y_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG2Y_L, tmp); + + this.bgy[0] = (int)tmp; + } + break; + + case BG3Y_L: + case BG3Y_L + 1: + case BG3Y_L + 2: + case BG3Y_L + 3: + { + this.ioReg[address] = value; + uint tmp = Memory.ReadU32(this.ioReg, BG3Y_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG3Y_L, tmp); + + this.bgy[1] = (int)tmp; + } + break; + + case DMA0CNT_H: + case DMA0CNT_H + 1: + this.ioReg[address] = value; + this.WriteDmaControl(0); + break; + + case DMA1CNT_H: + case DMA1CNT_H + 1: + this.ioReg[address] = value; + this.WriteDmaControl(1); + break; + + case DMA2CNT_H: + case DMA2CNT_H + 1: + this.ioReg[address] = value; + this.WriteDmaControl(2); + break; + + case DMA3CNT_H: + case DMA3CNT_H + 1: + this.ioReg[address] = value; + this.WriteDmaControl(3); + break; + + case TM0CNT: + case TM0CNT + 1: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM0CNT); + this.ioReg[address] = value; + this.WriteTimerControl(0, oldCnt); + } + break; + + case TM1CNT: + case TM1CNT + 1: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM1CNT); + this.ioReg[address] = value; + this.WriteTimerControl(1, oldCnt); + } + break; + + case TM2CNT: + case TM2CNT + 1: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM2CNT); + this.ioReg[address] = value; + this.WriteTimerControl(2, oldCnt); + } + break; + + case TM3CNT: + case TM3CNT + 1: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM3CNT); + this.ioReg[address] = value; + this.WriteTimerControl(3, oldCnt); + } + break; + + case FIFO_A_L: + case FIFO_A_L+1: + case FIFO_A_H: + case FIFO_A_H+1: + this.ioReg[address] = value; + this.soundManager.IncrementFifoA(); + break; + + case FIFO_B_L: + case FIFO_B_L + 1: + case FIFO_B_H: + case FIFO_B_H + 1: + this.ioReg[address] = value; + this.soundManager.IncrementFifoB(); + break; + + case IF: + case IF + 1: + this.ioReg[address] &= (byte)~value; + break; + + case HALTCNT + 1: + this.ioReg[address] = value; + this.processor.Halt(); + break; + + default: + this.ioReg[address] = value; + break; + } + } + + private void WriteIO16(uint address, ushort value) + { + this.waitCycles++; + address &= 0xFFFFFF; + if (address >= Memory.ioRegMask) return; + + switch (address) + { + case BG2X_L: + case BG2X_L + 2: + { + Memory.WriteU16(this.ioReg, address, value); + uint tmp = Memory.ReadU32(this.ioReg, BG2X_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG2X_L, tmp); + + this.bgx[0] = (int)tmp; + } + break; + + case BG3X_L: + case BG3X_L + 2: + { + Memory.WriteU16(this.ioReg, address, value); + uint tmp = Memory.ReadU32(this.ioReg, BG3X_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG3X_L, tmp); + + this.bgx[1] = (int)tmp; + } + break; + + case BG2Y_L: + case BG2Y_L + 2: + { + Memory.WriteU16(this.ioReg, address, value); + uint tmp = Memory.ReadU32(this.ioReg, BG2Y_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG2Y_L, tmp); + + this.bgy[0] = (int)tmp; + } + break; + + case BG3Y_L: + case BG3Y_L + 2: + { + Memory.WriteU16(this.ioReg, address, value); + uint tmp = Memory.ReadU32(this.ioReg, BG3Y_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG3Y_L, tmp); + + this.bgy[1] = (int)tmp; + } + break; + + case DMA0CNT_H: + Memory.WriteU16(this.ioReg, address, value); + this.WriteDmaControl(0); + break; + + case DMA1CNT_H: + Memory.WriteU16(this.ioReg, address, value); + this.WriteDmaControl(1); + break; + + case DMA2CNT_H: + Memory.WriteU16(this.ioReg, address, value); + this.WriteDmaControl(2); + break; + + case DMA3CNT_H: + Memory.WriteU16(this.ioReg, address, value); + this.WriteDmaControl(3); + break; + + case TM0CNT: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM0CNT); + Memory.WriteU16(this.ioReg, address, value); + this.WriteTimerControl(0, oldCnt); + } + break; + + case TM1CNT: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM1CNT); + Memory.WriteU16(this.ioReg, address, value); + this.WriteTimerControl(1, oldCnt); + } + break; + + case TM2CNT: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM2CNT); + Memory.WriteU16(this.ioReg, address, value); + this.WriteTimerControl(2, oldCnt); + } + break; + + case TM3CNT: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM3CNT); + Memory.WriteU16(this.ioReg, address, value); + this.WriteTimerControl(3, oldCnt); + } + break; + + case FIFO_A_L: + case FIFO_A_H: + Memory.WriteU16(this.ioReg, address, value); + this.soundManager.IncrementFifoA(); + break; + + case FIFO_B_L: + case FIFO_B_H: + Memory.WriteU16(this.ioReg, address, value); + this.soundManager.IncrementFifoB(); + break; + + case SOUNDCNT_H: + Memory.WriteU16(this.ioReg, address, value); + if ((value & (1 << 11)) != 0) + { + this.soundManager.ResetFifoA(); + } + if ((value & (1 << 15)) != 0) + { + this.soundManager.ResetFifoB(); + } + break; + + case IF: + { + ushort tmp = Memory.ReadU16(this.ioReg, address); + Memory.WriteU16(this.ioReg, address, (ushort)(tmp & (~value))); + } + break; + + case HALTCNT: + Memory.WriteU16(this.ioReg, address, value); + this.processor.Halt(); + break; + + default: + Memory.WriteU16(this.ioReg, address, value); + break; + } + } + + private void WriteIO32(uint address, uint value) + { + this.waitCycles++; + address &= 0xFFFFFF; + if (address >= Memory.ioRegMask) return; + + switch (address) + { + case BG2X_L: + { + Memory.WriteU32(this.ioReg, address, value); + uint tmp = Memory.ReadU32(this.ioReg, BG2X_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG2X_L, tmp); + + this.bgx[0] = (int)tmp; + } + break; + + case BG3X_L: + { + Memory.WriteU32(this.ioReg, address, value); + uint tmp = Memory.ReadU32(this.ioReg, BG3X_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG3X_L, tmp); + + this.bgx[1] = (int)tmp; + } + break; + + case BG2Y_L: + { + Memory.WriteU32(this.ioReg, address, value); + uint tmp = Memory.ReadU32(this.ioReg, BG2Y_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG2Y_L, tmp); + + this.bgy[0] = (int)tmp; + } + break; + + case BG3Y_L: + { + Memory.WriteU32(this.ioReg, address, value); + uint tmp = Memory.ReadU32(this.ioReg, BG3Y_L); + if ((tmp & (1 << 27)) != 0) tmp |= 0xF0000000; + Memory.WriteU32(this.ioReg, BG3Y_L, tmp); + + this.bgy[1] = (int)tmp; + } + break; + + case DMA0CNT_L: + Memory.WriteU32(this.ioReg, address, value); + this.WriteDmaControl(0); + break; + + case DMA1CNT_L: + Memory.WriteU32(this.ioReg, address, value); + this.WriteDmaControl(1); + break; + + case DMA2CNT_L: + Memory.WriteU32(this.ioReg, address, value); + this.WriteDmaControl(2); + break; + + case DMA3CNT_L: + Memory.WriteU32(this.ioReg, address, value); + this.WriteDmaControl(3); + break; + + case TM0D: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM0CNT); + Memory.WriteU32(this.ioReg, address, value); + this.WriteTimerControl(0, oldCnt); + } + break; + + case TM1D: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM1CNT); + Memory.WriteU32(this.ioReg, address, value); + this.WriteTimerControl(1, oldCnt); + } + break; + + case TM2D: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM2CNT); + Memory.WriteU32(this.ioReg, address, value); + this.WriteTimerControl(2, oldCnt); + } + break; + + case TM3D: + { + ushort oldCnt = Memory.ReadU16(this.ioReg, TM3CNT); + Memory.WriteU32(this.ioReg, address, value); + this.WriteTimerControl(3, oldCnt); + } + break; + + case FIFO_A_L: + Memory.WriteU32(this.ioReg, address, value); + this.soundManager.IncrementFifoA(); + break; + + case FIFO_B_L: + Memory.WriteU32(this.ioReg, address, value); + this.soundManager.IncrementFifoB(); + break; + + case SOUNDCNT_L: + Memory.WriteU32(this.ioReg, address, value); + if (((value >> 16) & (1 << 11)) != 0) + { + this.soundManager.ResetFifoA(); + } + if (((value >> 16) & (1 << 15)) != 0) + { + this.soundManager.ResetFifoB(); + } + break; + + case IE: + { + uint tmp = Memory.ReadU32(this.ioReg, address); + Memory.WriteU32(this.ioReg, address, (uint)((value & 0xFFFF) | (tmp & (~(value & 0xFFFF0000))))); + } + break; + + case HALTCNT: + Memory.WriteU32(this.ioReg, address, value); + this.processor.Halt(); + break; + + default: + Memory.WriteU32(this.ioReg, address, value); + break; + } + } + + private void WritePalRam8(uint address, byte value) + { + this.waitCycles++; + address &= Memory.palRamMask & ~1U; + this.palRam[address] = value; + this.palRam[address + 1] = value; + } + + private void WritePalRam16(uint address, ushort value) + { + this.waitCycles++; + Memory.WriteU16(this.palRam, address & Memory.palRamMask, value); + } + + private void WritePalRam32(uint address, uint value) + { + this.waitCycles += 2; + Memory.WriteU32(this.palRam, address & Memory.palRamMask, value); + } + + private void WriteVRam8(uint address, byte value) + { + this.waitCycles++; + address &= Memory.vRamMask & ~1U; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + this.vRam[address] = value; + this.vRam[address + 1] = value; + } + + private void WriteVRam16(uint address, ushort value) + { + this.waitCycles++; + address &= Memory.vRamMask; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + Memory.WriteU16(this.vRam, address, value); + } + + private void WriteVRam32(uint address, uint value) + { + this.waitCycles += 2; + address &= Memory.vRamMask; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + Memory.WriteU32(this.vRam, address, value); + } + + private void WriteOamRam8(uint address, byte value) + { + this.waitCycles++; + address &= Memory.oamRamMask & ~1U; + + this.oamRam[address] = value; + this.oamRam[address + 1] = value; + } + + private void WriteOamRam16(uint address, ushort value) + { + this.waitCycles++; + Memory.WriteU16(this.oamRam, address & Memory.oamRamMask, value); + } + + private void WriteOamRam32(uint address, uint value) + { + this.waitCycles++; + Memory.WriteU32(this.oamRam, address & Memory.oamRamMask, value); + } + + private void WriteSRam8(uint address, byte value) + { + this.sRam[address & Memory.sRamMask] = value; + } + + private void WriteSRam16(uint address, ushort value) + { + // TODO + } + + private void WriteSRam32(uint address, uint value) + { + // TODO + } + + private enum EepromModes + { + Idle, + ReadData + } + + private EepromModes eepromMode = EepromModes.Idle; + private byte[] eeprom = new byte[0xffff]; + private byte[] eepromStore = new byte[0xff]; + private int curEepromByte; + private int eepromReadAddress = -1; + + private void WriteEeprom8(uint address, byte value) + { + // EEPROM writes must be done by DMA 3 + if ((this.dmaRegs[3, 3] & (1 << 15)) == 0) return; + // 0 length eeprom writes are bad + if (this.dmaRegs[3, 2] == 0) return; + + if (this.eepromMode != EepromModes.ReadData) + { + this.curEepromByte = 0; + this.eepromMode = EepromModes.ReadData; + this.eepromReadAddress = -1; + + for (int i = 0; i < this.eepromStore.Length; i++) this.eepromStore[i] = 0; + } + + this.eepromStore[this.curEepromByte >> 3] |= (byte)(value << (7 - (this.curEepromByte & 0x7))); + this.curEepromByte++; + + if (this.curEepromByte == this.dmaRegs[3, 2]) + { + if ((this.eepromStore[0] & 0x80) == 0) return; + + if ((this.eepromStore[0] & 0x40) != 0) + { + // Read request + if (this.curEepromByte == 9) + { + this.eepromReadAddress = this.eepromStore[0] & 0x3F; + } + else + { + this.eepromReadAddress = ((this.eepromStore[0] & 0x3F) << 8) | this.eepromStore[1]; + } + + this.curEepromByte = 0; + } + else + { + // Write request + int eepromAddress, offset; + if (this.curEepromByte == 64 + 9) + { + eepromAddress = (int)(this.eepromStore[0] & 0x3F); + offset = 1; + } + else + { + eepromAddress = ((this.eepromStore[0] & 0x3F) << 8) | this.eepromStore[1]; + offset = 2; + } + + for (int i = 0; i < 8; i++) + { + this.eeprom[eepromAddress * 8 + i] = this.eepromStore[i + offset]; + } + + this.eepromMode = EepromModes.Idle; + } + } + } + + private void WriteEeprom16(uint address, ushort value) + { + this.WriteEeprom8(address, (byte)(value & 0xff)); + } + + private void WriteEeprom32(uint address, uint value) + { + this.WriteEeprom8(address, (byte)(value & 0xff)); + } + + private byte ReadEeprom8(uint address) + { + if (this.eepromReadAddress == -1) return 1; + + byte retval = 0; + + if (this.curEepromByte >= 4) + { + retval = (byte)((this.eeprom[this.eepromReadAddress * 8 + ((this.curEepromByte - 4) / 8)] >> (7 - ((this.curEepromByte - 4) & 7))) & 1); + } + + this.curEepromByte++; + + if (this.curEepromByte == this.dmaRegs[3, 2]) + { + this.eepromReadAddress = -1; + this.eepromMode = EepromModes.Idle; + } + + return retval; + } + + private ushort ReadEeprom16(uint address) + { + return (ushort)this.ReadEeprom8(address); + } + + private uint ReadEeprom32(uint address) + { + return (uint)this.ReadEeprom8(address); + } + #endregion + + #region Shader Renderer Vram Writes + private List vramUpdated = new List(); + private List palUpdated = new List(); + public const int VramBlockSize = 64; + public const int PalBlockSize = 32; + private bool[] vramHit = new bool[(Memory.vRamMask + 1) / VramBlockSize]; + private bool[] palHit = new bool[(Memory.palRamMask + 1) / PalBlockSize]; + + public List VramUpdated + { + get + { + List old = this.vramUpdated; + for (int i = 0; i < old.Count; i++) + { + vramHit[old[i]] = false; + } + this.vramUpdated = new List(); + return old; + } + } + + + public List PalUpdated + { + get + { + List old = this.palUpdated; + for (int i = 0; i < old.Count; i++) + { + palHit[old[i]] = false; + } + this.palUpdated = new List(); + return old; + } + } + + private void UpdatePal(uint address) + { + uint index = address / PalBlockSize; + if (!palHit[index]) + { + palHit[index] = true; + this.palUpdated.Add(index); + } + } + + private void UpdateVram(uint address) + { + uint index = address / VramBlockSize; + if (!vramHit[index]) + { + vramHit[index] = true; + this.vramUpdated.Add(index); + } + } + + private void ShaderWritePalRam8(uint address, byte value) + { + this.waitCycles++; + address &= Memory.palRamMask & ~1U; + this.palRam[address] = value; + this.palRam[address + 1] = value; + + this.UpdatePal(address); + } + + private void ShaderWritePalRam16(uint address, ushort value) + { + this.waitCycles++; + Memory.WriteU16(this.palRam, address & Memory.palRamMask, value); + + this.UpdatePal(address & Memory.palRamMask); + } + + private void ShaderWritePalRam32(uint address, uint value) + { + this.waitCycles += 2; + Memory.WriteU32(this.palRam, address & Memory.palRamMask, value); + + this.UpdatePal(address & Memory.palRamMask); + } + + private void ShaderWriteVRam8(uint address, byte value) + { + this.waitCycles++; + address &= Memory.vRamMask & ~1U; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + this.vRam[address] = value; + this.vRam[address + 1] = value; + + this.UpdateVram(address); + } + + private void ShaderWriteVRam16(uint address, ushort value) + { + this.waitCycles++; + address &= Memory.vRamMask; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + Memory.WriteU16(this.vRam, address, value); + + this.UpdateVram(address); + } + + private void ShaderWriteVRam32(uint address, uint value) + { + this.waitCycles += 2; + address &= Memory.vRamMask; + if (address > 0x17FFF) address = 0x10000 + ((address - 0x17FFF) & 0x7FFF); + Memory.WriteU32(this.vRam, address, value); + + this.UpdateVram(address); + } + + public void EnableVramUpdating() + { + this.WriteU8Funcs[0x5] = this.ShaderWritePalRam8; + this.WriteU16Funcs[0x5] = this.ShaderWritePalRam16; + this.WriteU32Funcs[0x5] = this.ShaderWritePalRam32; + this.WriteU8Funcs[0x6] = this.ShaderWriteVRam8; + this.WriteU16Funcs[0x6] = this.ShaderWriteVRam16; + this.WriteU32Funcs[0x6] = this.ShaderWriteVRam32; + + for (uint i = 0; i < (Memory.vRamMask + 1) / Memory.VramBlockSize; i++) + { + this.vramUpdated.Add(i); + } + + for (uint i = 0; i < (Memory.palRamMask + 1) / Memory.PalBlockSize; i++) + { + this.palUpdated.Add(i); + } + } + #endregion + + public byte ReadU8(uint address) + { + uint bank = (address >> 24) & 0xf; + return this.ReadU8Funcs[bank](address); + } + + public ushort ReadU16(uint address) + { + address &= ~1U; + uint bank = (address >> 24) & 0xf; + return this.ReadU16Funcs[bank](address); + } + + public uint ReadU32(uint address) + { + int shiftAmt = (int)((address & 3U) << 3); + address &= ~3U; + uint bank = (address >> 24) & 0xf; + uint res = this.ReadU32Funcs[bank](address); + return (res >> shiftAmt) | (res << (32 - shiftAmt)); + } + + public uint ReadU32Aligned(uint address) + { + uint bank = (address >> 24) & 0xf; + return this.ReadU32Funcs[bank](address); + } + + public ushort ReadU16Debug(uint address) + { + address &= ~1U; + uint bank = (address >> 24) & 0xf; + int oldWaitCycles = this.waitCycles; + ushort res = this.ReadU16Funcs[bank](address); + this.waitCycles = oldWaitCycles; + return res; + } + + public uint ReadU32Debug(uint address) + { + int shiftAmt = (int)((address & 3U) << 3); + address &= ~3U; + uint bank = (address >> 24) & 0xf; + int oldWaitCycles = this.waitCycles; + uint res = this.ReadU32Funcs[bank](address); + this.waitCycles = oldWaitCycles; + return (res >> shiftAmt) | (res << (32 - shiftAmt)); + } + + public void WriteU8(uint address, byte value) + { + uint bank = (address >> 24) & 0xf; + this.WriteU8Funcs[bank](address, value); + } + + public void WriteU16(uint address, ushort value) + { + address &= ~1U; + uint bank = (address >> 24) & 0xf; + this.WriteU16Funcs[bank](address, value); + } + + public void WriteU32(uint address, uint value) + { + address &= ~3U; + uint bank = (address >> 24) & 0xf; + this.WriteU32Funcs[bank](address, value); + } + + public void WriteU8Debug(uint address, byte value) + { + uint bank = (address >> 24) & 0xf; + int oldWaitCycles = this.waitCycles; + this.WriteU8Funcs[bank](address, value); + this.waitCycles = oldWaitCycles; + } + + public void WriteU16Debug(uint address, ushort value) + { + address &= ~1U; + uint bank = (address >> 24) & 0xf; + int oldWaitCycles = this.waitCycles; + this.WriteU16Funcs[bank](address, value); + this.waitCycles = oldWaitCycles; + } + + public void WriteU32Debug(uint address, uint value) + { + address &= ~3U; + uint bank = (address >> 24) & 0xf; + int oldWaitCycles = this.waitCycles; + this.WriteU32Funcs[bank](address, value); + this.waitCycles = oldWaitCycles; + } + + public void LoadBios(byte[] biosRom) + { + Array.Copy(biosRom, this.biosRam, this.biosRam.Length); + } + + public void LoadCartridge(byte[] cartRom) + { + this.ResetRomBank1(); + this.ResetRomBank2(); + + // Set up the appropriate cart size + int cartSize = 1; + while (cartSize < cartRom.Length) + { + cartSize <<= 1; + } + + if (cartSize != cartRom.Length) + { + throw new Exception("Unable to load non power of two carts"); + } + + // Split across bank 1 and 2 if cart is too big + if (cartSize > 1 << 24) + { + this.romBank1 = cartRom; + this.romBank1Mask = (1 << 24) - 1; + + cartRom.CopyTo(this.romBank2, 1 << 24); + this.romBank2Mask = (1 << 24) - 1; + } + else + { + this.romBank1 = cartRom; + this.romBank1Mask = (uint)(cartSize - 1); + } + + if (this.romBank1Mask != 0) + { + // TODO: Writes (i.e. eeprom, and other stuff) + this.ReadU8Funcs[0x8] = this.ReadRom1_8; + this.ReadU8Funcs[0xA] = this.ReadRom1_8; + this.ReadU8Funcs[0xC] = this.ReadRom1_8; + this.ReadU16Funcs[0x8] = this.ReadRom1_16; + this.ReadU16Funcs[0xA] = this.ReadRom1_16; + this.ReadU16Funcs[0xC] = this.ReadRom1_16; + this.ReadU32Funcs[0x8] = this.ReadRom1_32; + this.ReadU32Funcs[0xA] = this.ReadRom1_32; + this.ReadU32Funcs[0xC] = this.ReadRom1_32; + } + + if (this.romBank2Mask != 0) + { + this.ReadU8Funcs[0x9] = this.ReadRom2_8; + this.ReadU8Funcs[0xB] = this.ReadRom2_8; + this.ReadU8Funcs[0xD] = this.ReadRom2_8; + this.ReadU16Funcs[0x9] = this.ReadRom2_16; + this.ReadU16Funcs[0xB] = this.ReadRom2_16; + this.ReadU16Funcs[0xD] = this.ReadRom2_16; + this.ReadU32Funcs[0x9] = this.ReadRom2_32; + this.ReadU32Funcs[0xB] = this.ReadRom2_32; + this.ReadU32Funcs[0xD] = this.ReadRom2_32; + } + } + + private void ResetRomBank1() + { + this.romBank1 = null; + this.romBank1Mask = 0; + + for (int i = 0; i < this.bankSTimes.Length; i++) + { + this.bankSTimes[i] = 2; + } + + this.ReadU8Funcs[0x8] = this.ReadNop8; + this.ReadU8Funcs[0xA] = this.ReadNop8; + this.ReadU8Funcs[0xC] = this.ReadNop8; + this.ReadU16Funcs[0x8] = this.ReadNop16; + this.ReadU16Funcs[0xA] = this.ReadNop16; + this.ReadU16Funcs[0xC] = this.ReadNop16; + this.ReadU32Funcs[0x8] = this.ReadNop32; + this.ReadU32Funcs[0xA] = this.ReadNop32; + this.ReadU32Funcs[0xC] = this.ReadNop32; + } + + private void ResetRomBank2() + { + this.romBank2 = null; + this.romBank2Mask = 0; + + this.ReadU8Funcs[0x9] = this.ReadEeprom8; + this.ReadU8Funcs[0xB] = this.ReadEeprom8; + this.ReadU8Funcs[0xD] = this.ReadEeprom8; + this.ReadU16Funcs[0x9] = this.ReadEeprom16; + this.ReadU16Funcs[0xB] = this.ReadEeprom16; + this.ReadU16Funcs[0xD] = this.ReadEeprom16; + this.ReadU32Funcs[0x9] = this.ReadEeprom32; + this.ReadU32Funcs[0xB] = this.ReadEeprom32; + this.ReadU32Funcs[0xD] = this.ReadEeprom32; + + this.WriteU8Funcs[0x9] = this.WriteEeprom8; + this.WriteU8Funcs[0xB] = this.WriteEeprom8; + this.WriteU8Funcs[0xD] = this.WriteEeprom8; + this.WriteU16Funcs[0x9] = this.WriteEeprom16; + this.WriteU16Funcs[0xB] = this.WriteEeprom16; + this.WriteU16Funcs[0xD] = this.WriteEeprom16; + this.WriteU32Funcs[0x9] = this.WriteEeprom32; + this.WriteU32Funcs[0xB] = this.WriteEeprom32; + this.WriteU32Funcs[0xD] = this.WriteEeprom32; + } + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Renderer.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Renderer.cs new file mode 100644 index 0000000000..d323240619 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Renderer.cs @@ -0,0 +1,952 @@ +namespace GarboDev +{ + using System; + using System.Collections.Generic; + using System.Text; + + public partial class Renderer : IRenderer + { + private Memory memory; + private uint[] scanline = new uint[240]; + private byte[] blend = new byte[240]; + private byte[] windowCover = new byte[240]; + private uint[] back = new uint[240 * 160]; + //private uint[] front = new uint[240 * 160]; + private const uint pitch = 240; + + // Convenience variable as I use it everywhere, set once in RenderLine + private ushort dispCnt; + + // Window helper variables + private byte win0x1, win0x2, win0y1, win0y2; + private byte win1x1, win1x2, win1y1, win1y2; + private byte win0Enabled, win1Enabled, winObjEnabled, winOutEnabled; + private bool winEnabled; + + private byte blendSource, blendTarget; + private byte blendA, blendB, blendY; + private int blendType; + + private int curLine = 0; + + private static uint[] colorLUT; + + static Renderer() + { + colorLUT = new uint[0x10000]; + // Pre-calculate the color LUT + for (uint i = 0; i <= 0xFFFF; i++) + { + uint r = (i & 0x1FU); + uint g = (i & 0x3E0U) >> 5; + uint b = (i & 0x7C00U) >> 10; + r = (r << 3) | (r >> 2); + g = (g << 3) | (g >> 2); + b = (b << 3) | (b >> 2); + colorLUT[i] = (r << 16) | (g << 8) | b; + } + } + + public Memory Memory + { + set { this.memory = value; } + } + + public void Initialize(object data) + { + } + + public void Reset() + { + } + + public uint[] ShowFrame() + { + //Array.Copy(this.back, this.front, this.front.Length); + + //return this.front; + return this.back; + } + + public void RenderLine(int line) + { + this.curLine = line; + + // Render the line + this.dispCnt = Memory.ReadU16(this.memory.IORam, Memory.DISPCNT); + + if ((this.dispCnt & (1 << 7)) != 0) + { + uint bgColor = Renderer.GbaTo32((ushort)0x7FFF); + for (int i = 0; i < 240; i++) this.scanline[i] = bgColor; + } + else + { + this.winEnabled = false; + + if ((this.dispCnt & (1 << 13)) != 0) + { + // Calculate window 0 information + ushort winy = Memory.ReadU16(this.memory.IORam, Memory.WIN0V); + this.win0y1 = (byte)(winy >> 8); + this.win0y2 = (byte)(winy & 0xff); + ushort winx = Memory.ReadU16(this.memory.IORam, Memory.WIN0H); + this.win0x1 = (byte)(winx >> 8); + this.win0x2 = (byte)(winx & 0xff); + + if (this.win0x2 > 240 || this.win0x1 > this.win0x2) + { + this.win0x2 = 240; + } + + if (this.win0y2 > 160 || this.win0y1 > this.win0y2) + { + this.win0y2 = 160; + } + + this.win0Enabled = this.memory.IORam[Memory.WININ]; + this.winEnabled = true; + } + + if ((this.dispCnt & (1 << 14)) != 0) + { + // Calculate window 1 information + ushort winy = Memory.ReadU16(this.memory.IORam, Memory.WIN1V); + this.win1y1 = (byte)(winy >> 8); + this.win1y2 = (byte)(winy & 0xff); + ushort winx = Memory.ReadU16(this.memory.IORam, Memory.WIN1H); + this.win1x1 = (byte)(winx >> 8); + this.win1x2 = (byte)(winx & 0xff); + + if (this.win1x2 > 240 || this.win1x1 > this.win1x2) + { + this.win1x2 = 240; + } + + if (this.win1y2 > 160 || this.win1y1 > this.win1y2) + { + this.win1y2 = 160; + } + + this.win1Enabled = this.memory.IORam[Memory.WININ + 1]; + this.winEnabled = true; + } + + if ((this.dispCnt & (1 << 15)) != 0 && (this.dispCnt & (1 << 12)) != 0) + { + // Object windows are enabled + this.winObjEnabled = this.memory.IORam[Memory.WINOUT + 1]; + this.winEnabled = true; + } + + if (this.winEnabled) + { + this.winOutEnabled = this.memory.IORam[Memory.WINOUT]; + } + + // Calculate blending information + ushort bldcnt = Memory.ReadU16(this.memory.IORam, Memory.BLDCNT); + this.blendType = (bldcnt >> 6) & 0x3; + this.blendSource = (byte)(bldcnt & 0x3F); + this.blendTarget = (byte)((bldcnt >> 8) & 0x3F); + + ushort bldalpha = Memory.ReadU16(this.memory.IORam, Memory.BLDALPHA); + this.blendA = (byte)(bldalpha & 0x1F); + if (this.blendA > 0x10) this.blendA = 0x10; + this.blendB = (byte)((bldalpha >> 8) & 0x1F); + if (this.blendB > 0x10) this.blendB = 0x10; + + this.blendY = (byte)(this.memory.IORam[Memory.BLDY] & 0x1F); + if (this.blendY > 0x10) this.blendY = 0x10; + + switch (this.dispCnt & 0x7) + { + case 0: this.RenderMode0Line(); break; + case 1: this.RenderMode1Line(); break; + case 2: this.RenderMode2Line(); break; + case 3: this.RenderMode3Line(); break; + case 4: this.RenderMode4Line(); break; + case 5: this.RenderMode5Line(); break; + } + } + + Array.Copy(this.scanline, 0, this.back, this.curLine * Renderer.pitch, Renderer.pitch); + } + + private void DrawBackdrop() + { + byte[] palette = this.memory.PaletteRam; + + // Initialize window coverage buffer if neccesary + if (this.winEnabled) + { + for (int i = 0; i < 240; i++) + { + this.windowCover[i] = this.winOutEnabled; + } + + if ((this.dispCnt & (1 << 15)) != 0) + { + // Sprite window + this.DrawSpriteWindows(); + } + + if ((this.dispCnt & (1 << 14)) != 0) + { + // Window 1 + if (this.curLine >= this.win1y1 && this.curLine < this.win1y2) + { + for (int i = this.win1x1; i < this.win1x2; i++) + { + this.windowCover[i] = this.win1Enabled; + } + } + } + + if ((this.dispCnt & (1 << 13)) != 0) + { + // Window 0 + if (this.curLine >= this.win0y1 && this.curLine < this.win0y2) + { + for (int i = this.win0x1; i < this.win0x2; i++) + { + this.windowCover[i] = this.win0Enabled; + } + } + } + } + + // Draw backdrop first + uint bgColor = Renderer.GbaTo32((ushort)(palette[0] | (palette[1] << 8))); + uint modColor = bgColor; + + if (this.blendType == 2 && (this.blendSource & (1 << 5)) != 0) + { + // Brightness increase + uint r = bgColor & 0xFF; + uint g = (bgColor >> 8) & 0xFF; + uint b = (bgColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + modColor = r | (g << 8) | (b << 16); + } + else if (this.blendType == 3 && (this.blendSource & (1 << 5)) != 0) + { + // Brightness decrease + uint r = bgColor & 0xFF; + uint g = (bgColor >> 8) & 0xFF; + uint b = (bgColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + modColor = r | (g << 8) | (b << 16); + } + + if (this.winEnabled) + { + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << 5)) != 0) + { + this.scanline[i] = modColor; + } + else + { + this.scanline[i] = bgColor; + } + this.blend[i] = 1 << 5; + } + } + else + { + for (int i = 0; i < 240; i++) + { + this.scanline[i] = modColor; + this.blend[i] = 1 << 5; + } + } + } + + private void RenderTextBg(int bg) + { + if (this.winEnabled) + { + switch (this.blendType) + { + case 0: + this.RenderTextBgWindow(bg); + break; + case 1: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderTextBgWindowBlend(bg); + else + this.RenderTextBgWindow(bg); + break; + case 2: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderTextBgWindowBrightInc(bg); + else + this.RenderTextBgWindow(bg); + break; + case 3: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderTextBgWindowBrightDec(bg); + else + this.RenderTextBgWindow(bg); + break; + } + } + else + { + switch (this.blendType) + { + case 0: + this.RenderTextBgNormal(bg); + break; + case 1: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderTextBgBlend(bg); + else + this.RenderTextBgNormal(bg); + break; + case 2: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderTextBgBrightInc(bg); + else + this.RenderTextBgNormal(bg); + break; + case 3: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderTextBgBrightDec(bg); + else + this.RenderTextBgNormal(bg); + break; + } + } + } + + private void RenderRotScaleBg(int bg) + { + if (this.winEnabled) + { + switch (this.blendType) + { + case 0: + this.RenderRotScaleBgWindow(bg); + break; + case 1: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderRotScaleBgWindowBlend(bg); + else + this.RenderRotScaleBgWindow(bg); + break; + case 2: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderRotScaleBgWindowBrightInc(bg); + else + this.RenderRotScaleBgWindow(bg); + break; + case 3: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderRotScaleBgWindowBrightDec(bg); + else + this.RenderRotScaleBgWindow(bg); + break; + } + } + else + { + switch (this.blendType) + { + case 0: + this.RenderRotScaleBgNormal(bg); + break; + case 1: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderRotScaleBgBlend(bg); + else + this.RenderRotScaleBgNormal(bg); + break; + case 2: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderRotScaleBgBrightInc(bg); + else + this.RenderRotScaleBgNormal(bg); + break; + case 3: + if ((this.blendSource & (1 << bg)) != 0) + this.RenderRotScaleBgBrightDec(bg); + else + this.RenderRotScaleBgNormal(bg); + break; + } + } + } + + private void DrawSprites(int pri) + { + if (this.winEnabled) + { + switch (this.blendType) + { + case 0: + this.DrawSpritesWindow(pri); + break; + case 1: + if ((this.blendSource & (1 << 4)) != 0) + this.DrawSpritesWindowBlend(pri); + else + this.DrawSpritesWindow(pri); + break; + case 2: + if ((this.blendSource & (1 << 4)) != 0) + this.DrawSpritesWindowBrightInc(pri); + else + this.DrawSpritesWindow(pri); + break; + case 3: + if ((this.blendSource & (1 << 4)) != 0) + this.DrawSpritesWindowBrightDec(pri); + else + this.DrawSpritesWindow(pri); + break; + } + } + else + { + switch (this.blendType) + { + case 0: + this.DrawSpritesNormal(pri); + break; + case 1: + if ((this.blendSource & (1 << 4)) != 0) + this.DrawSpritesBlend(pri); + else + this.DrawSpritesNormal(pri); + break; + case 2: + if ((this.blendSource & (1 << 4)) != 0) + this.DrawSpritesBrightInc(pri); + else + this.DrawSpritesNormal(pri); + break; + case 3: + if ((this.blendSource & (1 << 4)) != 0) + this.DrawSpritesBrightDec(pri); + else + this.DrawSpritesNormal(pri); + break; + } + } + } + + private void RenderMode0Line() + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + this.DrawBackdrop(); + + for (int pri = 3; pri >= 0; pri--) + { + for (int i = 3; i >= 0; i--) + { + if ((this.dispCnt & (1 << (8 + i))) != 0) + { + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i); + + if ((bgcnt & 0x3) == pri) + { + this.RenderTextBg(i); + } + } + } + + this.DrawSprites(pri); + } + } + + private void RenderMode1Line() + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + this.DrawBackdrop(); + + for (int pri = 3; pri >= 0; pri--) + { + if ((this.dispCnt & (1 << (8 + 2))) != 0) + { + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT); + + if ((bgcnt & 0x3) == pri) + { + this.RenderRotScaleBg(2); + } + } + + for (int i = 1; i >= 0; i--) + { + if ((this.dispCnt & (1 << (8 + i))) != 0) + { + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i); + + if ((bgcnt & 0x3) == pri) + { + this.RenderTextBg(i); + } + } + } + + this.DrawSprites(pri); + } + } + + private void RenderMode2Line() + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + this.DrawBackdrop(); + + for (int pri = 3; pri >= 0; pri--) + { + for (int i = 3; i >= 2; i--) + { + if ((this.dispCnt & (1 << (8 + i))) != 0) + { + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)i); + + if ((bgcnt & 0x3) == pri) + { + this.RenderRotScaleBg(i); + } + } + } + + this.DrawSprites(pri); + } + } + + private void RenderMode3Line() + { + ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT); + + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + this.DrawBackdrop(); + + byte blendMaskType = (byte)(1 << 2); + + int bgPri = bg2Cnt & 0x3; + for (int pri = 3; pri > bgPri; pri--) + { + this.DrawSprites(pri); + } + + if ((this.dispCnt & (1 << 10)) != 0) + { + // Background enabled, render it + int x = this.memory.Bgx[0]; + int y = this.memory.Bgy[0]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC); + + for (int i = 0; i < 240; i++) + { + int ax = ((int)x) >> 8; + int ay = ((int)y) >> 8; + + if (ax >= 0 && ax < 240 && ay >= 0 && ay < 160) + { + int curIdx = ((ay * 240) + ax) * 2; + this.scanline[i] = Renderer.GbaTo32((ushort)(vram[curIdx] | (vram[curIdx + 1] << 8))); + this.blend[i] = blendMaskType; + } + + x += dx; + y += dy; + } + } + + for (int pri = bgPri; pri >= 0; pri--) + { + this.DrawSprites(pri); + } + } + + private void RenderMode4Line() + { + ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT); + + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + this.DrawBackdrop(); + + byte blendMaskType = (byte)(1 << 2); + + int bgPri = bg2Cnt & 0x3; + for (int pri = 3; pri > bgPri; pri--) + { + this.DrawSprites(pri); + } + + if ((this.dispCnt & (1 << 10)) != 0) + { + // Background enabled, render it + int baseIdx = 0; + if ((this.dispCnt & (1 << 4)) == 1 << 4) baseIdx = 0xA000; + + int x = this.memory.Bgx[0]; + int y = this.memory.Bgy[0]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC); + + for (int i = 0; i < 240; i++) + { + int ax = ((int)x) >> 8; + int ay = ((int)y) >> 8; + + if (ax >= 0 && ax < 240 && ay >= 0 && ay < 160) + { + int lookup = vram[baseIdx + (ay * 240) + ax]; + if (lookup != 0) + { + this.scanline[i] = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + this.blend[i] = blendMaskType; + } + } + + x += dx; + y += dy; + } + } + + for (int pri = bgPri; pri >= 0; pri--) + { + this.DrawSprites(pri); + } + } + + private void RenderMode5Line() + { + ushort bg2Cnt = Memory.ReadU16(this.memory.IORam, Memory.BG2CNT); + + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + this.DrawBackdrop(); + + byte blendMaskType = (byte)(1 << 2); + + int bgPri = bg2Cnt & 0x3; + for (int pri = 3; pri > bgPri; pri--) + { + this.DrawSprites(pri); + } + + if ((this.dispCnt & (1 << 10)) != 0) + { + // Background enabled, render it + int baseIdx = 0; + if ((this.dispCnt & (1 << 4)) == 1 << 4) baseIdx += 160 * 128 * 2; + + int x = this.memory.Bgx[0]; + int y = this.memory.Bgy[0]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC); + + for (int i = 0; i < 240; i++) + { + int ax = ((int)x) >> 8; + int ay = ((int)y) >> 8; + + if (ax >= 0 && ax < 160 && ay >= 0 && ay < 128) + { + int curIdx = (int)(ay * 160 + ax) * 2; + + this.scanline[i] = Renderer.GbaTo32((ushort)(vram[baseIdx + curIdx] | (vram[baseIdx + curIdx + 1] << 8))); + this.blend[i] = blendMaskType; + } + + x += dx; + y += dy; + } + } + + for (int pri = bgPri; pri >= 0; pri--) + { + this.DrawSprites(pri); + } + } + + private void DrawSpriteWindows() + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + + // Not an object window, so continue + if (((attr0 >> 10) & 3) != 2) continue; + + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + this.windowCover[i & 0x1ff] = this.winObjEnabled; + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + this.windowCover[i & 0x1ff] = this.winObjEnabled; + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + short rx = (short)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + short ry = (short)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + this.windowCover[i & 0x1ff] = this.winObjEnabled; + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + this.windowCover[i & 0x1ff] = this.winObjEnabled; + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + + public static uint GbaTo32(ushort color) + { + // more accurate, but slower :( + // return colorLUT[color]; + return ((color & 0x1FU) << 19) | ((color & 0x3E0U) << 6) | ((color & 0x7C00U) >> 7); + } + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Renderers.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Renderers.cs new file mode 100644 index 0000000000..a05b3b8634 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/Renderers.cs @@ -0,0 +1,5939 @@ +namespace GarboDev +{ + partial class Renderer + { + #region Sprite Drawing + private void DrawSpritesNormal(int priority) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + byte blendMaskType = (byte)(1 << 4); + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + if (((attr2 >> 10) & 3) != priority) continue; + + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + bool semiTransparent = false; + + switch ((attr0 >> 10) & 3) + { + case 1: + // Semi-transparent + semiTransparent = true; + break; + case 2: + // Obj window + continue; + case 3: + continue; + } + + if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if (semiTransparent) + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + else + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + } + private void DrawSpritesBlend(int priority) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + byte blendMaskType = (byte)(1 << 4); + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + if (((attr2 >> 10) & 3) != priority) continue; + + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + bool semiTransparent = false; + + switch ((attr0 >> 10) & 3) + { + case 1: + // Semi-transparent + semiTransparent = true; + break; + case 2: + // Obj window + continue; + case 3: + continue; + } + + if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if (semiTransparent) + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + else + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + } + private void DrawSpritesBrightInc(int priority) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + byte blendMaskType = (byte)(1 << 4); + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + if (((attr2 >> 10) & 3) != priority) continue; + + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + bool semiTransparent = false; + + switch ((attr0 >> 10) & 3) + { + case 1: + // Semi-transparent + semiTransparent = true; + break; + case 2: + // Obj window + continue; + case 3: + continue; + } + + if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if (semiTransparent) + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + else + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + } + private void DrawSpritesBrightDec(int priority) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + byte blendMaskType = (byte)(1 << 4); + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + if (((attr2 >> 10) & 3) != priority) continue; + + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + bool semiTransparent = false; + + switch ((attr0 >> 10) & 3) + { + case 1: + // Semi-transparent + semiTransparent = true; + break; + case 2: + // Obj window + continue; + case 3: + continue; + } + + if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if (semiTransparent) + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + else + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && true) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && true) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + } + private void DrawSpritesWindow(int priority) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + byte blendMaskType = (byte)(1 << 4); + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + if (((attr2 >> 10) & 3) != priority) continue; + + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + bool semiTransparent = false; + + switch ((attr0 >> 10) & 3) + { + case 1: + // Semi-transparent + semiTransparent = true; + break; + case 2: + // Obj window + continue; + case 3: + continue; + } + + if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if (semiTransparent) + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + else + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + } + private void DrawSpritesWindowBlend(int priority) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + byte blendMaskType = (byte)(1 << 4); + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + if (((attr2 >> 10) & 3) != priority) continue; + + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + bool semiTransparent = false; + + switch ((attr0 >> 10) & 3) + { + case 1: + // Semi-transparent + semiTransparent = true; + break; + case 2: + // Obj window + continue; + case 3: + continue; + } + + if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if (semiTransparent) + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + else + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + } + private void DrawSpritesWindowBrightInc(int priority) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + byte blendMaskType = (byte)(1 << 4); + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + if (((attr2 >> 10) & 3) != priority) continue; + + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + bool semiTransparent = false; + + switch ((attr0 >> 10) & 3) + { + case 1: + // Semi-transparent + semiTransparent = true; + break; + case 2: + // Obj window + continue; + case 3: + continue; + } + + if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if (semiTransparent) + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + else + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + } + private void DrawSpritesWindowBrightDec(int priority) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + // OBJ must be enabled in this.dispCnt + if ((this.dispCnt & (1 << 12)) == 0) return; + + byte blendMaskType = (byte)(1 << 4); + + for (int oamNum = 127; oamNum >= 0; oamNum--) + { + ushort attr2 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 4); + + if (((attr2 >> 10) & 3) != priority) continue; + + ushort attr0 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 0); + ushort attr1 = this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(oamNum * 8) + 2); + + int x = attr1 & 0x1FF; + int y = attr0 & 0xFF; + + bool semiTransparent = false; + + switch ((attr0 >> 10) & 3) + { + case 1: + // Semi-transparent + semiTransparent = true; + break; + case 2: + // Obj window + continue; + case 3: + continue; + } + + if ((this.dispCnt & 0x7) >= 3 && (attr2 & 0x3FF) < 0x200) continue; + + int width = -1, height = -1; + switch ((attr0 >> 14) & 3) + { + case 0: + // Square + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 8; break; + case 1: width = 16; height = 16; break; + case 2: width = 32; height = 32; break; + case 3: width = 64; height = 64; break; + } + break; + case 1: + // Horizontal Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 16; height = 8; break; + case 1: width = 32; height = 8; break; + case 2: width = 32; height = 16; break; + case 3: width = 64; height = 32; break; + } + break; + case 2: + // Vertical Rectangle + switch ((attr1 >> 14) & 3) + { + case 0: width = 8; height = 16; break; + case 1: width = 8; height = 32; break; + case 2: width = 16; height = 32; break; + case 3: width = 32; height = 64; break; + } + break; + } + + // Check double size flag here + + int rwidth = width, rheight = height; + if ((attr0 & (1 << 8)) != 0) + { + // Rot-scale on + if ((attr0 & (1 << 9)) != 0) + { + rwidth *= 2; + rheight *= 2; + } + } + else + { + // Invalid sprite + if ((attr0 & (1 << 9)) != 0) + width = -1; + } + + if (width == -1) + { + // Invalid sprite + continue; + } + + // Y clipping + if (y > ((y + rheight) & 0xff)) + { + if (this.curLine >= ((y + rheight) & 0xff) && !(y < this.curLine)) continue; + } + else + { + if (this.curLine < y || this.curLine >= ((y + rheight) & 0xff)) continue; + } + + int scale = 1; + if ((attr0 & (1 << 13)) != 0) scale = 2; + + int spritey = this.curLine - y; + if (spritey < 0) spritey += 256; + + if (semiTransparent) + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + if ((this.blend[i & 0x1ff] & this.blendTarget) != 0 && this.blend[i & 0x1ff] != blendMaskType) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[(i & 0x1ff)]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + else + { + if ((attr0 & (1 << 8)) == 0) + { + if ((attr1 & (1 << 13)) != 0) spritey = (height - 1) - spritey; + + int baseSprite; + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * (width / 8)) * scale; + } + else + { + // 2 dimensional + baseSprite = (attr2 & 0x3FF) + ((spritey / 8) * 0x20); + } + + int baseInc = scale; + if ((attr1 & (1 << 12)) != 0) + { + baseSprite += ((width / 8) * scale) - scale; + baseInc = -baseInc; + } + + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 8) + tx; + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + width; i++) + { + if ((i & 0x1ff) < 240 && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int tx = (i - x) & 7; + if ((attr1 & (1 << 12)) != 0) tx = 7 - tx; + int curIdx = baseSprite * 32 + ((spritey & 7) * 4) + (tx / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + if (((i - x) & 7) == 7) baseSprite += baseInc; + } + } + } + else + { + int rotScaleParam = (attr1 >> 9) & 0x1F; + + short dx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x6); + short dmx = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0xE); + short dy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x16); + short dmy = (short)this.memory.ReadU16Debug(Memory.OAM_BASE + (uint)(rotScaleParam * 8 * 4) + 0x1E); + + int cx = rwidth / 2; + int cy = rheight / 2; + + int baseSprite = attr2 & 0x3FF; + int pitch; + + if ((this.dispCnt & (1 << 6)) != 0) + { + // 1 dimensional + pitch = (width / 8) * scale; + } + else + { + // 2 dimensional + pitch = 0x20; + } + + int rx = (int)((dmx * (spritey - cy)) - (cx * dx) + (width << 7)); + int ry = (int)((dmy * (spritey - cy)) - (cx * dy) + (height << 7)); + + // Draw a rot/scale sprite + if ((attr0 & (1 << 13)) != 0) + { + // 256 colors + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 8) + (tx & 7); + int lookup = vram[0x10000 + curIdx]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[0x200 + lookup * 2] | (palette[0x200 + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + else + { + // 16 colors + int palIdx = 0x200 + (((attr2 >> 12) & 0xF) * 16 * 2); + for (int i = x; i < x + rwidth; i++) + { + int tx = rx >> 8; + int ty = ry >> 8; + + if ((i & 0x1ff) < 240 && tx >= 0 && tx < width && ty >= 0 && ty < height + && (this.windowCover[i & 0x1ff] & (1 << 4)) != 0) + { + int curIdx = (baseSprite + ((ty / 8) * pitch) + ((tx / 8) * scale)) * 32 + ((ty & 7) * 4) + ((tx & 7) / 2); + int lookup = vram[0x10000 + curIdx]; + if ((tx & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palIdx + lookup * 2] | (palette[palIdx + lookup * 2 + 1] << 8))); + if ((this.windowCover[i & 0x1ff] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[(i & 0x1ff)] = pixelColor; + this.blend[(i & 0x1ff)] = blendMaskType; + + } + } + + rx += dx; + ry += dy; + } + } + } + } + } + } + #endregion Sprite Drawing + #region Rot/Scale Bg + private void RenderRotScaleBgNormal(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 128; height = 128; break; + case 1: width = 256; height = 256; break; + case 2: width = 512; height = 512; break; + case 3: width = 1024; height = 1024; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int x = this.memory.Bgx[bg - 2]; + int y = this.memory.Bgy[bg - 2]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10); + + bool transparent = (bgcnt & (1 << 13)) == 0; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int ax = x >> 8; + int ay = y >> 8; + + if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent) + { + int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8)); + int tileChar = vram[tmpTileIdx]; + + int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + + x += dx; + y += dy; + } + } + private void RenderRotScaleBgBlend(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 128; height = 128; break; + case 1: width = 256; height = 256; break; + case 2: width = 512; height = 512; break; + case 3: width = 1024; height = 1024; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int x = this.memory.Bgx[bg - 2]; + int y = this.memory.Bgy[bg - 2]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10); + + bool transparent = (bgcnt & (1 << 13)) == 0; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int ax = x >> 8; + int ay = y >> 8; + + if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent) + { + int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8)); + int tileChar = vram[tmpTileIdx]; + + int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + if ((this.blend[i] & this.blendTarget) != 0) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[i]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + + x += dx; + y += dy; + } + } + private void RenderRotScaleBgBrightInc(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 128; height = 128; break; + case 1: width = 256; height = 256; break; + case 2: width = 512; height = 512; break; + case 3: width = 1024; height = 1024; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int x = this.memory.Bgx[bg - 2]; + int y = this.memory.Bgy[bg - 2]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10); + + bool transparent = (bgcnt & (1 << 13)) == 0; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int ax = x >> 8; + int ay = y >> 8; + + if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent) + { + int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8)); + int tileChar = vram[tmpTileIdx]; + + int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + + x += dx; + y += dy; + } + } + private void RenderRotScaleBgBrightDec(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 128; height = 128; break; + case 1: width = 256; height = 256; break; + case 2: width = 512; height = 512; break; + case 3: width = 1024; height = 1024; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int x = this.memory.Bgx[bg - 2]; + int y = this.memory.Bgy[bg - 2]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10); + + bool transparent = (bgcnt & (1 << 13)) == 0; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int ax = x >> 8; + int ay = y >> 8; + + if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent) + { + int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8)); + int tileChar = vram[tmpTileIdx]; + + int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + + x += dx; + y += dy; + } + } + private void RenderRotScaleBgWindow(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 128; height = 128; break; + case 1: width = 256; height = 256; break; + case 2: width = 512; height = 512; break; + case 3: width = 1024; height = 1024; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int x = this.memory.Bgx[bg - 2]; + int y = this.memory.Bgy[bg - 2]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10); + + bool transparent = (bgcnt & (1 << 13)) == 0; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int ax = x >> 8; + int ay = y >> 8; + + if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent) + { + int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8)); + int tileChar = vram[tmpTileIdx]; + + int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + + x += dx; + y += dy; + } + } + private void RenderRotScaleBgWindowBlend(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 128; height = 128; break; + case 1: width = 256; height = 256; break; + case 2: width = 512; height = 512; break; + case 3: width = 1024; height = 1024; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int x = this.memory.Bgx[bg - 2]; + int y = this.memory.Bgy[bg - 2]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10); + + bool transparent = (bgcnt & (1 << 13)) == 0; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int ax = x >> 8; + int ay = y >> 8; + + if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent) + { + int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8)); + int tileChar = vram[tmpTileIdx]; + + int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + if ((this.blend[i] & this.blendTarget) != 0) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[i]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + + x += dx; + y += dy; + } + } + private void RenderRotScaleBgWindowBrightInc(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 128; height = 128; break; + case 1: width = 256; height = 256; break; + case 2: width = 512; height = 512; break; + case 3: width = 1024; height = 1024; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int x = this.memory.Bgx[bg - 2]; + int y = this.memory.Bgy[bg - 2]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10); + + bool transparent = (bgcnt & (1 << 13)) == 0; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int ax = x >> 8; + int ay = y >> 8; + + if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent) + { + int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8)); + int tileChar = vram[tmpTileIdx]; + + int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + + x += dx; + y += dy; + } + } + private void RenderRotScaleBgWindowBrightDec(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 128; height = 128; break; + case 1: width = 256; height = 256; break; + case 2: width = 512; height = 512; break; + case 3: width = 1024; height = 1024; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int x = this.memory.Bgx[bg - 2]; + int y = this.memory.Bgy[bg - 2]; + + short dx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PA + (uint)(bg - 2) * 0x10); + short dy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PC + (uint)(bg - 2) * 0x10); + + bool transparent = (bgcnt & (1 << 13)) == 0; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int ax = x >> 8; + int ay = y >> 8; + + if ((ax >= 0 && ax < width && ay >= 0 && ay < height) || !transparent) + { + int tmpTileIdx = (int)(screenBase + ((ay & (height - 1)) / 8) * (width / 8) + ((ax & (width - 1)) / 8)); + int tileChar = vram[tmpTileIdx]; + + int lookup = vram[charBase + (tileChar * 64) + ((ay & 7) * 8) + (ax & 7)]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + + x += dx; + y += dy; + } + } + #endregion Rot/Scale Bg + #region Text Bg + private void RenderTextBgNormal(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 256; height = 256; break; + case 1: width = 512; height = 256; break; + case 2: width = 256; height = 512; break; + case 3: width = 512; height = 512; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF; + int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF; + + if ((bgcnt & (1 << 7)) != 0) + { + // 256 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 8; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 56 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + else + { + // 16 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 4; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 28 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)]; + if ((x & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + int palNum = ((tileChar >> 12) & 0xf) * 16 * 2; + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8))); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + } + private void RenderTextBgBlend(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 256; height = 256; break; + case 1: width = 512; height = 256; break; + case 2: width = 256; height = 512; break; + case 3: width = 512; height = 512; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF; + int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF; + + if ((bgcnt & (1 << 7)) != 0) + { + // 256 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 8; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 56 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + if ((this.blend[i] & this.blendTarget) != 0) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[i]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + else + { + // 16 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 4; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 28 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)]; + if ((x & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + int palNum = ((tileChar >> 12) & 0xf) * 16 * 2; + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8))); + if ((this.blend[i] & this.blendTarget) != 0) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[i]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + } + private void RenderTextBgBrightInc(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 256; height = 256; break; + case 1: width = 512; height = 256; break; + case 2: width = 256; height = 512; break; + case 3: width = 512; height = 512; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF; + int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF; + + if ((bgcnt & (1 << 7)) != 0) + { + // 256 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 8; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 56 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + else + { + // 16 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 4; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 28 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)]; + if ((x & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + int palNum = ((tileChar >> 12) & 0xf) * 16 * 2; + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + } + private void RenderTextBgBrightDec(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 256; height = 256; break; + case 1: width = 512; height = 256; break; + case 2: width = 256; height = 512; break; + case 3: width = 512; height = 512; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF; + int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF; + + if ((bgcnt & (1 << 7)) != 0) + { + // 256 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 8; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 56 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + else + { + // 16 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 4; + + for (int i = 0; i < 240; i++) + { + if (true) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 28 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)]; + if ((x & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + int palNum = ((tileChar >> 12) & 0xf) * 16 * 2; + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8))); + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + } + private void RenderTextBgWindow(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 256; height = 256; break; + case 1: width = 512; height = 256; break; + case 2: width = 256; height = 512; break; + case 3: width = 512; height = 512; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF; + int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF; + + if ((bgcnt & (1 << 7)) != 0) + { + // 256 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 8; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 56 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + else + { + // 16 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 4; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 28 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)]; + if ((x & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + int palNum = ((tileChar >> 12) & 0xf) * 16 * 2; + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8))); + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + } + private void RenderTextBgWindowBlend(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 256; height = 256; break; + case 1: width = 512; height = 256; break; + case 2: width = 256; height = 512; break; + case 3: width = 512; height = 512; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF; + int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF; + + if ((bgcnt & (1 << 7)) != 0) + { + // 256 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 8; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 56 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + if ((this.blend[i] & this.blendTarget) != 0) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[i]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + else + { + // 16 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 4; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 28 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)]; + if ((x & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + int palNum = ((tileChar >> 12) & 0xf) * 16 * 2; + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + if ((this.blend[i] & this.blendTarget) != 0) + { + uint r = ((pixelColor & 0xFF) * this.blendA) >> 4; + uint g = (((pixelColor >> 8) & 0xFF) * this.blendA) >> 4; + uint b = (((pixelColor >> 16) & 0xFF) * this.blendA) >> 4; + uint sourceValue = this.scanline[i]; + r += ((sourceValue & 0xFF) * this.blendB) >> 4; + g += (((sourceValue >> 8) & 0xFF) * this.blendB) >> 4; + b += (((sourceValue >> 16) & 0xFF) * this.blendB) >> 4; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + pixelColor = r | (g << 8) | (b << 16); + } + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + } + private void RenderTextBgWindowBrightInc(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 256; height = 256; break; + case 1: width = 512; height = 256; break; + case 2: width = 256; height = 512; break; + case 3: width = 512; height = 512; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF; + int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF; + + if ((bgcnt & (1 << 7)) != 0) + { + // 256 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 8; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 56 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + else + { + // 16 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 4; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 28 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)]; + if ((x & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + int palNum = ((tileChar >> 12) & 0xf) * 16 * 2; + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r + (((0xFF - r) * this.blendY) >> 4); + g = g + (((0xFF - g) * this.blendY) >> 4); + b = b + (((0xFF - b) * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + } + private void RenderTextBgWindowBrightDec(int bg) + { + byte[] palette = this.memory.PaletteRam; + byte[] vram = this.memory.VideoRam; + + byte blendMaskType = (byte)(1 << bg); + + ushort bgcnt = Memory.ReadU16(this.memory.IORam, Memory.BG0CNT + 0x2 * (uint)bg); + + int width = 0, height = 0; + switch ((bgcnt >> 14) & 0x3) + { + case 0: width = 256; height = 256; break; + case 1: width = 512; height = 256; break; + case 2: width = 256; height = 512; break; + case 3: width = 512; height = 512; break; + } + + int screenBase = ((bgcnt >> 8) & 0x1F) * 0x800; + int charBase = ((bgcnt >> 2) & 0x3) * 0x4000; + + int hofs = Memory.ReadU16(this.memory.IORam, Memory.BG0HOFS + (uint)bg * 4) & 0x1FF; + int vofs = Memory.ReadU16(this.memory.IORam, Memory.BG0VOFS + (uint)bg * 4) & 0x1FF; + + if ((bgcnt & (1 << 7)) != 0) + { + // 256 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 8; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 56 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 64) + y + x]; + if (lookup != 0) + { + uint pixelColor = Renderer.GbaTo32((ushort)(palette[lookup * 2] | (palette[lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + else + { + // 16 color tiles + int bgy = ((this.curLine + vofs) & (height - 1)) / 8; + + int tileIdx = screenBase + (((bgy & 31) * 32) * 2); + switch ((bgcnt >> 14) & 0x3) + { + case 2: if (bgy >= 32) tileIdx += 32 * 32 * 2; break; + case 3: if (bgy >= 32) tileIdx += 32 * 32 * 4; break; + } + + int tileY = ((this.curLine + vofs) & 0x7) * 4; + + for (int i = 0; i < 240; i++) + { + if ((this.windowCover[i] & (1 << bg)) != 0) + { + int bgx = ((i + hofs) & (width - 1)) / 8; + int tmpTileIdx = tileIdx + ((bgx & 31) * 2); + if (bgx >= 32) tmpTileIdx += 32 * 32 * 2; + int tileChar = vram[tmpTileIdx] | (vram[tmpTileIdx + 1] << 8); + int x = (i + hofs) & 7; + int y = tileY; + if ((tileChar & (1 << 10)) != 0) x = 7 - x; + if ((tileChar & (1 << 11)) != 0) y = 28 - y; + int lookup = vram[charBase + ((tileChar & 0x3FF) * 32) + y + (x / 2)]; + if ((x & 1) == 0) + { + lookup &= 0xf; + } + else + { + lookup >>= 4; + } + if (lookup != 0) + { + int palNum = ((tileChar >> 12) & 0xf) * 16 * 2; + uint pixelColor = Renderer.GbaTo32((ushort)(palette[palNum + lookup * 2] | (palette[palNum + lookup * 2 + 1] << 8))); + if ((this.windowCover[i] & (1 << 5)) != 0) + { + uint r = pixelColor & 0xFF; + uint g = (pixelColor >> 8) & 0xFF; + uint b = (pixelColor >> 16) & 0xFF; + r = r - ((r * this.blendY) >> 4); + g = g - ((g * this.blendY) >> 4); + b = b - ((b * this.blendY) >> 4); + pixelColor = r | (g << 8) | (b << 16); + } + this.scanline[i] = pixelColor; this.blend[i] = blendMaskType; + + } + } + } + } + } + #endregion Text Bg + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/SoundManager.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/SoundManager.cs new file mode 100644 index 0000000000..5679202345 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/SoundManager.cs @@ -0,0 +1,191 @@ +namespace GarboDev +{ + using System; + using System.Collections.Generic; + using System.Text; + + public class SoundManager + { + private Memory memory = null; + private Queue[] soundQueue = new Queue[2]; + private byte latchedA, latchedB; + private int frequency, cyclesPerSample; + private int leftover = 0; + + private short[] soundBuffer = new short[40000]; + private int soundBufferPos = 0; + private int lastSoundBufferPos = 0; + + public SoundManager(Memory memory, int frequency) + { + this.Frequency = frequency; + + this.memory = memory; + this.memory.SoundManager = this; + + this.soundQueue[0] = new Queue(32); + this.soundQueue[1] = new Queue(32); + } + + #region Public Properties + public int Frequency + { + get { return this.frequency; } + set + { + this.frequency = value; + this.cyclesPerSample = (GbaManager.cpuFreq << 5) / this.frequency; + } + } + + public int QueueSizeA + { + get { return this.soundQueue[0].Count; } + } + + public int QueueSizeB + { + get { return this.soundQueue[1].Count; } + } + + public int SamplesMixed + { + get + { + int value = this.soundBufferPos - this.lastSoundBufferPos; + if (value < 0) value += this.soundBuffer.Length; + return value; + } + } + #endregion + + #region Public Methods + public void GetSamples(short[] buffer, int length) + { + for (int i = 0; i < length; i++) + { + if (this.lastSoundBufferPos == this.soundBuffer.Length) + { + this.lastSoundBufferPos = 0; + } + buffer[i] = this.soundBuffer[this.lastSoundBufferPos++]; + } + } + + public void Mix(int cycles) + { + ushort soundCntH = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_H); + ushort soundCntX = Memory.ReadU16(this.memory.IORam, Memory.SOUNDCNT_X); + + cycles <<= 5; + cycles += this.leftover; + + if (cycles > 0) + { + // Precompute loop invariants + short directA = (short)(sbyte)(this.latchedA); + short directB = (short)(sbyte)(this.latchedB); + + if ((soundCntH & (1 << 2)) == 0) + { + directA >>= 1; + } + if ((soundCntH & (1 << 3)) == 0) + { + directB >>= 1; + } + + while (cycles > 0) + { + short l = 0, r = 0; + + cycles -= this.cyclesPerSample; + + // Mixing + if ((soundCntX & (1 << 7)) != 0) + { + if ((soundCntH & (1 << 8)) != 0) + { + r += directA; + } + if ((soundCntH & (1 << 9)) != 0) + { + l += directA; + } + if ((soundCntH & (1 << 12)) != 0) + { + r += directB; + } + if ((soundCntH & (1 << 13)) != 0) + { + l += directB; + } + } + + if (this.soundBufferPos == this.soundBuffer.Length) + { + this.soundBufferPos = 0; + } + + this.soundBuffer[this.soundBufferPos++] = (short)(l << 6); + this.soundBuffer[this.soundBufferPos++] = (short)(r << 6); + } + } + + this.leftover = cycles; + } + + public void ResetFifoA() + { + this.soundQueue[0].Clear(); + this.latchedA = 0; + } + + public void ResetFifoB() + { + this.soundQueue[1].Clear(); + this.latchedB = 0; + } + + public void IncrementFifoA() + { + for (int i = 0; i < 4; i++) + { + this.EnqueueDSoundSample(0, this.memory.IORam[Memory.FIFO_A_L + i]); + } + } + + public void IncrementFifoB() + { + for (int i = 0; i < 4; i++) + { + this.EnqueueDSoundSample(1, this.memory.IORam[Memory.FIFO_B_L + i]); + } + } + + public void DequeueA() + { + if (this.soundQueue[0].Count > 0) + { + this.latchedA = this.soundQueue[0].Dequeue(); + } + } + + public void DequeueB() + { + if (this.soundQueue[1].Count > 0) + { + this.latchedB = this.soundQueue[1].Dequeue(); + } + } + #endregion Public Methods + + private void EnqueueDSoundSample(int channel, byte sample) + { + if (this.soundQueue[channel].Count < 32) + { + this.soundQueue[channel].Enqueue(sample); + } + } + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/ThumbCore.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/ThumbCore.cs new file mode 100644 index 0000000000..ca17f233c2 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/ThumbCore.cs @@ -0,0 +1,981 @@ +//#define ARM_DEBUG + +namespace GarboDev +{ + using System; + using System.Collections.Generic; + using System.Text; + + public class ThumbCore + { + private const int COND_EQ = 0; // Z set + private const int COND_NE = 1; // Z clear + private const int COND_CS = 2; // C set + private const int COND_CC = 3; // C clear + private const int COND_MI = 4; // N set + private const int COND_PL = 5; // N clear + private const int COND_VS = 6; // V set + private const int COND_VC = 7; // V clear + private const int COND_HI = 8; // C set and Z clear + private const int COND_LS = 9; // C clear or Z set + private const int COND_GE = 10; // N equals V + private const int COND_LT = 11; // N not equal to V + private const int COND_GT = 12; // Z clear AND (N equals V) + private const int COND_LE = 13; // Z set OR (N not equal to V) + private const int COND_AL = 14; // Always + private const int COND_NV = 15; // Never execute + + private const int OP_AND = 0x0; + private const int OP_EOR = 0x1; + private const int OP_LSL = 0x2; + private const int OP_LSR = 0x3; + private const int OP_ASR = 0x4; + private const int OP_ADC = 0x5; + private const int OP_SBC = 0x6; + private const int OP_ROR = 0x7; + private const int OP_TST = 0x8; + private const int OP_NEG = 0x9; + private const int OP_CMP = 0xA; + private const int OP_CMN = 0xB; + private const int OP_ORR = 0xC; + private const int OP_MUL = 0xD; + private const int OP_BIC = 0xE; + private const int OP_MVN = 0xF; + + private Arm7Processor parent; + private Memory memory; + private uint[] registers; + + // CPU flags + private uint zero, carry, negative, overflow; + private ushort curInstruction, instructionQueue; + + private delegate void ExecuteInstruction(); + private ExecuteInstruction[] NormalOps = null; + + public ThumbCore(Arm7Processor parent, Memory memory) + { + this.parent = parent; + this.memory = memory; + this.registers = this.parent.Registers; + + this.NormalOps = new ExecuteInstruction[256] + { + OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, OpLslImm, + OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, OpLsrImm, + OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, OpAsrImm, + OpAddRegReg, OpAddRegReg, OpSubRegReg, OpSubRegReg, OpAddRegImm, OpAddRegImm, OpSubRegImm, OpSubRegImm, + OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, OpMovImm, + OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, OpCmpImm, + OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, OpAddImm, + OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, OpSubImm, + OpArith, OpArith, OpArith, OpArith, OpAddHi, OpCmpHi, OpMovHi, OpBx, + OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, OpLdrPc, + OpStrReg, OpStrReg, OpStrhReg, OpStrhReg, OpStrbReg, OpStrbReg, OpLdrsbReg, OpLdrsbReg, + OpLdrReg, OpLdrReg, OpLdrhReg, OpLdrhReg, OpLdrbReg, OpLdrbReg, OpLdrshReg, OpLdrshReg, + OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, OpStrImm, + OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, OpLdrImm, + OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, OpStrbImm, + OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, OpLdrbImm, + OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, OpStrhImm, + OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, OpLdrhImm, + OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, OpStrSp, + OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, OpLdrSp, + OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, OpAddPc, + OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, OpAddSp, + OpSubSp, OpUnd, OpUnd, OpUnd, OpPush, OpPushLr, OpUnd, OpUnd, + OpUnd, OpUnd, OpUnd, OpUnd, OpPop, OpPopPc, OpUnd, OpUnd, + OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, OpStmia, + OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, OpLdmia, + OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, + OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpBCond, OpUnd, OpSwi, + OpB, OpB, OpB, OpB, OpB, OpB, OpB, OpB, + OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, OpUnd, + OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, OpBl1, + OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2, OpBl2 + }; + } + + public void BeginExecution() + { + this.FlushQueue(); + } + + public void Step() + { + this.UnpackFlags(); + + this.curInstruction = this.instructionQueue; + this.instructionQueue = this.memory.ReadU16(registers[15]); + registers[15] += 2; + + // Execute the instruction + this.NormalOps[this.curInstruction >> 8](); + + this.parent.Cycles -= this.memory.WaitCycles; + + if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK) + { + if ((this.curInstruction >> 8) != 0xDF) this.parent.ReloadQueue(); + } + + this.PackFlags(); + } + + public void Execute() + { + this.UnpackFlags(); + + while (this.parent.Cycles > 0) + { + this.curInstruction = this.instructionQueue; + this.instructionQueue = this.memory.ReadU16(registers[15]); + registers[15] += 2; + + // Execute the instruction + this.NormalOps[this.curInstruction >> 8](); + + this.parent.Cycles -= this.memory.WaitCycles; + + if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK) + { + if ((this.curInstruction >> 8) != 0xDF) this.parent.ReloadQueue(); + break; + } + + // Check the current PC +#if ARM_DEBUG + if (this.parent.Breakpoints.ContainsKey(registers[15] - 2U)) + { + this.parent.BreakpointHit = true; + break; + } +#endif + } + + this.PackFlags(); + } + + #region Flag helpers + public void OverflowCarryAdd(uint a, uint b, uint r) + { + overflow = ((a & b & ~r) | (~a & ~b & r)) >> 31; + carry = ((a & b) | (a & ~r) | (b & ~r)) >> 31; + } + + public void OverflowCarrySub(uint a, uint b, uint r) + { + overflow = ((a & ~b & ~r) | (~a & b & r)) >> 31; + carry = ((a & ~b) | (a & ~r) | (~b & ~r)) >> 31; + } + #endregion + + #region Opcodes + private void OpLslImm() + { + // 0x00 - 0x07 + // lsl rd, rm, #immed + int rd = this.curInstruction & 0x7; + int rm = (this.curInstruction >> 3) & 0x7; + int immed = (this.curInstruction >> 6) & 0x1F; + + if (immed == 0) + { + registers[rd] = registers[rm]; + } else + { + carry = (registers[rm] >> (32 - immed)) & 0x1; + registers[rd] = registers[rm] << immed; + } + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpLsrImm() + { + // 0x08 - 0x0F + // lsr rd, rm, #immed + int rd = this.curInstruction & 0x7; + int rm = (this.curInstruction >> 3) & 0x7; + int immed = (this.curInstruction >> 6) & 0x1F; + + if (immed == 0) + { + carry = registers[rm] >> 31; + registers[rd] = 0; + } + else + { + carry = (registers[rm] >> (immed - 1)) & 0x1; + registers[rd] = registers[rm] >> immed; + } + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpAsrImm() + { + // asr rd, rm, #immed + int rd = this.curInstruction & 0x7; + int rm = (this.curInstruction >> 3) & 0x7; + int immed = (this.curInstruction >> 6) & 0x1F; + + if (immed == 0) + { + carry = registers[rm] >> 31; + if (carry == 1) registers[rd] = 0xFFFFFFFF; + else registers[rd] = 0; + } + else + { + carry = (registers[rm] >> (immed - 1)) & 0x1; + registers[rd] = (uint)(((int)registers[rm]) >> immed); + } + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpAddRegReg() + { + // add rd, rn, rm + int rd = this.curInstruction & 0x7; + int rn = (this.curInstruction >> 3) & 0x7; + int rm = (this.curInstruction >> 6) & 0x7; + + uint orn = registers[rn]; + uint orm = registers[rm]; + + registers[rd] = orn + orm; + + this.OverflowCarryAdd(orn, orm, registers[rd]); + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpSubRegReg() + { + // sub rd, rn, rm + int rd = this.curInstruction & 0x7; + int rn = (this.curInstruction >> 3) & 0x7; + int rm = (this.curInstruction >> 6) & 0x7; + + uint orn = registers[rn]; + uint orm = registers[rm]; + + registers[rd] = orn - orm; + + this.OverflowCarrySub(orn, orm, registers[rd]); + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpAddRegImm() + { + // add rd, rn, #immed + int rd = this.curInstruction & 0x7; + int rn = (this.curInstruction >> 3) & 0x7; + uint immed = (uint)((this.curInstruction >> 6) & 0x7); + + uint orn = registers[rn]; + + registers[rd] = orn + immed; + + this.OverflowCarryAdd(orn, immed, registers[rd]); + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpSubRegImm() + { + // sub rd, rn, #immed + int rd = this.curInstruction & 0x7; + int rn = (this.curInstruction >> 3) & 0x7; + uint immed = (uint)((this.curInstruction >> 6) & 0x7); + + uint orn = registers[rn]; + + registers[rd] = orn - immed; + + this.OverflowCarrySub(orn, immed, registers[rd]); + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpMovImm() + { + // mov rd, #immed + int rd = (this.curInstruction >> 8) & 0x7; + + registers[rd] = (uint)(this.curInstruction & 0xFF); + + negative = 0; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpCmpImm() + { + // cmp rn, #immed + int rn = (this.curInstruction >> 8) & 0x7; + + uint alu = registers[rn] - (uint)(this.curInstruction & 0xFF); + + this.OverflowCarrySub(registers[rn], (uint)(this.curInstruction & 0xFF), alu); + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + } + + private void OpAddImm() + { + // add rd, #immed + int rd = (this.curInstruction >> 8) & 0x7; + + uint ord = registers[rd]; + + registers[rd] += (uint)(this.curInstruction & 0xFF); + + this.OverflowCarryAdd(ord, (uint)(this.curInstruction & 0xFF), registers[rd]); + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpSubImm() + { + // sub rd, #immed + int rd = (this.curInstruction >> 8) & 0x7; + + uint ord = registers[rd]; + + registers[rd] -= (uint)(this.curInstruction & 0xFF); + + this.OverflowCarrySub(ord, (uint)(this.curInstruction & 0xFF), registers[rd]); + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + } + + private void OpArith() + { + int rd = this.curInstruction & 0x7; + uint rn = registers[(this.curInstruction >> 3) & 0x7]; + + uint orig, alu; + int shiftAmt; + + switch ((this.curInstruction >> 6) & 0xF) + { + case OP_ADC: + orig = registers[rd]; + registers[rd] += rn + carry; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarryAdd(orig, rn, registers[rd]); + break; + + case OP_AND: + registers[rd] &= rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_ASR: + shiftAmt = (int)(rn & 0xFF); + if (shiftAmt == 0) + { + // Do nothing + } + else if (shiftAmt < 32) + { + carry = (registers[rd] >> (shiftAmt - 1)) & 0x1; + registers[rd] = (uint)(((int)registers[rd]) >> shiftAmt); + } + else + { + carry = (registers[rd] >> 31) & 1; + if (carry == 1) registers[rd] = 0xFFFFFFFF; + else registers[rd] = 0; + } + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_BIC: + registers[rd] &= ~rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_CMN: + alu = registers[rd] + rn; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarryAdd(registers[rd], rn, alu); + break; + + case OP_CMP: + alu = registers[rd] - rn; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(registers[rd], rn, alu); + break; + + case OP_EOR: + registers[rd] ^= rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_LSL: + shiftAmt = (int)(rn & 0xFF); + if (shiftAmt == 0) + { + // Do nothing + } + else if (shiftAmt < 32) + { + carry = (registers[rd] >> (32 - shiftAmt)) & 0x1; + registers[rd] <<= shiftAmt; + } + else if (shiftAmt == 32) + { + carry = registers[rd] & 0x1; + registers[rd] = 0; + } + else + { + carry = 0; + registers[rd] = 0; + } + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_LSR: + shiftAmt = (int)(rn & 0xFF); + if (shiftAmt == 0) + { + // Do nothing + } + else if (shiftAmt < 32) + { + carry = (registers[rd] >> (shiftAmt - 1)) & 0x1; + registers[rd] >>= shiftAmt; + } + else if (shiftAmt == 32) + { + carry = (registers[rd] >> 31) & 0x1; + registers[rd] = 0; + } + else + { + carry = 0; + registers[rd] = 0; + } + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_MUL: + int mulCycles = 4; + // Multiply cycle calculations + if ((rn & 0xFFFFFF00) == 0 || (rn & 0xFFFFFF00) == 0xFFFFFF00) + { + mulCycles = 1; + } + else if ((rn & 0xFFFF0000) == 0 || (rn & 0xFFFF0000) == 0xFFFF0000) + { + mulCycles = 2; + } + else if ((rn & 0xFF000000) == 0 || (rn & 0xFF000000) == 0xFF000000) + { + mulCycles = 3; + } + + this.parent.Cycles -= mulCycles; + + registers[rd] *= rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_MVN: + registers[rd] = ~rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_NEG: + registers[rd] = 0 - rn; + + this.OverflowCarrySub(0, rn, registers[rd]); + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_ORR: + registers[rd] |= rn; + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_ROR: + shiftAmt = (int)(rn & 0xFF); + if (shiftAmt == 0) + { + // Do nothing + } + else if ((shiftAmt & 0x1F) == 0) + { + carry = registers[rd] >> 31; + } + else + { + shiftAmt &= 0x1F; + carry = (registers[rd] >> (shiftAmt - 1)) & 0x1; + registers[rd] = (registers[rd] >> shiftAmt) | (registers[rd] << (32 - shiftAmt)); + } + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + break; + + case OP_SBC: + orig = registers[rd]; + registers[rd] = (registers[rd] - rn) - (1U - carry); + + negative = registers[rd] >> 31; + zero = registers[rd] == 0 ? 1U : 0U; + this.OverflowCarrySub(orig, rn, registers[rd]); + break; + + case OP_TST: + alu = registers[rd] & rn; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + break; + + default: + throw new Exception("The coder screwed up on the thumb alu op..."); + } + } + + private void OpAddHi() + { + int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7); + int rm = (this.curInstruction >> 3) & 0xF; + + registers[rd] += registers[rm]; + + if (rd == 15) + { + registers[rd] &= ~1U; + this.FlushQueue(); + } + } + + private void OpCmpHi() + { + int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7); + int rm = (this.curInstruction >> 3) & 0xF; + + uint alu = registers[rd] - registers[rm]; + + negative = alu >> 31; + zero = alu == 0 ? 1U : 0U; + this.OverflowCarrySub(registers[rd], registers[rm], alu); + } + + private void OpMovHi() + { + int rd = ((this.curInstruction & (1 << 7)) >> 4) | (this.curInstruction & 0x7); + int rm = (this.curInstruction >> 3) & 0xF; + + registers[rd] = registers[rm]; + + if (rd == 15) + { + registers[rd] &= ~1U; + this.FlushQueue(); + } + } + + private void OpBx() + { + int rm = (this.curInstruction >> 3) & 0xf; + + this.PackFlags(); + + this.parent.CPSR &= ~Arm7Processor.T_MASK; + this.parent.CPSR |= (registers[rm] & 1) << Arm7Processor.T_BIT; + + registers[15] = registers[rm] & (~1U); + + this.UnpackFlags(); + + // Check for branch back to Arm Mode + if ((this.parent.CPSR & Arm7Processor.T_MASK) != Arm7Processor.T_MASK) + { + return; + } + + this.FlushQueue(); + } + + private void OpLdrPc() + { + int rd = (this.curInstruction >> 8) & 0x7; + + registers[rd] = this.memory.ReadU32((registers[15] & ~2U) + (uint)((this.curInstruction & 0xFF) * 4)); + + this.parent.Cycles--; + } + + private void OpStrReg() + { + this.memory.WriteU32(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7], + registers[this.curInstruction & 0x7]); + } + + private void OpStrhReg() + { + this.memory.WriteU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7], + (ushort)(registers[this.curInstruction & 0x7] & 0xFFFF)); + } + + private void OpStrbReg() + { + this.memory.WriteU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7], + (byte)(registers[this.curInstruction & 0x7] & 0xFF)); + } + + private void OpLdrsbReg() + { + registers[this.curInstruction & 0x7] = + this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]); + + if ((registers[this.curInstruction & 0x7] & (1 << 7)) != 0) + { + registers[this.curInstruction & 0x7] |= 0xFFFFFF00; + } + + this.parent.Cycles--; + } + + private void OpLdrReg() + { + registers[this.curInstruction & 0x7] = + this.memory.ReadU32(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]); + + this.parent.Cycles--; + } + + private void OpLdrhReg() + { + registers[this.curInstruction & 0x7] = + this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]); + + this.parent.Cycles--; + } + + private void OpLdrbReg() + { + registers[this.curInstruction & 0x7] = + this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]); + + this.parent.Cycles--; + } + + private void OpLdrshReg() + { + registers[this.curInstruction & 0x7] = + this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + registers[(this.curInstruction >> 6) & 0x7]); + + if ((registers[this.curInstruction & 0x7] & (1 << 15)) != 0) + { + registers[this.curInstruction & 0x7] |= 0xFFFF0000; + } + + this.parent.Cycles--; + } + + private void OpStrImm() + { + this.memory.WriteU32(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 4), + registers[this.curInstruction & 0x7]); + } + + private void OpLdrImm() + { + registers[this.curInstruction & 0x7] = + this.memory.ReadU32(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 4)); + + this.parent.Cycles--; + } + + private void OpStrbImm() + { + this.memory.WriteU8(registers[(this.curInstruction >> 3) & 0x7] + (uint)((this.curInstruction >> 6) & 0x1F), + (byte)(registers[this.curInstruction & 0x7] & 0xFF)); + } + + private void OpLdrbImm() + { + registers[this.curInstruction & 0x7] = + this.memory.ReadU8(registers[(this.curInstruction >> 3) & 0x7] + (uint)((this.curInstruction >> 6) & 0x1F)); + + this.parent.Cycles--; + } + + private void OpStrhImm() + { + this.memory.WriteU16(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 2), + (ushort)(registers[this.curInstruction & 0x7] & 0xFFFF)); + } + + private void OpLdrhImm() + { + registers[this.curInstruction & 0x7] = + this.memory.ReadU16(registers[(this.curInstruction >> 3) & 0x7] + (uint)(((this.curInstruction >> 6) & 0x1F) * 2)); + + this.parent.Cycles--; + } + + private void OpStrSp() + { + this.memory.WriteU32(registers[13] + (uint)((this.curInstruction & 0xFF) * 4), + registers[(this.curInstruction >> 8) & 0x7]); + } + + private void OpLdrSp() + { + registers[(this.curInstruction >> 8) & 0x7] = + this.memory.ReadU32(registers[13] + (uint)((this.curInstruction & 0xFF) * 4)); + } + + private void OpAddPc() + { + registers[(this.curInstruction >> 8) & 0x7] = + (registers[15] & ~2U) + (uint)((this.curInstruction & 0xFF) * 4); + } + + private void OpAddSp() + { + registers[(this.curInstruction >> 8) & 0x7] = + registers[13] + (uint)((this.curInstruction & 0xFF) * 4); + } + + private void OpSubSp() + { + if ((this.curInstruction & (1 << 7)) != 0) + registers[13] -= (uint)((this.curInstruction & 0x7F) * 4); + else + registers[13] += (uint)((this.curInstruction & 0x7F) * 4); + } + + private void OpPush() + { + for (int i = 7; i >= 0; i--) + { + if (((this.curInstruction >> i) & 1) != 0) + { + registers[13] -= 4; + this.memory.WriteU32(registers[13], registers[i]); + } + } + } + + private void OpPushLr() + { + registers[13] -= 4; + this.memory.WriteU32(registers[13], registers[14]); + + for (int i = 7; i >= 0; i--) + { + if (((this.curInstruction >> i) & 1) != 0) + { + registers[13] -= 4; + this.memory.WriteU32(registers[13], registers[i]); + } + } + } + + private void OpPop() + { + for (int i = 0; i < 8; i++) + { + if (((this.curInstruction >> i) & 1) != 0) + { + registers[i] = this.memory.ReadU32(registers[13]); + registers[13] += 4; + } + } + + this.parent.Cycles--; + } + + private void OpPopPc() + { + for (int i = 0; i < 8; i++) + { + if (((this.curInstruction >> i) & 1) != 0) + { + registers[i] = this.memory.ReadU32(registers[13]); + registers[13] += 4; + } + } + + registers[15] = this.memory.ReadU32(registers[13]) & (~1U); + registers[13] += 4; + + // ARM9 check here + + this.FlushQueue(); + + this.parent.Cycles--; + } + + private void OpStmia() + { + int rn = (this.curInstruction >> 8) & 0x7; + + for (int i = 0; i < 8; i++) + { + if (((this.curInstruction >> i) & 1) != 0) + { + this.memory.WriteU32(registers[rn] & (~3U), registers[i]); + registers[rn] += 4; + } + } + } + + private void OpLdmia() + { + int rn = (this.curInstruction >> 8) & 0x7; + + uint address = registers[rn]; + + for (int i = 0; i < 8; i++) + { + if (((this.curInstruction >> i) & 1) != 0) + { + registers[i] = this.memory.ReadU32Aligned(address & (~3U)); + address += 4; + } + } + + if (((this.curInstruction >> rn) & 1) == 0) + { + registers[rn] = address; + } + } + + private void OpBCond() + { + uint cond = 0; + switch ((this.curInstruction >> 8) & 0xF) + { + case COND_AL: cond = 1; break; + case COND_EQ: cond = zero; break; + case COND_NE: cond = 1 - zero; break; + case COND_CS: cond = carry; break; + case COND_CC: cond = 1 - carry; break; + case COND_MI: cond = negative; break; + case COND_PL: cond = 1 - negative; break; + case COND_VS: cond = overflow; break; + case COND_VC: cond = 1 - overflow; break; + case COND_HI: cond = carry & (1 - zero); break; + case COND_LS: cond = (1 - carry) | zero; break; + case COND_GE: cond = (1 - negative) ^ overflow; break; + case COND_LT: cond = negative ^ overflow; break; + case COND_GT: cond = (1 - zero) & (negative ^ (1 - overflow)); break; + case COND_LE: cond = (negative ^ overflow) | zero; break; + } + + if (cond == 1) + { + uint offset = (uint)(this.curInstruction & 0xFF); + if ((offset & (1 << 7)) != 0) offset |= 0xFFFFFF00; + + registers[15] += offset << 1; + + this.FlushQueue(); + } + } + + private void OpSwi() + { + registers[15] -= 4U; + this.parent.EnterException(Arm7Processor.SVC, 0x8, false, false); + } + + private void OpB() + { + uint offset = (uint)(this.curInstruction & 0x7FF); + if ((offset & (1 << 10)) != 0) offset |= 0xFFFFF800; + + registers[15] += offset << 1; + + this.FlushQueue(); + } + + private void OpBl1() + { + uint offset = (uint)(this.curInstruction & 0x7FF); + if ((offset & (1 << 10)) != 0) offset |= 0xFFFFF800; + + registers[14] = registers[15] + (offset << 12); + } + + private void OpBl2() + { + uint tmp = registers[15]; + registers[15] = registers[14] + (uint)((this.curInstruction & 0x7FF) << 1); + registers[14] = (tmp - 2U) | 1; + + this.FlushQueue(); + } + + private void OpUnd() + { + throw new Exception("Unknown opcode"); + } + #endregion + + private void PackFlags() + { + this.parent.CPSR &= 0x0FFFFFFF; + this.parent.CPSR |= this.negative << Arm7Processor.N_BIT; + this.parent.CPSR |= this.zero << Arm7Processor.Z_BIT; + this.parent.CPSR |= this.carry << Arm7Processor.C_BIT; + this.parent.CPSR |= this.overflow << Arm7Processor.V_BIT; + } + + private void UnpackFlags() + { + this.negative = (this.parent.CPSR >> Arm7Processor.N_BIT) & 1; + this.zero = (this.parent.CPSR >> Arm7Processor.Z_BIT) & 1; + this.carry = (this.parent.CPSR >> Arm7Processor.C_BIT) & 1; + this.overflow = (this.parent.CPSR >> Arm7Processor.V_BIT) & 1; + } + + private void FlushQueue() + { + this.instructionQueue = this.memory.ReadU16(registers[15]); + registers[15] += 2; + } + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/VideoManager.cs b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/VideoManager.cs new file mode 100644 index 0000000000..e820aa990d --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/GBAAlt/VideoManager.cs @@ -0,0 +1,164 @@ +namespace GarboDev +{ + public class VideoManager + { + public delegate void OnPresent(uint[] data); + + private Memory memory = null; + private IRenderer renderer = null; + private OnPresent presenter; + private GbaManager gbaManager; + private int curLine; + + public Memory Memory + { + set{ this.memory = value; } + } + + public IRenderer Renderer + { + set + { + this.renderer = value; + this.renderer.Memory = this.memory; + } + } + + public OnPresent Presenter + { + set { this.presenter = value; } + } + + public VideoManager(GbaManager gbaManager) + { + this.gbaManager = gbaManager; + } + + public void Reset() + { + this.curLine = 0; + + this.renderer.Memory = memory; + this.renderer.Reset(); + } + + private void EnterVBlank(Arm7Processor processor) + { + ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT); + dispstat |= 1; + Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat); + + // Render the frame + this.gbaManager.FramesRendered++; + this.presenter(this.renderer.ShowFrame()); + + if ((dispstat & (1 << 3)) != 0) + { + // Fire the vblank irq + processor.RequestIrq(0); + } + + // Check for DMA triggers + this.memory.VBlankDma(); + } + + private void LeaveVBlank(Arm7Processor processor) + { + ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT); + dispstat &= 0xFFFE; + Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat); + + processor.UpdateKeyState(); + + // Update the rot/scale values + this.memory.Bgx[0] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG2X_L); + this.memory.Bgx[1] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG3X_L); + this.memory.Bgy[0] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG2Y_L); + this.memory.Bgy[1] = (int)Memory.ReadU32(this.memory.IORam, Memory.BG3Y_L); + } + + public void EnterHBlank(Arm7Processor processor) + { + ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT); + dispstat |= 1 << 1; + Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat); + + // Advance the bgx registers + for (int bg = 0; bg <= 1; bg++) + { + short dmx = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PB + (uint)bg * 0x10); + short dmy = (short)Memory.ReadU16(this.memory.IORam, Memory.BG2PD + (uint)bg * 0x10); + this.memory.Bgx[bg] += dmx; + this.memory.Bgy[bg] += dmy; + } + + if (this.curLine < 160) + { + this.memory.HBlankDma(); + + // Trigger hblank irq + if ((dispstat & (1 << 4)) != 0) + { + processor.RequestIrq(1); + } + } + } + + /// + /// + /// + /// + /// true if end of frame + public bool LeaveHBlank(Arm7Processor processor) + { + bool ret = false; + ushort dispstat = Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT); + dispstat &= 0xFFF9; + Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat); + + // Move to the next line + this.curLine++; + + if (this.curLine >= 228) + { + // Start again at the beginning + this.curLine = 0; + } + + // Update registers + Memory.WriteU16(this.memory.IORam, Memory.VCOUNT, (ushort)this.curLine); + + // Check for vblank + if (this.curLine == 160) + { + this.EnterVBlank(processor); + ret = true; + } + else if (this.curLine == 0) + { + this.LeaveVBlank(processor); + } + + // Check y-line trigger + if (((dispstat >> 8) & 0xff) == this.curLine) + { + dispstat = (ushort)(Memory.ReadU16(this.memory.IORam, Memory.DISPSTAT) | (1 << 2)); + Memory.WriteU16(this.memory.IORam, Memory.DISPSTAT, dispstat); + + if ((dispstat & (1 << 5)) != 0) + { + processor.RequestIrq(2); + } + } + return ret; + } + + public void RenderLine() + { + if (this.curLine < 160) + { + this.renderer.RenderLine(this.curLine); + } + } + } +} \ No newline at end of file diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs index cc2e070177..71a88e346a 100644 --- a/BizHawk.MultiClient/MainForm.cs +++ b/BizHawk.MultiClient/MainForm.cs @@ -1928,7 +1928,8 @@ namespace BizHawk.MultiClient MessageBox.Show("Unable to find the required GBA BIOS file - \n" + gbabios, "Unable to load BIOS", MessageBoxButtons.OK, MessageBoxIcon.Error); throw new Exception(); } - GBA gba = new GBA(nextComm); + //GBA gba = new GBA(nextComm); + var gba = new GarboDev.GbaManager(nextComm); gba.Load(rom.RomData, gbabios); nextEmulator = gba; }