//#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; } } }