diff options
| author | degasus <wickmarkus@web.de> | 2013-03-12 11:28:56 +0100 |
|---|---|---|
| committer | degasus <wickmarkus@web.de> | 2013-03-12 11:28:56 +0100 |
| commit | 382be2aabdf0635c67f9ec945283f77f8ed9b0c9 (patch) | |
| tree | 8df3cf8a3539e4b4b1a96772f17c353f99393ed5 /Source/Core | |
| parent | e1ca002937cf8915ab9fb04f82e2dc5e68db1fa2 (diff) | |
| parent | 83fc5f4747f9c8c6066bf002730827ecc17458aa (diff) | |
Merge branch 'master' into GLSL-master
Conflicts:
.gitignore
Diffstat (limited to 'Source/Core')
52 files changed, 830 insertions, 415 deletions
diff --git a/Source/Core/Common/Src/ArmEmitter.cpp b/Source/Core/Common/Src/ArmEmitter.cpp index a56724b78b..c6fd34f55c 100644 --- a/Source/Core/Common/Src/ArmEmitter.cpp +++ b/Source/Core/Common/Src/ArmEmitter.cpp @@ -83,65 +83,104 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated) } } -void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize) +void ARMXEmitter::MOVI2F(ARMReg dest, float val, ARMReg tempReg) +{ + union {float f; u32 u;} conv; + conv.f = val; + MOVI2R(tempReg, conv.u); + VMOV(dest, tempReg); + // TODO: VMOV an IMM directly if possible + // Otherwise, use a literal pool and VLDR directly (+- 1020) +} + +void ARMXEmitter::ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch) { Operand2 op2; bool inverse; - if (!optimize) - { - // Only used in backpatch atm - // Only support ARMv7 right now - if (cpu_info.bArmV7) { - MOVW(reg, val & 0xFFFF); - MOVT(reg, val, true); + if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + if (!inverse) { + AND(rd, rs, op2); + } else { + BIC(rd, rs, op2); } - else + } else { + MOVI2R(scratch, val); + AND(rd, rs, scratch); + } +} + +void ARMXEmitter::ORI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch) +{ + Operand2 op2; + if (TryMakeOperand2(val, op2)) { + ORR(rd, rs, op2); + } else { + MOVI2R(scratch, val); + ORR(rd, rs, scratch); + } +} + +void ARMXEmitter::FlushLitPool() +{ + for(std::vector<LiteralPool>::iterator it = currentLitPool.begin(); it != currentLitPool.end(); ++it) { + // Search for duplicates + for(std::vector<LiteralPool>::iterator old_it = currentLitPool.begin(); old_it != it; ++old_it) { + if ((*old_it).val == (*it).val) + (*it).loc = (*old_it).loc; + } + + // Write the constant to Literal Pool + if (!(*it).loc) { - // ARMv6 version won't use backpatch for now - // Run again with optimizations - MOVI2R(reg, val); + (*it).loc = (s32)code; + Write32((*it).val); } - } else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { - if (!inverse) - MOV(reg, op2); - else - MVN(reg, op2); + s32 offset = (*it).loc - (s32)(*it).ldr_address - 8; + + // Backpatch the LDR + *(u32*)(*it).ldr_address |= (offset >= 0) << 23 | abs(offset); + } + // TODO: Save a copy of previous pools in case they are still in range. + currentLitPool.clear(); +} + +void ARMXEmitter::AddNewLit(u32 val) +{ + LiteralPool pool_item; + pool_item.loc = 0; + pool_item.val = val; + pool_item.ldr_address = code; + currentLitPool.push_back(pool_item); +} + +void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize) +{ + Operand2 op2; + bool inverse; + + if (cpu_info.bArmV7 && !optimize) + { + // For backpatching on ARMv7 + MOVW(reg, val & 0xFFFF); + MOVT(reg, val, true); + } + else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + inverse ? MVN(reg, op2) : MOV(reg, op2); } else { - if (cpu_info.bArmV7) { - // ARMv7 - can use MOVT/MOVW, best choice + if (cpu_info.bArmV7) + { + // Use MOVW+MOVT for ARMv7+ MOVW(reg, val & 0xFFFF); if(val & 0xFFFF0000) MOVT(reg, val, true); } else { - // ARMv6 - fallback sequence. - // TODO: Optimize further. Can for example choose negation etc. - // Literal pools is another way to do this but much more complicated - // so I can't really be bothered for an outdated CPU architecture like ARMv6. - bool first = true; - int shift = 16; - for (int i = 0; i < 4; i++) { - if (val & 0xFF) { - if (first) { - MOV(reg, Operand2((u8)val, (u8)(shift & 0xF))); - first = false; - } else { - ORR(reg, reg, Operand2((u8)val, (u8)(shift & 0xF))); - } - } - shift -= 4; - val >>= 8; - } + // Use literal pool for ARMv6. + AddNewLit(val); + LDRLIT(reg, 0, 0); // To be backpatched later } } } -// Moves IMM to memory location -void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val) -{ - // This moves imm to a memory location - MOVW(R14, val); MOVT(R14, val, true); - MOVW(R12, op); MOVT(R12, op, true); - STR(R12, R14); // R10 is what we want to store -} + void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) { MOVI2R(reg, (u32)(func)); BL(reg); @@ -151,6 +190,9 @@ void ARMXEmitter::SetCodePtr(u8 *ptr) { code = ptr; startcode = code; +#ifdef IOS + lastCacheFlushEnd = ptr; +#endif } const u8 *ARMXEmitter::GetCodePtr() const @@ -194,10 +236,11 @@ void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end) #elif defined(BLACKBERRY) msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE); #elif defined(IOS) - sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); + if (start != NULL) + sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); #elif !defined(_WIN32) #ifndef ANDROID - start = startcode; + start = startcode; // Should be Linux Only #endif __builtin___clear_cache(start, end); #endif @@ -474,6 +517,7 @@ void ARMXEmitter::LSL (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedData void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, true, dest, src, op2);} void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);} void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, true, dest, src, op2);} +void ARMXEmitter::LSR (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(3, false, dest, src, op2);} void ARMXEmitter::MUL (ARMReg dest, ARMReg src, ARMReg op2) { Write32(condition | (dest << 16) | (src << 8) | (9 << 4) | op2); @@ -497,10 +541,38 @@ void ARMXEmitter::SMULL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) Write4OpMultiply(0xC, destLo, destHi, rn, rm); } +void ARMXEmitter::UMLAL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) +{ + Write4OpMultiply(0xA, destLo, destHi, rn, rm); +} + +void ARMXEmitter::SMLAL(ARMReg destLo, ARMReg destHi, ARMReg rm, ARMReg rn) +{ + Write4OpMultiply(0xE, destLo, destHi, rn, rm); +} + +void ARMXEmitter::UBFX(ARMReg dest, ARMReg rn, u8 lsb, u8 width) +{ + Write32(condition | (0x7E0 << 16) | ((width - 1) << 16) | (dest << 12) | (lsb << 7) | (5 << 4) | rn); +} + +void ARMXEmitter::CLZ(ARMReg rd, ARMReg rm) +{ + Write32(condition | (0x16F << 16) | (rd << 12) | (0xF1 << 4) | rm); +} + +void ARMXEmitter::BFI(ARMReg rd, ARMReg rn, u8 lsb, u8 width) +{ + u32 msb = (lsb + width - 1); + if (msb > 31) msb = 31; + Write32(condition | (0x7C0 << 16) | (msb << 16) | (rd << 12) | (lsb << 7) | (1 << 4) | rn); +} + void ARMXEmitter::SXTB (ARMReg dest, ARMReg op2) { Write32(condition | (0x6AF << 16) | (dest << 12) | (7 << 4) | op2); } + void ARMXEmitter::SXTH (ARMReg dest, ARMReg op2, u8 rotation) { SXTAH(dest, (ARMReg)15, op2, rotation); @@ -511,9 +583,13 @@ void ARMXEmitter::SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation) // information Write32(condition | (0x6B << 20) | (src << 16) | (dest << 12) | (rotation << 10) | (7 << 4) | op2); } -void ARMXEmitter::REV (ARMReg dest, ARMReg src ) +void ARMXEmitter::RBIT(ARMReg dest, ARMReg src) { - Write32(condition | (107 << 20) | (15 << 16) | (dest << 12) | (243 << 4) | src); + Write32(condition | (0x6F << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src); +} +void ARMXEmitter::REV (ARMReg dest, ARMReg src) +{ + Write32(condition | (0x6B << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src); } void ARMXEmitter::REV16(ARMReg dest, ARMReg src) { @@ -533,27 +609,46 @@ void ARMXEmitter::MRS (ARMReg dest) Write32(condition | (16 << 20) | (15 << 16) | (dest << 12)); } -void ARMXEmitter::WriteStoreOp(u32 op, ARMReg dest, ARMReg src, s16 op2) +void ARMXEmitter::WriteStoreOp(u32 op, ARMReg src, ARMReg dest, s16 op2) +{ + // Qualcomm chipsets get /really/ angry if you don't use index, even if the offset is zero. + // bool Index = op2 != 0 ? true : false; + bool Index = true; + bool Add = op2 >= 0 ? true : false; + u32 imm = abs(op2); + Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (src << 16) | (dest << 12) | imm); +} +void ARMXEmitter::STR (ARMReg result, ARMReg base, s16 op) { WriteStoreOp(0x40, base, result, op);} +void ARMXEmitter::STRH (ARMReg result, ARMReg base, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x04 << 20) | (base << 16) | (result << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F)); +} +void ARMXEmitter::STRB (ARMReg result, ARMReg base, s16 op) { WriteStoreOp(0x44, base, result, op);} +void ARMXEmitter::STR (ARMReg result, ARMReg base, Operand2 op2, bool Index, bool Add) +{ + Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | op2.IMMSR()); +} +void ARMXEmitter::STR (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | offset); +} +void ARMXEmitter::STRH (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add) { - bool Index = op2 != 0 ? true : false; - bool Add = op2 >= 0 ? true : false; - u32 imm = abs(op2); - Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (src << 12) | imm); + Write32(condition | (0x00 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | (0xB << 4) | offset); } -void ARMXEmitter::STR (ARMReg dest, ARMReg src, s16 op) { WriteStoreOp(0x40, dest, src, op);} -void ARMXEmitter::STRB(ARMReg dest, ARMReg src, s16 op) { WriteStoreOp(0x44, dest, src, op);} -void ARMXEmitter::STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +void ARMXEmitter::STRB (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add) { - Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | offset); + Write32(condition | (0x64 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | offset); } void ARMXEmitter::LDREX(ARMReg dest, ARMReg base) { Write32(condition | (25 << 20) | (base << 16) | (dest << 12) | 0xF9F); } -void ARMXEmitter::STREX(ARMReg dest, ARMReg base, ARMReg op) +void ARMXEmitter::STREX(ARMReg result, ARMReg base, ARMReg op) { - _assert_msg_(DYNA_REC, (dest != base && dest != op), "STREX dest can't be other two registers"); - Write32(condition | (24 << 20) | (base << 16) | (dest << 12) | (0xF9 << 4) | op); + _assert_msg_(DYNA_REC, (result != base && result != op), "STREX dest can't be other two registers"); + Write32(condition | (24 << 20) | (base << 16) | (result << 12) | (0xF9 << 4) | op); } void ARMXEmitter::DMB () { @@ -570,12 +665,44 @@ void ARMXEmitter::LDRH(ARMReg dest, ARMReg src, Operand2 op) u8 Imm = op.Imm8(); Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F)); } +void ARMXEmitter::LDRSH(ARMReg dest, ARMReg src, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xF << 4) | (Imm & 0x0F)); +} void ARMXEmitter::LDRB(ARMReg dest, ARMReg src, s16 op) { WriteStoreOp(0x45, src, dest, op);} +void ARMXEmitter::LDRSB(ARMReg dest, ARMReg src, Operand2 op) +{ + u8 Imm = op.Imm8(); + Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xD << 4) | (Imm & 0x0F)); +} -void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +void ARMXEmitter::LDR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add) +{ + Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | op2.IMMSR()); +} +void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) { Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset); } +void ARMXEmitter::LDRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xB << 4) | offset); +} +void ARMXEmitter::LDRSH(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xF << 4) | offset); +} +void ARMXEmitter::LDRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x65 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset); +} +void ARMXEmitter::LDRSB(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) +{ + Write32(condition | (0x01 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | (0xD << 4) | offset); +} +void ARMXEmitter::LDRLIT (ARMReg dest, u32 offset, bool Add) { Write32(condition | 0x05 << 24 | Add << 23 | 0x1F << 16 | dest << 12 | offset);} + void ARMXEmitter::WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList) { Write32(condition | (op << 20) | (WriteBack << 21) | (dest << 16) | RegList); @@ -671,9 +798,8 @@ void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset) _assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VLDR: Offset needs to be word aligned and small enough"); - if (imm & 0xC03) { + if (imm & 0xC03) ERROR_LOG(DYNA_REC, "VLDR: Bad offset %08x", imm); - } bool single_reg = Dest < D0; @@ -681,13 +807,13 @@ void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset) if (single_reg) { - Write32(NO_COND | (0xD << 24) | (Add << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \ + Write32(condition | (0xD << 24) | (Add << 23) | ((Dest & 0x1) << 22) | (1 << 20) | (Base << 16) \ | ((Dest & 0x1E) << 11) | (10 << 8) | (imm >> 2)); } else { - Write32(NO_COND | (0xD << 24) | (Add << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \ + Write32(condition | (0xD << 24) | (Add << 23) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \ | ((Dest & 0xF) << 12) | (11 << 8) | (imm >> 2)); } } @@ -701,9 +827,8 @@ void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, s16 offset) _assert_msg_(DYNA_REC, (imm & 0xC03) == 0, "VSTR: Offset needs to be word aligned and small enough"); - if (imm & 0xC03) { + if (imm & 0xC03) ERROR_LOG(DYNA_REC, "VSTR: Bad offset %08x", imm); - } bool single_reg = Src < D0; @@ -711,17 +836,16 @@ void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, s16 offset) if (single_reg) { - Write32(NO_COND | (0xD << 24) | (Add << 23) | ((Src & 0x1) << 22) | (Base << 16) \ + Write32(condition | (0xD << 24) | (Add << 23) | ((Src & 0x1) << 22) | (Base << 16) \ | ((Src & 0x1E) << 11) | (10 << 8) | (imm >> 2)); - } else { - Write32(NO_COND | (0xD << 24) | (Add << 23) | ((Src & 0x10) << 18) | (Base << 16) \ + Write32(condition | (0xD << 24) | (Add << 23) | ((Src & 0x10) << 18) | (Base << 16) \ | ((Src & 0xF) << 12) | (11 << 8) | (imm >> 2)); } } -void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm) +void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm, bool E) { _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP"); bool single_reg = Vd < D0; @@ -731,16 +855,16 @@ void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \ - | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \ + | (E << 7) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \ - | (0x2F << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF)); + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \ + | (E << 7) | (0x2C << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF)); } } -void ARMXEmitter::VCMP(ARMReg Vd) +void ARMXEmitter::VCMP(ARMReg Vd, bool E) { _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP"); bool single_reg = Vd < D0; @@ -749,15 +873,26 @@ void ARMXEmitter::VCMP(ARMReg Vd) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \ - | (0x2B << 6)); + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \ + | (E << 7) | (0x29 << 6)); } else { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \ - | (0x2F << 6)); + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \ + | (E << 7) | (0x2C << 6)); } } + +void ARMXEmitter::VMRS_APSR() { + Write32(condition | 0xEF10A10 | (15 << 12)); +} +void ARMXEmitter::VMRS(ARMReg Rt) { + Write32(condition | (0xEF << 20) | (1 << 16) | (Rt << 12) | 0xA10); +} +void ARMXEmitter::VMSR(ARMReg Rt) { + Write32(condition | (0xEE << 20) | (1 << 16) | (Rt << 12) | 0xA10); +} + void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm) { _assert_msg_(DYNA_REC, Vd < Q0, "Pased invalid dest register to VSQRT"); @@ -771,13 +906,13 @@ void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | ((Vn & 0x1E) << 15) \ + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | ((Vn & 0x1E) << 15) \ | ((Vd & 0x1E) << 11) | (0xA << 8) | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) \ | (Vm >> 1)); } else { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \ + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | ((Vn & 0xF) << 16) \ | ((Vd & 0xF) << 12) | (0xB << 8) | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) \ | (Vm & 0xF)); } @@ -793,47 +928,17 @@ void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ | ((Vd & 0x1E) << 11) | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ | ((Vd & 0xF) << 12) | (0x2F << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } } // VFP and ASIMD -void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm) -{ - _assert_msg_(DYNA_REC, Vd < Q0, "VABS doesn't currently support Quad reg"); - _assert_msg_(DYNA_REC, Vd >= S0, "VABS doesn't support ARM Regs"); - bool single_reg = Vd < D0; - bool double_reg = Vd < Q0; - - Vd = SubBase(Vd); - Vm = SubBase(Vm); - - if (single_reg) - { - Write32(NO_COND | (0xEB << 20) | ((Vd & 0x1) << 22) | ((Vd & 0x1E) << 11) \ - | (0xAC << 4) | ((Vm & 0x1) << 5) | (Vm >> 1)); - } - else - { - if (double_reg) - { - Write32(NO_COND | (0xEB << 20) | ((Vd & 0x10) << 18) | ((Vd & 0xF) << 12) \ - | (0xBC << 4) | ((Vm & 0x10) << 1) | (Vm & 0xF)); - } - else - { - _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VADD with Quad Reg without support!"); - // XXX: TODO - } - } -} - void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) { _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD"); @@ -848,7 +953,7 @@ void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); } @@ -856,7 +961,7 @@ void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm) { if (double_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } @@ -883,7 +988,7 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x3 << 20) \ | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ | ((Vn & 0x1) << 7) | (1 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); } @@ -891,7 +996,7 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) { if (double_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x3 << 20) \ | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ | ((Vn & 0x10) << 3) | (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } @@ -904,6 +1009,7 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm) } } } +// VFP and ASIMD void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm) { _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD"); @@ -918,7 +1024,7 @@ void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm) if (single_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x2 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x2 << 20) \ | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); } @@ -926,46 +1032,87 @@ void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm) { if (double_reg) { - Write32(NO_COND | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x2 << 20) \ + Write32(condition | (0x1C << 23) | ((Vd & 0x10) << 18) | (0x2 << 20) \ | ((Vn & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \ | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } else { _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VMUL with Quad Reg without support!"); - // XXX: TODO } } } -void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm) +void ARMXEmitter::VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm) { - _assert_msg_(DYNA_REC, Vd < Q0, "VNEG doesn't currently support Quad reg"); - _assert_msg_(DYNA_REC, Vd >= S0, "VNEG doesn't support ARM Regs"); + _assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VMLA"); + _assert_msg_(DYNA_REC, Vn >= S0, "Passed invalid Vn to VMLA"); + _assert_msg_(DYNA_REC, Vm >= S0, "Passed invalid Vm to VMLA"); bool single_reg = Vd < D0; bool double_reg = Vd < Q0; Vd = SubBase(Vd); + Vn = SubBase(Vn); Vm = SubBase(Vm); if (single_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ - | ((Vd & 0x1E) << 11) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + Write32(condition | (0x1C << 23) | ((Vd & 0x1) << 22) | (0x0 << 20) \ + | ((Vn & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \ + | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + _assert_msg_(DYNA_REC, false, "VMLA: Please implement!"); + } +} + +void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm) +{ + _assert_msg_(DYNA_REC, Vd < Q0, "VABS doesn't currently support Quad reg"); + _assert_msg_(DYNA_REC, Vd >= S0, "VABS doesn't support ARM Regs"); + bool single_reg = Vd < D0; + bool double_reg = Vd < Q0; + + Vd = SubBase(Vd); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(condition | (0xEB << 20) | ((Vd & 0x1) << 22) | ((Vd & 0x1E) << 11) \ + | (0xAC << 4) | ((Vm & 0x1) << 5) | (Vm >> 1)); } else { if (double_reg) { - Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ - | ((Vd & 0xF) << 12) | (0x2D << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); + Write32(condition | (0xEB << 20) | ((Vd & 0x10) << 18) | ((Vd & 0xF) << 12) \ + | (0xBC << 4) | ((Vm & 0x10) << 1) | (Vm & 0xF)); } else { - _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VNEG with Quad Reg without support!"); + _assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VADD with Quad Reg without support!"); // XXX: TODO - } + } +} + +void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm) +{ + bool single_reg = Vd < D0; + + Vd = SubBase(Vd); + Vm = SubBase(Vm); + + if (single_reg) + { + Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ + | ((Vd & 0x1E) << 11) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); + } + else + { + Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ + | ((Vd & 0xF) << 12) | (0x2D << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); } } @@ -976,7 +1123,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high) Dest = SubBase(Dest); - Write32(NO_COND | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \ + Write32(condition | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \ | (11 << 8) | ((Dest & 0x10) << 3) | (1 << 4)); } @@ -990,7 +1137,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) { // Moving to a Neon register FROM ARM Reg Dest = (ARMReg)(Dest - S0); - Write32(NO_COND | (0xE0 << 20) | ((Dest & 0x1E) << 15) | (Src << 12) \ + Write32(condition | (0xE0 << 20) | ((Dest & 0x1E) << 15) | (Src << 12) \ | (0xA << 8) | ((Dest & 0x1) << 7) | (1 << 4)); return; } @@ -1010,7 +1157,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) { // Moving to ARM Reg from Neon Register Src = (ARMReg)(Src - S0); - Write32(NO_COND | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \ + Write32(condition | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \ | (0xA << 8) | ((Src & 0x1) << 7) | (1 << 4)); return; } @@ -1040,7 +1187,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) if (Single) { - Write32(NO_COND | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \ + Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \ | (0x5 << 9) | (1 << 6) | ((Src & 0x1) << 5) | ((Src & 0x1E) >> 1)); } else @@ -1057,10 +1204,28 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) } else { - Write32(NO_COND | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x3 << 20) | ((Dest & 0xF) << 12) \ + Write32(condition | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x3 << 20) | ((Dest & 0xF) << 12) \ | (0x2D << 6) | ((Src & 0x10) << 1) | (Src & 0xF)); } } } +void ARMXEmitter::VCVT(ARMReg Dest, ARMReg Source, int flags) +{ + bool single_reg = (Dest < D0) && (Source < D0); + int op = ((flags & TO_INT) ? (flags & ROUND_TO_ZERO) : (flags & IS_SIGNED)) ? 1 : 0; + int op2 = ((flags & TO_INT) ? (flags & IS_SIGNED) : 0) ? 1 : 0; + Dest = SubBase(Dest); + Source = SubBase(Source); + + if (single_reg) + { + Write32(condition | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \ + | ((Dest & 0x1E) << 11) | (op << 7) | (0x29 << 6) | ((Source & 0x1) << 5) | (Source >> 1)); + } else { + Write32(condition | (0x1D << 23) | ((Dest & 0x10) << 18) | (0x7 << 19) | ((flags & TO_INT) << 18) | (op2 << 16) \ + | ((Dest & 0xF) << 12) | (1 << 8) | (op << 7) | (0x29 << 6) | ((Source & 0x10) << 1) | (Source & 0xF)); + } +} + } diff --git a/Source/Core/Common/Src/ArmEmitter.h b/Source/Core/Common/Src/ArmEmitter.h index d3155285f3..20d867fa39 100644 --- a/Source/Core/Common/Src/ArmEmitter.h +++ b/Source/Core/Common/Src/ArmEmitter.h @@ -25,6 +25,7 @@ #if defined(__SYMBIAN32__) || defined(PANDORA) #include <signal.h> #endif +#include <vector> #undef _IP #undef R0 @@ -32,6 +33,12 @@ #undef _LR #undef _PC +// VCVT flags +#define TO_FLOAT 0 +#define TO_INT 1 << 0 +#define IS_SIGNED 1 << 1 +#define ROUND_TO_ZERO 1 << 2 + namespace ArmGen { enum ARMReg @@ -171,7 +178,7 @@ public: Value = base; } - Operand2(u8 shift, ShiftType type, ARMReg base)// For IMM shifted register + Operand2(ARMReg base, ShiftType type, u8 shift)// For IMM shifted register { if(shift == 32) shift = 0; switch (type) @@ -327,6 +334,13 @@ struct FixupBranch int type; //0 = B 1 = BL }; +struct LiteralPool +{ + s32 loc; + u8* ldr_address; + u32 val; +}; + typedef const u8* JumpTarget; class ARMXEmitter @@ -336,8 +350,9 @@ private: u8 *code, *startcode; u8 *lastCacheFlushEnd; u32 condition; + std::vector<LiteralPool> currentLitPool; - void WriteStoreOp(u32 op, ARMReg dest, ARMReg src, s16 op2); + void WriteStoreOp(u32 op, ARMReg src, ARMReg dest, s16 op2); void WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList); void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, ARMReg op2); void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2); @@ -373,6 +388,10 @@ public: void FlushIcacheSection(u8 *start, u8 *end); u8 *GetWritableCodePtr(); + void FlushLitPool(); + void AddNewLit(u32 val); + + CCFlags GetCC() { return CCFlags(condition >> 28); } void SetCC(CCFlags cond = CC_AL); // Special purpose instructions @@ -425,8 +444,10 @@ public: void LSL (ARMReg dest, ARMReg src, ARMReg op2); void LSLS(ARMReg dest, ARMReg src, Operand2 op2); void LSLS(ARMReg dest, ARMReg src, ARMReg op2); + void LSR (ARMReg dest, ARMReg src, Operand2 op2); void SBC (ARMReg dest, ARMReg src, Operand2 op2); void SBCS(ARMReg dest, ARMReg src, Operand2 op2); + void RBIT(ARMReg dest, ARMReg src); void REV (ARMReg dest, ARMReg src); void REV16 (ARMReg dest, ARMReg src); void RSC (ARMReg dest, ARMReg src, Operand2 op2); @@ -457,33 +478,53 @@ public: void UMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); void SMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void UMLAL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void SMLAL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); + void SXTB(ARMReg dest, ARMReg op2); void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0); void SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation = 0); + void BFI(ARMReg rd, ARMReg rn, u8 lsb, u8 width); + void UBFX(ARMReg dest, ARMReg op2, u8 lsb, u8 width); + void CLZ(ARMReg rd, ARMReg rm); + // Using just MSR here messes with our defines on the PPC side of stuff (when this code was in dolphin...) // Just need to put an underscore here, bit annoying. void _MSR (bool nzcvq, bool g, Operand2 op2); - void _MSR (bool nzcvq, bool g, ARMReg src ); + void _MSR (bool nzcvq, bool g, ARMReg src); void MRS (ARMReg dest); // Memory load/store operations - void LDR (ARMReg dest, ARMReg src, s16 op2 = 0); + void LDR (ARMReg dest, ARMReg src, s16 op2 = 0); + void LDRH (ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDRSH(ARMReg dest, ARMReg src, Operand2 op2 = 0); + void LDRB (ARMReg dest, ARMReg src, s16 op2 = 0); + void LDRSB(ARMReg dest, ARMReg src, Operand2 op2 = 0); // Offset adds to the base register in LDR - void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); - void LDRH(ARMReg dest, ARMReg src, Operand2 op = 0); - void LDRB(ARMReg dest, ARMReg src, s16 op2 = 0); - void STR (ARMReg dest, ARMReg src, s16 op2 = 0); + void LDR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add); + void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRSH(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRSB(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void LDRLIT(ARMReg dest, u32 offset, bool Add); + + void STR (ARMReg result, ARMReg base, s16 op2 = 0); + void STRH (ARMReg result, ARMReg base, Operand2 op2 = 0); + void STRB (ARMReg result, ARMReg base, s16 op2 = 0); // Offset adds on to the destination register in STR - void STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add); + void STR (ARMReg result, ARMReg base, Operand2 op2, bool Index, bool Add); + void STR (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add); + void STRH (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add); + void STRB (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add); - void STRB(ARMReg dest, ARMReg src, s16 op2 = 0); void STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...); void LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...); // Exclusive Access operations void LDREX(ARMReg dest, ARMReg base); - // dest contains the result if the instruction managed to store the value - void STREX(ARMReg dest, ARMReg base, ARMReg op); + // result contains the result if the instruction managed to store the value + void STREX(ARMReg result, ARMReg base, ARMReg op); void DMB (); void SVC(Operand2 op); @@ -501,9 +542,9 @@ public: // VFP Only void VLDR(ARMReg Dest, ARMReg Base, s16 offset); void VSTR(ARMReg Src, ARMReg Base, s16 offset); - void VCMP(ARMReg Vd, ARMReg Vm); + void VCMP(ARMReg Vd, ARMReg Vm, bool E); // Compares against zero - void VCMP(ARMReg Vd); + void VCMP(ARMReg Vd, bool E); void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VSQRT(ARMReg Vd, ARMReg Vm); @@ -513,13 +554,25 @@ public: void VABS(ARMReg Vd, ARMReg Vm); void VNEG(ARMReg Vd, ARMReg Vm); void VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm); + void VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VMOV(ARMReg Dest, ARMReg Src, bool high); void VMOV(ARMReg Dest, ARMReg Src); + void VCVT(ARMReg Dest, ARMReg Src, int flags); + + void VMRS_APSR(); + void VMRS(ARMReg Rt); + void VMSR(ARMReg Rt); void QuickCallFunction(ARMReg scratchreg, void *func); - // Utility functions + + // Wrapper around MOVT/MOVW with fallbacks. void MOVI2R(ARMReg reg, u32 val, bool optimize = true); - void ARMABI_MOVI2M(Operand2 op, Operand2 val); + void MOVI2F(ARMReg dest, float val, ARMReg tempReg); + + void ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch); + void ORI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch); + + }; // class ARMXEmitter @@ -556,7 +609,9 @@ public: // Call this when shutting down. Don't rely on the destructor, even though it'll do the job. void FreeCodeSpace() { +#ifndef __SYMBIAN32__ FreeMemoryPages(region, region_size); +#endif region = NULL; region_size = 0; } diff --git a/Source/Core/Core/Src/BootManager.cpp b/Source/Core/Core/Src/BootManager.cpp index 1e9e612fe0..88e0cebcb6 100644 --- a/Source/Core/Core/Src/BootManager.cpp +++ b/Source/Core/Core/Src/BootManager.cpp @@ -55,7 +55,7 @@ namespace BootManager struct ConfigCache { bool valid, bCPUThread, bSkipIdle, bEnableFPRF, bMMU, bDCBZOFF, - bVBeam, bFastDiscSpeed, bMergeBlocks, bDSPHLE, bHLE_BS2; + bVBeam, bSyncGPU, bFastDiscSpeed, bMergeBlocks, bDSPHLE, bHLE_BS2; int iTLBHack, iCPUCore; std::string strBackend; }; @@ -95,6 +95,7 @@ bool BootCore(const std::string& _rFilename) config_cache.bDCBZOFF = StartUp.bDCBZOFF; config_cache.iTLBHack = StartUp.iTLBHack; config_cache.bVBeam = StartUp.bVBeam; + config_cache.bSyncGPU = StartUp.bSyncGPU; config_cache.bFastDiscSpeed = StartUp.bFastDiscSpeed; config_cache.bMergeBlocks = StartUp.bMergeBlocks; config_cache.bDSPHLE = StartUp.bDSPHLE; @@ -109,6 +110,7 @@ bool BootCore(const std::string& _rFilename) game_ini.Get("Core", "TLBHack", &StartUp.iTLBHack, StartUp.iTLBHack); game_ini.Get("Core", "DCBZ", &StartUp.bDCBZOFF, StartUp.bDCBZOFF); game_ini.Get("Core", "VBeam", &StartUp.bVBeam, StartUp.bVBeam); + game_ini.Get("Core", "SyncGPU", &StartUp.bSyncGPU, StartUp.bSyncGPU); game_ini.Get("Core", "FastDiscSpeed", &StartUp.bFastDiscSpeed, StartUp.bFastDiscSpeed); game_ini.Get("Core", "BlockMerging", &StartUp.bMergeBlocks, StartUp.bMergeBlocks); game_ini.Get("Core", "DSPHLE", &StartUp.bDSPHLE, StartUp.bDSPHLE); @@ -168,6 +170,7 @@ void Stop() StartUp.bDCBZOFF = config_cache.bDCBZOFF; StartUp.iTLBHack = config_cache.iTLBHack; StartUp.bVBeam = config_cache.bVBeam; + StartUp.bSyncGPU = config_cache.bSyncGPU; StartUp.bFastDiscSpeed = config_cache.bFastDiscSpeed; StartUp.bMergeBlocks = config_cache.bMergeBlocks; StartUp.bDSPHLE = config_cache.bDSPHLE; diff --git a/Source/Core/Core/Src/ConfigManager.cpp b/Source/Core/Core/Src/ConfigManager.cpp index 8b6fec58e2..ab0fef3498 100644 --- a/Source/Core/Core/Src/ConfigManager.cpp +++ b/Source/Core/Core/Src/ConfigManager.cpp @@ -404,6 +404,7 @@ void SConfig::LoadSettings() ini.Get("Core", "MMU", &m_LocalCoreStartupParameter.bMMU, false); ini.Get("Core", "TLBHack", &m_LocalCoreStartupParameter.iTLBHack, 0); ini.Get("Core", "VBeam", &m_LocalCoreStartupParameter.bVBeam, false); + ini.Get("Core", "SyncGPU", &m_LocalCoreStartupParameter.bSyncGPU, false); ini.Get("Core", "FastDiscSpeed", &m_LocalCoreStartupParameter.bFastDiscSpeed, false); ini.Get("Core", "DCBZ", &m_LocalCoreStartupParameter.bDCBZOFF, false); ini.Get("Core", "FrameLimit", &m_Framelimit, 1); // auto frame limit by default @@ -413,7 +414,7 @@ void SConfig::LoadSettings() ini.Get("Core", "GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, ""); // Movie - ini.Get("General", "PauseMovie", &m_PauseMovie, false); + ini.Get("Movie", "PauseMovie", &m_PauseMovie, false); ini.Get("Movie", "Author", &m_strMovieAuthor, ""); // DSP diff --git a/Source/Core/Core/Src/CoreParameter.cpp b/Source/Core/Core/Src/CoreParameter.cpp index a7a44be5ea..0a6bb795d5 100644 --- a/Source/Core/Core/Src/CoreParameter.cpp +++ b/Source/Core/Core/Src/CoreParameter.cpp @@ -50,7 +50,7 @@ SCoreStartupParameter::SCoreStartupParameter() bDPL2Decoder(false), iLatency(14), bRunCompareServer(false), bRunCompareClient(false), bMMU(false), bDCBZOFF(false), iTLBHack(0), bVBeam(false), - bFastDiscSpeed(false), + bSyncGPU(false), bFastDiscSpeed(false), SelectedLanguage(0), bWii(false), bConfirmStop(false), bHideCursor(false), bAutoHideCursor(false), bUsePanicHandlers(true), bOnScreenDisplayMessages(true), @@ -78,6 +78,7 @@ void SCoreStartupParameter::LoadDefaults() bDCBZOFF = false; iTLBHack = 0; bVBeam = false; + bSyncGPU = false; bFastDiscSpeed = false; bMergeBlocks = false; SelectedLanguage = 0; diff --git a/Source/Core/Core/Src/CoreParameter.h b/Source/Core/Core/Src/CoreParameter.h index 55d20b7b1b..896f111ab7 100644 --- a/Source/Core/Core/Src/CoreParameter.h +++ b/Source/Core/Core/Src/CoreParameter.h @@ -116,6 +116,7 @@ struct SCoreStartupParameter bool bDCBZOFF; int iTLBHack; bool bVBeam; + bool bSyncGPU; bool bFastDiscSpeed; int SelectedLanguage; diff --git a/Source/Core/Core/Src/HW/AudioInterface.cpp b/Source/Core/Core/Src/HW/AudioInterface.cpp index 0a33e7c773..15e1f0bb29 100644 --- a/Source/Core/Core/Src/HW/AudioInterface.cpp +++ b/Source/Core/Core/Src/HW/AudioInterface.cpp @@ -145,6 +145,8 @@ static void GenerateAudioInterrupt(); static void UpdateInterrupts(); static void IncreaseSampleCount(const u32 _uAmount); void ReadStreamBlock(s16* _pPCM); +u64 GetAIPeriod(); +int et_AI; void Init() { @@ -159,6 +161,8 @@ void Init() g_AISSampleRate = 48000; g_AIDSampleRate = 32000; + + et_AI = CoreTiming::RegisterEvent("AICallback", Update); } void Shutdown() @@ -178,11 +182,8 @@ void Read32(u32& _rReturnValue, const u32 _Address) break; case AI_SAMPLE_COUNTER: + Update(0, 0); _rReturnValue = m_SampleCounter; - // HACK - AI SRC init will do while (oldval == sample_counter) {} - // in order to pass this, we need to increment the counter whenever read - if (m_Control.PSTAT) - m_SampleCounter++; break; case AI_INTERRUPT_TIMING: @@ -236,6 +237,9 @@ void Write32(const u32 _Value, const u32 _Address) // Tell Drive Interface to start/stop streaming DVDInterface::g_bStream = tmpAICtrl.PSTAT; + + CoreTiming::RemoveEvent(et_AI); + CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI); } // AI Interrupt @@ -271,6 +275,8 @@ void Write32(const u32 _Value, const u32 _Address) case AI_INTERRUPT_TIMING: m_InterruptTiming = _Value; + CoreTiming::RemoveEvent(et_AI); + CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI); DEBUG_LOG(AUDIO_INTERFACE, "Set interrupt: %08x samples", m_InterruptTiming); break; @@ -448,7 +454,7 @@ unsigned int GetAIDSampleRate() return g_AIDSampleRate; } -void Update() +void Update(u64 userdata, int cyclesLate) { if (m_Control.PSTAT) { @@ -458,8 +464,17 @@ void Update() const u32 Samples = static_cast<u32>(Diff / g_CPUCyclesPerSample); g_LastCPUTime += Samples * g_CPUCyclesPerSample; IncreaseSampleCount(Samples); - } + } + CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2) - cyclesLate, et_AI); } } +u64 GetAIPeriod() +{ + u64 period = g_CPUCyclesPerSample * m_InterruptTiming; + if (period == 0) + period = 32000 * g_CPUCyclesPerSample; + return period; +} + } // end of namespace AudioInterface diff --git a/Source/Core/Core/Src/HW/AudioInterface.h b/Source/Core/Core/Src/HW/AudioInterface.h index 1504198621..06fd3aec79 100644 --- a/Source/Core/Core/Src/HW/AudioInterface.h +++ b/Source/Core/Core/Src/HW/AudioInterface.h @@ -31,7 +31,7 @@ void Init(); void Shutdown(); void DoState(PointerWrap &p); -void Update(); +void Update(u64 userdata, int cyclesLate); // Called by DSP emulator void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate); diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index 5945cb7141..fa1d23f186 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -436,10 +436,6 @@ void Write16(const u16 _Value, const u32 _Address) if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0; if (tmpControl.DSP) g_dspState.DSPControl.DSP = 0; - // Tracking DMAState fixes Knockout Kings 2003 in DSP HLE mode - if (GetDSPEmulator()->IsLLE()) - g_dspState.DSPControl.DMAState = 0; // keep g_ARAM DMA State zero - // unknown g_dspState.DSPControl.unk3 = tmpControl.unk3; g_dspState.DSPControl.pad = tmpControl.pad; @@ -691,7 +687,7 @@ void UpdateAudioDMA() { // Send silence. Yeah, it's a bit of a waste to sample rate convert // silence. or hm. Maybe we shouldn't do this :) - //dsp_emulator->DSP_SendAIBuffer(0, AudioInterface::GetAIDSampleRate()); + dsp_emulator->DSP_SendAIBuffer(0, AudioInterface::GetAIDSampleRate()); } } @@ -701,7 +697,10 @@ void Do_ARAM_DMA() if (!GetDSPEmulator()->IsLLE()) g_dspState.DSPControl.DMAState = 1; - GenerateDSPInterrupt(INT_ARAM, true); + if (g_arDMA.Cnt.dir || g_arDMA.Cnt.count > 10240) + CoreTiming::ScheduleEvent_Threadsafe(0, et_GenerateDSPInterrupt, INT_ARAM | (1<<16)); + else + GenerateDSPInterrupt(INT_ARAM); // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks if (g_arDMA.Cnt.dir) diff --git a/Source/Core/Core/Src/HW/DVDInterface.cpp b/Source/Core/Core/Src/HW/DVDInterface.cpp index 214fb5dc02..57c85a82ed 100644 --- a/Source/Core/Core/Src/HW/DVDInterface.cpp +++ b/Source/Core/Core/Src/HW/DVDInterface.cpp @@ -32,11 +32,7 @@ #include "../Movie.h" // Disc transfer rate measured in bytes per second -static const u32 DISC_TRANSFER_RATE_GC = 3125 * 1024; -static const u32 DISC_TRANSFER_RATE_WII = 7926 * 1024; - -// Disc access time measured in milliseconds -static const u32 DISC_ACCESS_TIME_MS = 128; +static const u32 DISC_TRANSFER_RATE_GC = 4 * 1024 * 1024; namespace DVDInterface { @@ -501,8 +497,7 @@ void Write32(const u32 _iValue, const u32 _iAddress) if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) { u64 ticksUntilTC = m_DILENGTH.Length * - (SystemTimers::GetTicksPerSecond() / (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii?DISC_TRANSFER_RATE_WII:DISC_TRANSFER_RATE_GC)) + - (SystemTimers::GetTicksPerSecond() * DISC_ACCESS_TIME_MS / 1000); + (SystemTimers::GetTicksPerSecond() / (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii ? 1 : DISC_TRANSFER_RATE_GC)); CoreTiming::ScheduleEvent((int)ticksUntilTC, tc); } else diff --git a/Source/Core/Core/Src/HW/EXI_Channel.cpp b/Source/Core/Core/Src/HW/EXI_Channel.cpp index b38009f0b1..f31fc59b3f 100644 --- a/Source/Core/Core/Src/HW/EXI_Channel.cpp +++ b/Source/Core/Core/Src/HW/EXI_Channel.cpp @@ -25,9 +25,9 @@ #define EXI_WRITE 1 #define EXI_READWRITE 2 - #include "ProcessorInterface.h" #include "../PowerPC/PowerPC.h" +#include "CoreTiming.h" CEXIChannel::CEXIChannel(u32 ChannelId) : m_DMAMemoryAddress(0), @@ -45,6 +45,8 @@ CEXIChannel::CEXIChannel(u32 ChannelId) : for (int i = 0; i < NUM_DEVICES; i++) m_pDevices[i] = EXIDevice_Create(EXIDEVICE_NONE, m_ChannelId); + + updateInterrupts = CoreTiming::RegisterEvent("EXIInterrupt", UpdateInterrupts); } CEXIChannel::~CEXIChannel() @@ -88,12 +90,12 @@ void CEXIChannel::AddDevice(IEXIDevice* pDevice, const int device_num, bool noti if (m_ChannelId != 2) { m_Status.EXTINT = 1; - UpdateInterrupts(); + CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); } } } -void CEXIChannel::UpdateInterrupts() +void CEXIChannel::UpdateInterrupts(u64 userdata, int cyclesLate) { ExpansionInterface::UpdateInterrupts(); } @@ -149,7 +151,9 @@ void CEXIChannel::Read32(u32& _uReturnValue, const u32 _iRegister) if (m_ChannelId == 2) m_Status.EXT = 0; else + { m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0; + } _uReturnValue = m_Status.Hex; break; @@ -213,7 +217,7 @@ void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) if (pDevice != NULL) pDevice->SetCS(m_Status.CHIP_SELECT); - UpdateInterrupts(); + CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); } break; @@ -264,7 +268,7 @@ void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) if(!m_Control.TSTART) // completed ! { m_Status.TCINT = 1; - UpdateInterrupts(); + CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0); } } break; diff --git a/Source/Core/Core/Src/HW/EXI_Channel.h b/Source/Core/Core/Src/HW/EXI_Channel.h index ef6c0a4e0f..dfbd8558f9 100644 --- a/Source/Core/Core/Src/HW/EXI_Channel.h +++ b/Source/Core/Core/Src/HW/EXI_Channel.h @@ -110,6 +110,9 @@ private: // Since channels operate a bit differently from each other u32 m_ChannelId; + int updateInterrupts; + + static void UpdateInterrupts(u64 userdata, int cyclesLate); public: // get device IEXIDevice* GetDevice(const u8 _CHIP_SELECT); @@ -129,7 +132,6 @@ public: void Update(); bool IsCausingInterrupt(); - void UpdateInterrupts(); void DoState(PointerWrap &p); void PauseAndLock(bool doLock, bool unpauseOnUnlock); diff --git a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp index c157e55d1b..8307c8c3bc 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp @@ -47,6 +47,14 @@ void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate) pThis->Flush(); } +void CEXIMemoryCard::CmdDoneCallback(u64 userdata, int cyclesLate) +{ + int card_index = (int)userdata; + CEXIMemoryCard* pThis = (CEXIMemoryCard*)ExpansionInterface::FindDevice(EXIDEVICE_MEMORYCARD, card_index); + if (pThis) + pThis->CmdDone(); +} + CEXIMemoryCard::CEXIMemoryCard(const int index) : card_index(index) , m_bDirty(false) @@ -56,7 +64,8 @@ CEXIMemoryCard::CEXIMemoryCard(const int index) m_strFilename = "Movie.raw"; // we're potentially leaking events here, since there's no UnregisterEvent until emu shutdown, but I guess it's inconsequential - et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardA" : "memcardB", FlushCallback); + et_this_card = CoreTiming::RegisterEvent((card_index == 0) ? "memcardFlushA" : "memcardFlushB", FlushCallback); + et_cmd_done = CoreTiming::RegisterEvent((card_index == 0) ? "memcardDoneA" : "memcardDoneB", CmdDoneCallback); interruptSwitch = 0; m_bInterruptSet = 0; @@ -178,6 +187,21 @@ bool CEXIMemoryCard::IsPresent() return true; } +void CEXIMemoryCard::CmdDone() +{ + status |= MC_STATUS_READY; + status &= ~MC_STATUS_BUSY; + + m_bInterruptSet = 1; + m_bDirty = true; +} + +void CEXIMemoryCard::CmdDoneLater(u64 cycles) +{ + CoreTiming::RemoveEvent(et_cmd_done); + CoreTiming::ScheduleEvent(cycles, et_cmd_done, (u64)card_index); +} + void CEXIMemoryCard::SetCS(int cs) { // So that memory card won't be invalidated during flushing @@ -201,11 +225,7 @@ void CEXIMemoryCard::SetCS(int cs) //??? - status |= MC_STATUS_READY; - status &= ~MC_STATUS_BUSY; - - m_bInterruptSet = 1; - m_bDirty = true; + CmdDoneLater(5000); } break; @@ -232,11 +252,7 @@ void CEXIMemoryCard::SetCS(int cs) address = (address & ~0x1FF) | ((address+1) & 0x1FF); } - status |= MC_STATUS_READY; - status &= ~MC_STATUS_BUSY; - - m_bInterruptSet = 1; - m_bDirty = true; + CmdDoneLater(5000); } // Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec) diff --git a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h index dfdc6349aa..33b94ca530 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.h @@ -47,9 +47,18 @@ private: // through the userdata parameter, so that it can then call Flush on the right card. static void FlushCallback(u64 userdata, int cyclesLate); + // Scheduled when a command that required delayed end signaling is done. + static void CmdDoneCallback(u64 userdata, int cyclesLate); + // Flushes the memory card contents to disk. void Flush(bool exiting = false); + // Signals that the command that was previously executed is now done. + void CmdDone(); + + // Variant of CmdDone which schedules an event later in the future to complete the command. + void CmdDoneLater(u64 cycles); + enum { cmdNintendoID = 0x00, @@ -71,7 +80,7 @@ private: std::string m_strFilename; int card_index; - int et_this_card; + int et_this_card, et_cmd_done; //! memory card state // STATE_TO_SAVE diff --git a/Source/Core/Core/Src/HW/GPFifo.cpp b/Source/Core/Core/Src/HW/GPFifo.cpp index 3af387792f..63fff83db6 100644 --- a/Source/Core/Core/Src/HW/GPFifo.cpp +++ b/Source/Core/Core/Src/HW/GPFifo.cpp @@ -98,12 +98,17 @@ void STACKALIGN CheckGatherPipe() memmove(m_gatherPipe, m_gatherPipe + cnt, m_gatherPipeCount); // Profile where the FIFO writes are occurring. - if (jit && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end())) + if (jit && PC != 0 && (jit->js.fifoWriteAddresses.find(PC)) == (jit->js.fifoWriteAddresses.end())) { - jit->js.fifoWriteAddresses.insert(PC); + // Log only stores, fp stores and ps stores, filtering out other instructions arrived via optimizeGatherPipe + int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type; + if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_PS && GetOpInfo(Memory::ReadUnchecked_U32(PC))->opname=="psq_st")) + { + jit->js.fifoWriteAddresses.insert(PC); - // Invalidate the JIT block so that it gets recompiled with the external exception check included. - jit->GetBlockCache()->InvalidateICache(PC, 4); + // Invalidate the JIT block so that it gets recompiled with the external exception check included. + jit->GetBlockCache()->InvalidateICache(PC, 4); + } } } } diff --git a/Source/Core/Core/Src/HW/ProcessorInterface.cpp b/Source/Core/Core/Src/HW/ProcessorInterface.cpp index 9e72cc2169..d4142efaae 100644 --- a/Source/Core/Core/Src/HW/ProcessorInterface.cpp +++ b/Source/Core/Core/Src/HW/ProcessorInterface.cpp @@ -99,7 +99,7 @@ void Init() m_ResetCode = 0x80000000; // Cold reset m_InterruptCause = INT_CAUSE_RST_BUTTON | INT_CAUSE_VI; - toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", &ToggleResetButtonCallback); + toggleResetButton = CoreTiming::RegisterEvent("ToggleResetButton", ToggleResetButtonCallback); } void Read16(u16& _uReturnValue, const u32 _iAddress) diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp index 8c4c562b82..f6af63da53 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp +++ b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp @@ -69,6 +69,19 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength) *(u32*)&_pBuffer[0] = SI_GC_CONTROLLER; break; + case CMD_DIRECT: + { + INFO_LOG(SERIALINTERFACE, "PAD - Direct (Length: %d)", _iLength); + u32 high, low; + GetData(high, low); + for (int i = 0; i < (_iLength - 1) / 2; i++) + { + _pBuffer[0 + i] = (high >> (i * 8)) & 0xff; + _pBuffer[4 + i] = (low >> (i * 8)) & 0xff; + } + } + break; + case CMD_ORIGIN: { INFO_LOG(SERIALINTERFACE, "PAD - Get Origin"); @@ -96,7 +109,7 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength) default: { ERROR_LOG(SERIALINTERFACE, "unknown SI command (0x%x)", command); - PanicAlert("SI: Unknown command"); + PanicAlert("SI: Unknown command (0x%x)", command); } break; } diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCController.h b/Source/Core/Core/Src/HW/SI_DeviceGCController.h index 058dde3582..b89f433331 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCController.h +++ b/Source/Core/Core/Src/HW/SI_DeviceGCController.h @@ -31,6 +31,7 @@ private: enum EBufferCommands { CMD_RESET = 0x00, + CMD_DIRECT = 0x40, CMD_ORIGIN = 0x41, CMD_RECALIBRATE = 0x42, }; diff --git a/Source/Core/Core/Src/HW/SystemTimers.cpp b/Source/Core/Core/Src/HW/SystemTimers.cpp index a041a3b0ad..27ba4a62f9 100644 --- a/Source/Core/Core/Src/HW/SystemTimers.cpp +++ b/Source/Core/Core/Src/HW/SystemTimers.cpp @@ -74,6 +74,7 @@ IPC_HLE_PERIOD: For the Wiimote this is the call schedule: #include "Thread.h" #include "Timer.h" #include "VideoBackendBase.h" +#include "CommandProcessor.h" namespace SystemTimers @@ -109,7 +110,7 @@ enum int et_Dec; int et_VI; int et_SI; -int et_AI; +int et_CP; int et_AudioDMA; int et_DSP; int et_IPC_HLE; @@ -118,15 +119,15 @@ int et_PatchEngine; // PatchEngine updates every 1/60th of a second by default // These are badly educated guesses // Feel free to experiment. Set these in Init below. int - // This one should simply be determined by the increasing counter in AI. - AI_PERIOD, - // These shouldn't be period controlled either, most likely. DSP_PERIOD, // This is a fixed value, don't change it AUDIO_DMA_PERIOD, + // Regulates the speed of the Command Processor + CP_PERIOD, + // This is completely arbitrary. If we find that we need lower latency, we can just // increase this number. IPC_HLE_PERIOD; @@ -143,12 +144,6 @@ u32 ConvertMillisecondsToTicks(u32 _Milliseconds) return GetTicksPerSecond() / 1000 * _Milliseconds; } -void AICallback(u64 userdata, int cyclesLate) -{ - AudioInterface::Update(); - CoreTiming::ScheduleEvent(AI_PERIOD - cyclesLate, et_AI); -} - // DSP/CPU timeslicing. void DSPCallback(u64 userdata, int cyclesLate) { @@ -187,6 +182,12 @@ void SICallback(u64 userdata, int cyclesLate) CoreTiming::ScheduleEvent(SerialInterface::GetTicksToNextSIPoll() - cyclesLate, et_SI); } +void CPCallback(u64 userdata, int cyclesLate) +{ + CommandProcessor::Update(); + CoreTiming::ScheduleEvent(CP_PERIOD - cyclesLate, et_CP); +} + void DecrementerCallback(u64 userdata, int cyclesLate) { PowerPC::ppcState.spr[SPR_DEC] = 0xFFFFFFFF; @@ -266,12 +267,12 @@ void Init() if (DSP::GetDSPEmulator()->IsLLE()) DSP_PERIOD = 12000; // TO BE TWEAKED - // This is the biggest question mark. - AI_PERIOD = GetTicksPerSecond() / 80; - // System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA AUDIO_DMA_PERIOD = CPU_CORE_CLOCK / (AudioInterface::GetAIDSampleRate() * 4 / 32); + // Emulated gekko <-> flipper bus speed ratio (cpu clock / flipper clock) + CP_PERIOD = GetTicksPerSecond() / 10000; + Common::Timer::IncreaseResolution(); // store and convert localtime at boot to timebase ticks CoreTiming::SetFakeTBStartValue((u64)(CPU_CORE_CLOCK / TIMER_RATIO) * (u64)CEXIIPL::GetGCTime()); @@ -281,19 +282,21 @@ void Init() CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks()); et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback); - et_AI = CoreTiming::RegisterEvent("AICallback", AICallback); et_VI = CoreTiming::RegisterEvent("VICallback", VICallback); et_SI = CoreTiming::RegisterEvent("SICallback", SICallback); + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSyncGPU) + et_CP = CoreTiming::RegisterEvent("CPCallback", CPCallback); et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback); et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback); et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback); et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback); - CoreTiming::ScheduleEvent(AI_PERIOD, et_AI); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine(), et_VI); CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_SI); CoreTiming::ScheduleEvent(AUDIO_DMA_PERIOD, et_AudioDMA); + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSyncGPU) + CoreTiming::ScheduleEvent(CP_PERIOD, et_CP); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_PatchEngine); diff --git a/Source/Core/Core/Src/Movie.cpp b/Source/Core/Core/Src/Movie.cpp index 6c646f6870..e72163cddb 100644 --- a/Source/Core/Core/Src/Movie.cpp +++ b/Source/Core/Core/Src/Movie.cpp @@ -1043,7 +1043,7 @@ bool PlayWiimote(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf, if (size != sizeInMovie) { - PanicAlertT("Fatal desync. Aborting playback. (Error in PlayWiimote: %u != %u, byte %u.)%s", (u32)sizeInMovie, (u32)size, (u32)g_currentByte, (g_numPads & 0xF)?" Try re-creating the recording with all GameCube controllers disabled (in Configure > Gamecube > Device Settings).":""); + PanicAlertT("Fatal desync. Aborting playback. (Error in PlayWiimote: %u != %u, byte %u.)%s", (u32)sizeInMovie, (u32)size, (u32)g_currentByte, (g_numPads & 0xF)?" Try re-creating the recording with all GameCube controllers disabled (in Configure > Gamecube > Device Settings), or restarting Dolphin (Dolphin currently must be restarted every time before playing back a wiimote movie).":""); EndPlayInput(!g_bReadOnly); return false; } diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp index ff65f52471..7847496b6e 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp @@ -193,6 +193,7 @@ void Interpreter::mtmsr(UGeckoInstruction _inst) { // Privileged? MSR = m_GPR[_inst.RS]; + PowerPC::CheckExceptions(); m_EndBlock = true; } diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp index 710984571e..be4789e233 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp @@ -291,7 +291,9 @@ static void ImHere() void Jit64::Cleanup() { if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) + { ABI_CallFunction((void *)&GPFifo::CheckGatherPipe); + } // SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time. if (MMCR0.Hex || MMCR1.Hex) @@ -301,7 +303,8 @@ void Jit64::Cleanup() void Jit64::WriteExit(u32 destination, int exit_num) { Cleanup(); - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); + + SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); //If nobody has taken care of this yet (this can be removed when all branches are done) JitBlock *b = js.curBlock; @@ -561,6 +564,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32) { js.fifoBytesThisBlock -= 32; + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write ABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0)); } diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp index 7c1d60ad09..29d17dc875 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp @@ -302,6 +302,7 @@ void Jit64::stX(UGeckoInstruction inst) addr += offset; if ((addr & 0xFFFFF000) == 0xCC008000 && jo.optimizeGatherPipe) { + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write gpr.FlushLockX(ABI_PARAM1); MOV(32, R(ABI_PARAM1), gpr.R(s)); if (update) @@ -329,6 +330,7 @@ void Jit64::stX(UGeckoInstruction inst) } else { + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write switch (accessSize) { case 32: ABI_CallFunctionAC(thunks.ProtectFunction(true ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), 2), gpr.R(s), addr); break; diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp index 924758ab3a..a6f57796e4 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp @@ -42,6 +42,7 @@ const u8 GC_ALIGNED16(pbswapShuffle2x4[16]) = {3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, #if 0 static void WriteDual32(u64 value, u32 address) { + MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write Memory::Write_U32((u32)(value >> 32), address); Memory::Write_U32((u32)value, address + 4); } diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp index 0b6c0462d9..7c3766c69a 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp @@ -28,6 +28,7 @@ #include "Jit.h" #include "JitRegCache.h" +#include "HW/ProcessorInterface.h" void Jit64::mtspr(UGeckoInstruction inst) { @@ -122,7 +123,29 @@ void Jit64::mtmsr(UGeckoInstruction inst) gpr.UnlockAll(); gpr.Flush(FLUSH_ALL); fpr.Flush(FLUSH_ALL); + + // If some exceptions are pending and EE are now enabled, force checking + // external exceptions when going out of mtmsr in order to execute delayed + // interrupts as soon as possible. + TEST(32, M(&MSR), Imm32(0x8000)); + FixupBranch eeDisabled = J_CC(CC_Z); + + TEST(32, M((void*)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER)); + FixupBranch noExceptionsPending = J_CC(CC_Z); + + // Check if a CP interrupt is waiting and keep the GPU emulation in sync (issue 4336) + TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP)); + FixupBranch cpInt = J_CC(CC_NZ); + + MOV(32, M(&PC), Imm32(js.compilerPC + 4)); + WriteExternalExceptionExit(); + + SetJumpTarget(cpInt); + SetJumpTarget(noExceptionsPending); + SetJumpTarget(eeDisabled); + WriteExit(js.compilerPC + 4, 0); + js.firstFPInstructionFound = false; } diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h index 77f7d6977b..7fabd946f9 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h @@ -276,8 +276,8 @@ public: InstLoc EmitLoadMSR() { return FoldZeroOp(LoadMSR, 0); } - InstLoc EmitStoreMSR(InstLoc val) { - return FoldUOp(StoreMSR, val); + InstLoc EmitStoreMSR(InstLoc val, InstLoc pc) { + return FoldBiOp(StoreMSR, val, pc); } InstLoc EmitStoreFPRF(InstLoc value) { return FoldUOp(StoreFPRF, value); diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp index 3638ba8da6..7c12d613a3 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp @@ -994,8 +994,26 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak break; } case StoreMSR: { + unsigned InstLoc = ibuild->GetImmValue(getOp2(I)); regStoreInstToConstLoc(RI, 32, getOp1(I), &MSR); regNormalRegClear(RI, I); + + // If some exceptions are pending and EE are now enabled, force checking + // external exceptions when going out of mtmsr in order to execute delayed + // interrupts as soon as possible. + Jit->MOV(32, R(EAX), M(&MSR)); + Jit->TEST(32, R(EAX), Imm32(0x8000)); + FixupBranch eeDisabled = Jit->J_CC(CC_Z); + + Jit->MOV(32, R(EAX), M((void*)&PowerPC::ppcState.Exceptions)); + Jit->TEST(32, R(EAX), R(EAX)); + FixupBranch noExceptionsPending = Jit->J_CC(CC_Z); + + Jit->MOV(32, M(&PC), Imm32(InstLoc + 4)); + Jit->WriteExceptionExit(); // TODO: Implement WriteExternalExceptionExit for JitIL + + Jit->SetJumpTarget(eeDisabled); + Jit->SetJumpTarget(noExceptionsPending); break; } case StoreGQR: { diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp index dc21228858..a60becfad9 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp @@ -389,7 +389,9 @@ static void ImHere() void JitIL::Cleanup() { if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) + { ABI_CallFunction((void *)&GPFifo::CheckGatherPipe); + } // SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time. if (MMCR0.Hex || MMCR1.Hex) diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp index 70a6ea4843..4a1b26a1d6 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_SystemRegisters.cpp @@ -106,7 +106,7 @@ void JitIL::mfspr(UGeckoInstruction inst) // -------------- void JitIL::mtmsr(UGeckoInstruction inst) { - ibuild.EmitStoreMSR(ibuild.EmitLoadGReg(inst.RS)); + ibuild.EmitStoreMSR(ibuild.EmitLoadGReg(inst.RS), ibuild.EmitIntConst(js.compilerPC)); ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4)); } // ============== diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp index 8748ebac2d..2c25268c23 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/Jit.cpp @@ -148,20 +148,20 @@ void JitArm::DoDownCount() if(js.downcountAmount < 255) // We can enlarge this if we used rotations { SUBS(rB, rB, js.downcountAmount); - STR(rA, rB); + STR(rB, rA); } else { ARMReg rC = gpr.GetReg(false); MOVI2R(rC, js.downcountAmount); SUBS(rB, rB, rC); - STR(rA, rB); + STR(rB, rA); } gpr.Unlock(rA, rB); } void JitArm::WriteExitDestInR(ARMReg Reg) { - STR(R9, Reg, PPCSTATE_OFF(pc)); + STR(Reg, R9, PPCSTATE_OFF(pc)); Cleanup(); DoDownCount(); MOVI2R(Reg, (u32)asm_routines.dispatcher); @@ -170,7 +170,7 @@ void JitArm::WriteExitDestInR(ARMReg Reg) } void JitArm::WriteRfiExitDestInR(ARMReg Reg) { - STR(R9, Reg, PPCSTATE_OFF(pc)); + STR(Reg, R9, PPCSTATE_OFF(pc)); Cleanup(); DoDownCount(); @@ -209,7 +209,7 @@ void JitArm::WriteExit(u32 destination, int exit_num) { ARMReg A = gpr.GetReg(false); MOVI2R(A, destination); - STR(R9, A, PPCSTATE_OFF(pc)); + STR(A, R9, PPCSTATE_OFF(pc)); MOVI2R(A, (u32)asm_routines.dispatcher); B(A); } @@ -361,8 +361,9 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo // Downcount flag check, Only valid for linked blocks { FixupBranch skip = B_CC(CC_PL); - ARMABI_MOVI2M((u32)&PC, js.blockStart); ARMReg rA = gpr.GetReg(false); + MOVI2R(rA, js.blockStart); + STR(rA, R9, PPCSTATE_OFF(pc)); MOVI2R(rA, (u32)asm_routines.doTiming); B(rA); SetJumpTarget(skip); @@ -384,7 +385,7 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo LDR(A, R9, PPCSTATE_OFF(msr)); TST(A, Shift); FixupBranch b1 = B_CC(CC_NEQ); - STR(R9, C, PPCSTATE_OFF(pc)); + STR(C, R9, PPCSTATE_OFF(pc)); MOVI2R(A, (u32)asm_routines.fpException); B(A); SetJumpTarget(b1); @@ -397,7 +398,7 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo MOVI2R(rA, (u32)&b->runCount); // Load in to register LDR(rB, rA); // Load the actual value in to R11. ADD(rB, rB, 1); // Add one to the value - STR(rA, rB); // Now store it back in the memory location + STR(rB, rA); // Now store it back in the memory location // get start tic PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStart); gpr.Unlock(rA, rB); @@ -461,7 +462,7 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo MOVI2R(RA, (u32)&opinfo->runCount); LDR(RB, RA); ADD(RB, RB, 1); - STR(RA, RB); + STR(RB, RA); gpr.Unlock(RA, RB); } if (!ops[i].skip) diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp index f95c2e58e6..1706efafb0 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArmCache.cpp @@ -23,6 +23,7 @@ // locating performance issues. #include "../JitInterface.h" +#include "Jit.h" #include "JitArmCache.h" @@ -38,7 +39,7 @@ using namespace ArmGen; ARMXEmitter emit((u8 *)location); emit.MOVI2R(R11, address); emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher); - emit.STR(R9, R11, PPCSTATE_OFF(pc)); + emit.STR(R11, R9, PPCSTATE_OFF(pc)); emit.B(R12); } diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp index 3aa972248d..5a1a4659a6 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Branch.cpp @@ -48,11 +48,12 @@ void JitArm::sc(UGeckoInstruction inst) gpr.Flush(); fpr.Flush(); - ARMABI_MOVI2M((u32)&PC, js.compilerPC + 4); // Destroys R12 and R14 ARMReg rA = gpr.GetReg(); + MOVI2R(rA, js.compilerPC + 4); + STR(rA, R9, PPCSTATE_OFF(pc)); LDR(rA, R9, PPCSTATE_OFF(Exceptions)); ORR(rA, rA, EXCEPTION_SYSCALL); - STR(R9, rA, PPCSTATE_OFF(Exceptions)); + STR(rA, R9, PPCSTATE_OFF(Exceptions)); gpr.Unlock(rA); WriteExceptionExit(); @@ -84,14 +85,14 @@ void JitArm::rfi(UGeckoInstruction inst) LDR(rD, R9, PPCSTATE_OFF(msr)); AND(rD, rD, rB); // rD = Masked MSR - STR(R9, rD, PPCSTATE_OFF(msr)); + STR(rD, R9, PPCSTATE_OFF(msr)); LDR(rB, R9, PPCSTATE_OFF(spr[SPR_SRR1])); // rB contains SRR1 here AND(rB, rB, rC); // rB contains masked SRR1 here ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1 - STR(R9, rB, PPCSTATE_OFF(msr)); // STR rB in to rA + STR(rB, R9, PPCSTATE_OFF(msr)); // STR rB in to rA LDR(rA, R9, PPCSTATE_OFF(spr[SPR_SRR0])); @@ -117,7 +118,7 @@ void JitArm::bx(UGeckoInstruction inst) ARMReg rA = gpr.GetReg(false); u32 Jumpto = js.compilerPC + 4; MOVI2R(rA, Jumpto); - STR(R9, rA, PPCSTATE_OFF(spr[SPR_LR])); + STR(rA, R9, PPCSTATE_OFF(spr[SPR_LR])); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } // If this is not the last instruction of a block, @@ -168,7 +169,7 @@ void JitArm::bcx(UGeckoInstruction inst) { LDR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); SUBS(rB, rB, 1); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_CTR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); //SUB(32, M(&CTR), Imm8(1)); if (inst.BO & BO_BRANCH_IF_CTR_0) @@ -193,7 +194,7 @@ void JitArm::bcx(UGeckoInstruction inst) { u32 Jumpto = js.compilerPC + 4; MOVI2R(rB, Jumpto); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_LR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_LR])); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); // Careful, destroys R14, R12 } gpr.Unlock(rA, rB); @@ -235,7 +236,7 @@ void JitArm::bcctrx(UGeckoInstruction inst) { u32 Jumpto = js.compilerPC + 4; MOVI2R(rA, Jumpto); - STR(R9, rA, PPCSTATE_OFF(spr[SPR_LR])); + STR(rA, R9, PPCSTATE_OFF(spr[SPR_LR])); // ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } MVN(rB, 0x3); // 0xFFFFFFFC @@ -269,7 +270,7 @@ void JitArm::bcctrx(UGeckoInstruction inst) if (inst.LK_3){ u32 Jumpto = js.compilerPC + 4; MOVI2R(rB, Jumpto); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_LR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_LR])); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR @@ -290,7 +291,7 @@ void JitArm::bclrx(UGeckoInstruction inst) ARMReg rA = gpr.GetReg(false); u32 Jumpto = js.compilerPC + 4; MOVI2R(rA, Jumpto); - STR(R9, rA, PPCSTATE_OFF(spr[SPR_LR])); + STR(rA, R9, PPCSTATE_OFF(spr[SPR_LR])); // ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } return; @@ -305,7 +306,7 @@ void JitArm::bclrx(UGeckoInstruction inst) { LDR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); SUBS(rB, rB, 1); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_CTR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); //SUB(32, M(&CTR), Imm8(1)); if (inst.BO & BO_BRANCH_IF_CTR_0) @@ -341,7 +342,7 @@ void JitArm::bclrx(UGeckoInstruction inst) if (inst.LK){ u32 Jumpto = js.compilerPC + 4; MOVI2R(rB, Jumpto); - STR(R9, rB, PPCSTATE_OFF(spr[SPR_LR])); + STR(rB, R9, PPCSTATE_OFF(spr[SPR_LR])); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); } gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp index 964bd575c4..f9da7180e3 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Integer.cpp @@ -40,7 +40,7 @@ void JitArm::GenerateRC(int cr) { SetCC(CC_MI); MOV(rB, 0x8); // Result < 0 SetCC(); - STRB(R9, rB, PPCSTATE_OFF(cr_fast) + cr); + STRB(rB, R9, PPCSTATE_OFF(cr_fast) + cr); gpr.Unlock(rB); } void JitArm::ComputeRC(int cr) { @@ -51,7 +51,7 @@ void JitArm::ComputeRC(int cr) { SetCC(CC_GT); MOV(rB, 0x4); // Result > 0 SetCC(); - STRB(R9, rB, PPCSTATE_OFF(cr_fast) + cr); + STRB(rB, R9, PPCSTATE_OFF(cr_fast) + cr); gpr.Unlock(rB); } @@ -232,7 +232,7 @@ void JitArm::cmpli(UGeckoInstruction inst) SetCC(CC_HI); MOV(rA, 0x4); // Result > 0 SetCC(); - STRB(R9, rA, PPCSTATE_OFF(cr_fast) + crf); + STRB(rA, R9, PPCSTATE_OFF(cr_fast) + crf); gpr.Unlock(rA); } @@ -269,7 +269,7 @@ void JitArm::rlwimix(UGeckoInstruction inst) ARMReg rB = gpr.GetReg(); MOVI2R(rA, mask); - Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it. + Operand2 Shift(RS, ST_ROR, 32 - inst.SH); // This rotates left, while ARM has only rotate right, so swap it. if (inst.Rc) { BIC (rB, RA, rA); // RA & ~mask @@ -296,7 +296,7 @@ void JitArm::rlwinmx(UGeckoInstruction inst) ARMReg rA = gpr.GetReg(); MOVI2R(rA, mask); - Operand2 Shift(32 - inst.SH, ST_ROR, RS); // This rotates left, while ARM has only rotate right, so swap it. + Operand2 Shift(RS, ST_ROR, 32 - inst.SH); // This rotates left, while ARM has only rotate right, so swap it. if (inst.Rc) { ANDS(RA, rA, Shift); diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp index 9d248ebd02..1617b0fef6 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp @@ -141,7 +141,7 @@ void JitArm::StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset) switch (accessSize) { case 32: - STR(dest, value); // 8 + STR(value, dest); // 8 break; case 16: // Not implemented diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp index bbad7b780a..7eb8963e15 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp @@ -70,7 +70,7 @@ void JitArm::mtspr(UGeckoInstruction inst) } // OK, this is easy. - STR(R9, RD, PPCSTATE_OFF(spr) + iIndex * 4); + STR(RD, R9, PPCSTATE_OFF(spr) + iIndex * 4); } void JitArm::mfspr(UGeckoInstruction inst) @@ -99,6 +99,6 @@ void JitArm::mtmsr(UGeckoInstruction inst) // Don't interpret this, if we do we get thrown out //JITDISABLE(SystemRegisters) - STR(R9, gpr.R(inst.RS), PPCSTATE_OFF(msr)); + STR(gpr.R(inst.RS), R9, PPCSTATE_OFF(msr)); WriteExit(js.compilerPC + 4, 0); } diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp index a5ae0a1e81..28263baa32 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp @@ -102,10 +102,10 @@ void JitArmAsmRoutineManager::Generate() fpException = GetCodePtr(); LDR(R0, R9, PPCSTATE_OFF(Exceptions)); ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE); - STR(R9, R0, PPCSTATE_OFF(Exceptions)); + STR(R0, R9, PPCSTATE_OFF(Exceptions)); QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); LDR(R0, R9, PPCSTATE_OFF(npc)); - STR(R9, R0, PPCSTATE_OFF(pc)); + STR(R0, R9, PPCSTATE_OFF(pc)); B(dispatcher); SetJumpTarget(bail); @@ -117,10 +117,10 @@ void JitArmAsmRoutineManager::Generate() // Does exception checking testExceptions = GetCodePtr(); LDR(R0, R9, PPCSTATE_OFF(pc)); - STR(R9, R0, PPCSTATE_OFF(npc)); + STR(R0, R9, PPCSTATE_OFF(npc)); QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); LDR(R0, R9, PPCSTATE_OFF(npc)); - STR(R9, R0, PPCSTATE_OFF(pc)); + STR(R0, R9, PPCSTATE_OFF(pc)); // Check the state pointer to see if we are exiting // Gets checked on every exception check MOVI2R(R0, (u32)PowerPC::GetStatePtr()); diff --git a/Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp b/Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp index 18777fd8c9..2a9aa2154f 100644 --- a/Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp +++ b/Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp @@ -148,7 +148,7 @@ ARMReg ArmRegCache::R(u32 preg) return ArmCRegs[a].Reg; } // Alright, we couldn't get a free space, dump that least used register - emit->STR(R9, ArmCRegs[Num].Reg, PPCSTATE_OFF(gpr) + ArmCRegs[Num].PPCReg * 4); + emit->STR(ArmCRegs[Num].Reg, R9, PPCSTATE_OFF(gpr) + ArmCRegs[Num].PPCReg * 4); emit->LDR(ArmCRegs[Num].Reg, R9, PPCSTATE_OFF(gpr) + preg * 4); ArmCRegs[Num].PPCReg = preg; ArmCRegs[Num].LastLoad = 0; @@ -160,7 +160,7 @@ void ArmRegCache::Flush() for(u8 a = 0; a < NUMPPCREG; ++a) if (ArmCRegs[a].PPCReg != 33) { - emit->STR(R9, ArmCRegs[a].Reg, PPCSTATE_OFF(gpr) + ArmCRegs[a].PPCReg * 4); + emit->STR(ArmCRegs[a].Reg, R9, PPCSTATE_OFF(gpr) + ArmCRegs[a].PPCReg * 4); ArmCRegs[a].PPCReg = 33; ArmCRegs[a].LastLoad = 0; } diff --git a/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp b/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp index 7f509dbf0e..8514450cb8 100644 --- a/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp +++ b/Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp @@ -32,6 +32,7 @@ #include "../../HW/GPFifo.h" #include "../../Core.h" #include "JitAsmCommon.h" +#include "JitBase.h" using namespace Gen; diff --git a/Source/Core/Core/Src/PowerPC/JitInterface.cpp b/Source/Core/Core/Src/PowerPC/JitInterface.cpp index 8fcdd9837f..2f56f013be 100644 --- a/Source/Core/Core/Src/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/Src/PowerPC/JitInterface.cpp @@ -81,6 +81,7 @@ namespace JitInterface default: { PanicAlert("Unrecognizable cpu_core: %d", core); + jit = NULL; return NULL; break; } diff --git a/Source/Core/Core/Src/PowerPC/JitInterface.h b/Source/Core/Core/Src/PowerPC/JitInterface.h index a01c3dfa6e..9213072ab9 100644 --- a/Source/Core/Core/Src/PowerPC/JitInterface.h +++ b/Source/Core/Core/Src/PowerPC/JitInterface.h @@ -51,6 +51,3 @@ namespace JitInterface extern bool bFakeVMEM; extern bool bMMU; -#ifdef _M_ARM -#include "JitArm32/Jit.h" -#endif diff --git a/Source/Core/Core/Src/PowerPC/PowerPC.cpp b/Source/Core/Core/Src/PowerPC/PowerPC.cpp index d4447331e7..e65b310f5a 100644 --- a/Source/Core/Core/Src/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/Src/PowerPC/PowerPC.cpp @@ -161,13 +161,18 @@ void Init(int cpu_core) switch (cpu_core) { - case 0: + case 0: { cpu_core_base = interpreter; break; } default: cpu_core_base = JitInterface::InitJitCore(cpu_core); + if (!cpu_core_base) // Handle Situations where JIT core isn't available + { + WARN_LOG(POWERPC, "Jit core %d not available. Defaulting to interpreter.", cpu_core); + cpu_core_base = interpreter; + } break; } @@ -213,6 +218,8 @@ void SetMode(CoreMode new_mode) case MODE_JIT: // Switching from interpreter to JIT. // Don't really need to do much. It'll work, the cache will refill itself. cpu_core_base = JitInterface::GetCore(); + if (!cpu_core_base) // Has a chance to not get a working JIT core if one isn't active on host + cpu_core_base = interpreter; break; } } diff --git a/Source/Core/DolphinWX/Src/ConfigMain.cpp b/Source/Core/DolphinWX/Src/ConfigMain.cpp index 329906a301..cbd780e6aa 100644 --- a/Source/Core/DolphinWX/Src/ConfigMain.cpp +++ b/Source/Core/DolphinWX/Src/ConfigMain.cpp @@ -875,7 +875,7 @@ void CConfigMain::OnOk(wxCommandEvent& WXUNUSED (event)) { Close(); - // Save the config. Dolphin crashes to often to save the settings on closing only + // Save the config. Dolphin crashes too often to only save the settings on closing SConfig::GetInstance().SaveSettings(); } diff --git a/Source/Core/DolphinWX/Src/ISOProperties.cpp b/Source/Core/DolphinWX/Src/ISOProperties.cpp index f6ca1cb851..0721f9ad4d 100644 --- a/Source/Core/DolphinWX/Src/ISOProperties.cpp +++ b/Source/Core/DolphinWX/Src/ISOProperties.cpp @@ -316,6 +316,8 @@ void CISOProperties::CreateGUIControls(bool IsWad) DCBZOFF->SetToolTip(_("Bypass the clearing of the data cache by the DCBZ instruction. Usually leave this option disabled.")); VBeam = new wxCheckBox(m_GameConfig, ID_VBEAM, _("Accurate VBeam emulation"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); VBeam->SetToolTip(_("If the FPS is erratic, this option may help. (ON = Compatible, OFF = Fast)")); + SyncGPU = new wxCheckBox(m_GameConfig, ID_SYNCGPU, _("Sychronise GPU thread"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); + SyncGPU->SetToolTip(_("Synchonises the GPU and CPU threads to help prevent random freezes in Dual Core mode. (ON = Compatible, OFF = Fast)")); FastDiscSpeed = new wxCheckBox(m_GameConfig, ID_DISCSPEED, _("Speed up Disc Transfer Rate"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); FastDiscSpeed->SetToolTip(_("Enable fast disc access. Needed for a few games. (ON = Fast, OFF = Compatible)")); BlockMerging = new wxCheckBox(m_GameConfig, ID_MERGEBLOCKS, _("Enable Block Merging"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); @@ -357,9 +359,10 @@ void CISOProperties::CreateGUIControls(bool IsWad) sbCoreOverrides->Add(CPUThread, 0, wxLEFT, 5); sbCoreOverrides->Add(SkipIdle, 0, wxLEFT, 5); sbCoreOverrides->Add(MMU, 0, wxLEFT, 5); - sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5); sbCoreOverrides->Add(TLBHack, 0, wxLEFT, 5); + sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5); sbCoreOverrides->Add(VBeam, 0, wxLEFT, 5); + sbCoreOverrides->Add(SyncGPU, 0, wxLEFT, 5); sbCoreOverrides->Add(FastDiscSpeed, 0, wxLEFT, 5); sbCoreOverrides->Add(BlockMerging, 0, wxLEFT, 5); sbCoreOverrides->Add(DSPHLE, 0, wxLEFT, 5); @@ -936,6 +939,11 @@ void CISOProperties::LoadGameConfig() else VBeam->Set3StateValue(wxCHK_UNDETERMINED); + if (GameIni.Get("Core", "SyncGPU", &bTemp)) + SyncGPU->Set3StateValue((wxCheckBoxState)bTemp); + else + SyncGPU->Set3StateValue(wxCHK_UNDETERMINED); + if (GameIni.Get("Core", "FastDiscSpeed", &bTemp)) FastDiscSpeed->Set3StateValue((wxCheckBoxState)bTemp); else @@ -1025,6 +1033,11 @@ bool CISOProperties::SaveGameConfig() else GameIni.Set("Core", "VBeam", VBeam->Get3StateValue()); + if (SyncGPU->Get3StateValue() == wxCHK_UNDETERMINED) + GameIni.DeleteKey("Core", "SyncGPU"); + else + GameIni.Set("Core", "SyncGPU", SyncGPU->Get3StateValue()); + if (FastDiscSpeed->Get3StateValue() == wxCHK_UNDETERMINED) GameIni.DeleteKey("Core", "FastDiscSpeed"); else diff --git a/Source/Core/DolphinWX/Src/ISOProperties.h b/Source/Core/DolphinWX/Src/ISOProperties.h index 499b242516..41e7d250a0 100644 --- a/Source/Core/DolphinWX/Src/ISOProperties.h +++ b/Source/Core/DolphinWX/Src/ISOProperties.h @@ -70,7 +70,7 @@ private: // Core wxCheckBox *CPUThread, *SkipIdle, *MMU, *DCBZOFF, *TLBHack; - wxCheckBox *VBeam, *FastDiscSpeed, *BlockMerging, *DSPHLE; + wxCheckBox *VBeam, *SyncGPU, *FastDiscSpeed, *BlockMerging, *DSPHLE; // Wii wxCheckBox *EnableWideScreen; // Video @@ -130,6 +130,7 @@ private: ID_DCBZOFF, ID_TLBHACK, ID_VBEAM, + ID_SYNCGPU, ID_DISCSPEED, ID_MERGEBLOCKS, ID_AUDIO_DSP_HLE, diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.cpp b/Source/Core/VideoCommon/Src/CommandProcessor.cpp index a4dddf537f..eb03730fc3 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.cpp +++ b/Source/Core/VideoCommon/Src/CommandProcessor.cpp @@ -32,6 +32,8 @@ #include "HW/GPFifo.h" #include "HW/Memmap.h" #include "DLCache.h" +#include "HW/SystemTimers.h" +#include "Core.h" namespace CommandProcessor { @@ -57,12 +59,15 @@ static bool bProcessFifoAllDistance = false; volatile bool isPossibleWaitingSetDrawDone = false; volatile bool isHiWatermarkActive = false; +volatile bool isLoWatermarkActive = false; volatile bool interruptSet= false; volatile bool interruptWaiting= false; volatile bool interruptTokenWaiting = false; volatile bool interruptFinishWaiting = false; volatile bool waitingForPEInterruptDisable = false; +volatile u32 VITicks = CommandProcessor::m_cpClockOrigin; + bool IsOnThread() { return SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread; @@ -88,6 +93,7 @@ void DoState(PointerWrap &p) p.Do(bProcessFifoToLoWatermark); p.Do(bProcessFifoAllDistance); p.Do(isHiWatermarkActive); + p.Do(isLoWatermarkActive); p.Do(isPossibleWaitingSetDrawDone); p.Do(interruptSet); p.Do(interruptWaiting); @@ -119,7 +125,7 @@ void Init() m_tokenReg = 0; memset(&fifo,0,sizeof(fifo)); - fifo.CPCmdIdle = 1 ; + fifo.CPCmdIdle = 1; fifo.CPReadIdle = 1; fifo.bFF_Breakpoint = 0; fifo.bFF_HiWatermark = 0; @@ -136,6 +142,7 @@ void Init() bProcessFifoAllDistance = false; isPossibleWaitingSetDrawDone = false; isHiWatermarkActive = false; + isLoWatermarkActive = false; et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); } @@ -294,7 +301,6 @@ void Read16(u16& _rReturnValue, const u32 _Address) void Write16(const u16 _Value, const u32 _Address) { - INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address); switch (_Address & 0xFFF) @@ -405,7 +411,8 @@ void Write16(const u16 _Value, const u32 _Address) { GPFifo::ResetGatherPipe(); ResetVideoBuffer(); - }else + } + else { ResetVideoBuffer(); } @@ -461,7 +468,7 @@ void STACKALIGN GatherPipeBursted() } if (IsOnThread()) - SetOverflowStatusFromGatherPipe(); + SetCpStatus(); // update the fifo-pointer if (fifo.CPWritePointer >= fifo.CPEnd) @@ -511,19 +518,6 @@ void AbortFrame() } -void SetOverflowStatusFromGatherPipe() -{ - fifo.bFF_HiWatermark = (fifo.CPReadWriteDistance > fifo.CPHiWatermark); - isHiWatermarkActive = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt && m_CPCtrlReg.GPReadEnable; - - if (isHiWatermarkActive) - { - interruptSet = true; - INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); - ProcessorInterface::SetInterrupt(INT_CAUSE_CP, true); - } -} - void SetCpStatus() { // overflow & underflow check @@ -531,30 +525,33 @@ void SetCpStatus() fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); // breakpoint - if (fifo.bFF_BPEnable) - { - if (fifo.CPBreakpoint == fifo.CPReadPointer) - { - if (!fifo.bFF_Breakpoint) + if (Core::IsGPUThread()) + { + if (fifo.bFF_BPEnable) + { + if (fifo.CPBreakpoint == fifo.CPReadPointer) { - INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer); - fifo.bFF_Breakpoint = true; - IncrementCheckContextId(); + if (!fifo.bFF_Breakpoint) + { + INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer); + fifo.bFF_Breakpoint = true; + IncrementCheckContextId(); + } } - } + else + { + if (fifo.bFF_Breakpoint) + INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer); + fifo.bFF_Breakpoint = false; + } + } else { if (fifo.bFF_Breakpoint) INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer); - fifo.bFF_Breakpoint = false; + fifo.bFF_Breakpoint = false; } - } - else - { - if (fifo.bFF_Breakpoint) - INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer); - fifo.bFF_Breakpoint = false; - } + } bool bpInt = fifo.bFF_Breakpoint && fifo.bFF_BPInt; bool ovfInt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt; @@ -562,17 +559,27 @@ void SetCpStatus() bool interrupt = (bpInt || ovfInt || undfInt) && m_CPCtrlReg.GPReadEnable; - isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable; + isHiWatermarkActive = ovfInt && m_CPCtrlReg.GPReadEnable; + isLoWatermarkActive = undfInt && m_CPCtrlReg.GPReadEnable; if (interrupt != interruptSet && !interruptWaiting) { u64 userdata = interrupt?1:0; if (IsOnThread()) { - if(!interrupt || bpInt || undfInt) + if(!interrupt || bpInt || undfInt || ovfInt) { - interruptWaiting = true; - CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); + if (Core::IsGPUThread()) + { + interruptWaiting = true; + CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); + } + else if (Core::IsCPUThread()) + { + interruptSet = interrupt; + INFO_LOG(COMMANDPROCESSOR,"Interrupt set"); + ProcessorInterface::SetInterrupt(INT_CAUSE_CP, interrupt); + } } } else @@ -596,7 +603,7 @@ void ProcessFifoAllDistance() if (IsOnThread()) { while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && - fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) + fifo.CPReadWriteDistance && !AtBreakpoint()) Common::YieldCPU(); } bProcessFifoAllDistance = false; @@ -617,15 +624,11 @@ void SetCpStatusRegister() { // Here always there is one fifo attached to the GPU m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; - m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; - m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance; + m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || (fifo.CPReadPointer == fifo.CPWritePointer); + m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || !fifo.bFF_GPReadEnable; m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; - // HACK to compensate for slow response to PE interrupts in Time Splitters: Future Perfect - if (IsOnThread()) - PixelEngine::ResumeWaitingForPEInterrupt(); - INFO_LOG(COMMANDPROCESSOR,"\t Read from STATUS_REGISTER : %04x", m_CPStatusReg.Hex); DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s" , m_CPStatusReg.Breakpoint ? "ON" : "OFF" @@ -693,4 +696,12 @@ void SetCpClearRegister() // } } +void Update() +{ + while (VITicks > m_cpClockOrigin && fifo.isGpuReadingData && IsOnThread()) + Common::YieldCPU(); + + if (fifo.isGpuReadingData) + Common::AtomicAdd(VITicks, SystemTimers::GetTicksPerSecond() / 10000); +} } // end of namespace CommandProcessor diff --git a/Source/Core/VideoCommon/Src/CommandProcessor.h b/Source/Core/VideoCommon/Src/CommandProcessor.h index 5d31453537..6190c48e14 100644 --- a/Source/Core/VideoCommon/Src/CommandProcessor.h +++ b/Source/Core/VideoCommon/Src/CommandProcessor.h @@ -31,6 +31,7 @@ namespace CommandProcessor extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread. extern volatile bool isPossibleWaitingSetDrawDone; //This one is used for sync gfx thread and emulator thread. extern volatile bool isHiWatermarkActive; +extern volatile bool isLoWatermarkActive; extern volatile bool interruptSet; extern volatile bool interruptWaiting; extern volatile bool interruptTokenWaiting; @@ -140,6 +141,9 @@ union UCPClearReg UCPClearReg(u16 _hex) {Hex = _hex; } }; +// Can be any number, low enough to not be below the number of clocks executed by the GPU per CP_PERIOD +const static u32 m_cpClockOrigin = 200000; + // Init void Init(); void Shutdown(); @@ -161,11 +165,14 @@ bool AllowIdleSkipping(); void SetCpClearRegister(); void SetCpControlRegister(); void SetCpStatusRegister(); -void SetOverflowStatusFromGatherPipe(); void ProcessFifoToLoWatermark(); void ProcessFifoAllDistance(); void ProcessFifoEvents(); void AbortFrame(); + +void Update(); +extern volatile u32 VITicks; + } // namespace CommandProcessor #endif // _COMMANDPROCESSOR_H diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index 413b163ced..93decf7ce3 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -26,6 +26,7 @@ #include "Fifo.h" #include "HW/Memmap.h" #include "Core.h" +#include "CoreTiming.h" volatile bool g_bSkipCurrentFrame = false; extern u8* g_pVideoData; @@ -72,6 +73,7 @@ void Fifo_Init() videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE); size = 0; GpuRunningState = false; + Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin); } void Fifo_Shutdown() @@ -123,7 +125,7 @@ void ReadDataFromFifo(u8* _uData, u32 len) size -= pos; if (size + len > FIFO_SIZE) { - PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos); + PanicAlert("FIFO out of bounds (sz = %i, len = %i at %08x)", size, len, pos); } memmove(&videoBuffer[0], &videoBuffer[pos], size); g_pVideoData = videoBuffer; @@ -147,6 +149,7 @@ void RunGpuLoop() std::lock_guard<std::mutex> lk(m_csHWVidOccupied); GpuRunningState = true; SCPFifoStruct &fifo = CommandProcessor::fifo; + u32 cyclesExecuted = 0; while (GpuRunningState) { @@ -155,31 +158,41 @@ void RunGpuLoop() VideoFifo_CheckAsyncRequest(); CommandProcessor::SetCpStatus(); + + Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin); + // check if we are able to run this buffer - while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) + while (GpuRunningState && !CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint()) { if (!GpuRunningState) break; fifo.isGpuReadingData = true; CommandProcessor::isPossibleWaitingSetDrawDone = fifo.bFF_GPLinkEnable ? true : false; - - u32 readPtr = fifo.CPReadPointer; - u8 *uData = Memory::GetPointer(readPtr); - if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase; + if (Common::AtomicLoad(CommandProcessor::VITicks) > CommandProcessor::m_cpClockOrigin || !Core::g_CoreStartupParameter.bSyncGPU) + { + u32 readPtr = fifo.CPReadPointer; + u8 *uData = Memory::GetPointer(readPtr); + + if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase; else readPtr += 32; - - _assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 , - "Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce inestabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32); - - ReadDataFromFifo(uData, 32); - - OpcodeDecoder_Run(g_bSkipCurrentFrame); - Common::AtomicStore(fifo.CPReadPointer, readPtr); - Common::AtomicAdd(fifo.CPReadWriteDistance, -32); - if((GetVideoBufferEndPtr() - g_pVideoData) == 0) - Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer); + _assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 , + "Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce instabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32); + + ReadDataFromFifo(uData, 32); + + cyclesExecuted = OpcodeDecoder_Run(g_bSkipCurrentFrame); + + if (Common::AtomicLoad(CommandProcessor::VITicks) > cyclesExecuted && Core::g_CoreStartupParameter.bSyncGPU) + Common::AtomicAdd(CommandProcessor::VITicks, -(s32)cyclesExecuted); + + Common::AtomicStore(fifo.CPReadPointer, readPtr); + Common::AtomicAdd(fifo.CPReadWriteDistance, -32); + if((GetVideoBufferEndPtr() - g_pVideoData) == 0) + Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer); + } + CommandProcessor::SetCpStatus(); // This call is pretty important in DualCore mode and must be called in the FIFO Loop. @@ -188,7 +201,7 @@ void RunGpuLoop() VideoFifo_CheckAsyncRequest(); CommandProcessor::isPossibleWaitingSetDrawDone = false; } - + fifo.isGpuReadingData = false; @@ -217,23 +230,23 @@ bool AtBreakpoint() void RunGpu() { - SCPFifoStruct &fifo = CommandProcessor::fifo; - while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() ) - { - u8 *uData = Memory::GetPointer(fifo.CPReadPointer); + SCPFifoStruct &fifo = CommandProcessor::fifo; + while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() ) + { + u8 *uData = Memory::GetPointer(fifo.CPReadPointer); FPURoundMode::SaveSIMDState(); FPURoundMode::LoadDefaultSIMDState(); ReadDataFromFifo(uData, 32); - OpcodeDecoder_Run(g_bSkipCurrentFrame); + u32 count = OpcodeDecoder_Run(g_bSkipCurrentFrame); FPURoundMode::LoadSIMDState(); - //DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base"); + //DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base"); - if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase; - else fifo.CPReadPointer += 32; + if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase; + else fifo.CPReadPointer += 32; - fifo.CPReadWriteDistance -= 32; - } - CommandProcessor::SetCpStatus(); + fifo.CPReadWriteDistance -= 32; + } + CommandProcessor::SetCpStatus(); } diff --git a/Source/Core/VideoCommon/Src/Fifo.h b/Source/Core/VideoCommon/Src/Fifo.h index 75e7d782f5..f465db8f85 100644 --- a/Source/Core/VideoCommon/Src/Fifo.h +++ b/Source/Core/VideoCommon/Src/Fifo.h @@ -23,7 +23,7 @@ class PointerWrap; -#define FIFO_SIZE (1024*1024) +#define FIFO_SIZE (2*1024*1024) extern volatile bool g_bSkipCurrentFrame; diff --git a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp index a1ff1a9985..2e2664e219 100644 --- a/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp +++ b/Source/Core/VideoCommon/Src/OpcodeDecoding.cpp @@ -136,29 +136,38 @@ void ExecuteDisplayList(u32 address, u32 size) InterpretDisplayList(address, size); } -bool FifoCommandRunnable() +u32 FifoCommandRunnable(u32 &command_size) { + u32 cycleTime = 0; u32 buffer_size = (u32)(GetVideoBufferEndPtr() - g_pVideoData); if (buffer_size == 0) - return false; // can't peek + return 0; // can't peek u8 cmd_byte = DataPeek8(0); - u32 command_size = 0; switch (cmd_byte) { case GX_NOP: // Hm, this means that we scan over nop streams pretty slowly... + command_size = 1; + cycleTime = 6; + break; case GX_CMD_INVL_VC: // Invalidate Vertex Cache - no parameters + command_size = 1; + cycleTime = 6; + break; case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that command_size = 1; + cycleTime = 6; break; case GX_LOAD_BP_REG: command_size = 5; + cycleTime = 12; break; case GX_LOAD_CP_REG: command_size = 6; + cycleTime = 12; break; case GX_LOAD_INDX_A: @@ -166,10 +175,39 @@ bool FifoCommandRunnable() case GX_LOAD_INDX_C: case GX_LOAD_INDX_D: command_size = 5; + cycleTime = 6; // TODO break; - case GX_CMD_CALL_DL: - command_size = 9; + case GX_CMD_CALL_DL: + { + // FIXME: Calculate the cycle time of the display list. + //u32 address = DataPeek32(1); + //u32 size = DataPeek32(5); + //u8* old_pVideoData = g_pVideoData; + //u8* startAddress = Memory::GetPointer(address); + + //// Avoid the crash if Memory::GetPointer failed .. + //if (startAddress != 0) + //{ + // g_pVideoData = startAddress; + // u8 *end = g_pVideoData + size; + // u32 step = 0; + // while (g_pVideoData < end) + // { + // cycleTime += FifoCommandRunnable(step); + // g_pVideoData += step; + // } + //} + //else + //{ + // cycleTime = 45; + //} + + //// reset to the old pointer + //g_pVideoData = old_pVideoData; + command_size = 9; + cycleTime = 45; // This is unverified + } break; case GX_LOAD_XF_REG: @@ -180,11 +218,12 @@ bool FifoCommandRunnable() command_size = 1 + 4; u32 Cmd2 = DataPeek32(1); int transfer_size = ((Cmd2 >> 16) & 15) + 1; - command_size += transfer_size * 4; + command_size += transfer_size * 4; + cycleTime = 18 + 6 * transfer_size; } else { - return false; + return 0; } } break; @@ -198,10 +237,11 @@ bool FifoCommandRunnable() command_size = 1 + 2; u16 numVertices = DataPeek16(1); command_size += numVertices * VertexLoaderManager::GetVertexSize(cmd_byte & GX_VAT_MASK); + cycleTime = 1600; // This depends on the number of pixels rendered } else { - return false; + return 0; } } else @@ -248,11 +288,19 @@ bool FifoCommandRunnable() } if (command_size > buffer_size) - return false; + return 0; // INFO_LOG("OP detected: cmd_byte 0x%x size %i buffer %i",cmd_byte, command_size, buffer_size); + if (cycleTime == 0) + cycleTime = 6; - return true; + return cycleTime; +} + +u32 FifoCommandRunnable() +{ + u32 command_size = 0; + return FifoCommandRunnable(command_size); } static void Decode() @@ -461,16 +509,15 @@ void OpcodeDecoder_Shutdown() } } -void OpcodeDecoder_Run(bool skipped_frame) +u32 OpcodeDecoder_Run(bool skipped_frame) { - if (!skipped_frame) - { - while (FifoCommandRunnable()) - Decode(); - } - else + u32 totalCycles = 0; + u32 cycles = FifoCommandRunnable(); + while (cycles > 0) { - while (FifoCommandRunnable()) - DecodeSemiNop(); + skipped_frame ? DecodeSemiNop() : Decode(); + totalCycles += cycles; + cycles = FifoCommandRunnable(); } + return totalCycles; } diff --git a/Source/Core/VideoCommon/Src/OpcodeDecoding.h b/Source/Core/VideoCommon/Src/OpcodeDecoding.h index f2e9cd1321..71a15477a3 100644 --- a/Source/Core/VideoCommon/Src/OpcodeDecoding.h +++ b/Source/Core/VideoCommon/Src/OpcodeDecoding.h @@ -50,6 +50,6 @@ extern bool g_bRecordFifoData; void OpcodeDecoder_Init(); void OpcodeDecoder_Shutdown(); -void OpcodeDecoder_Run(bool skipped_frame); +u32 OpcodeDecoder_Run(bool skipped_frame); void ExecuteDisplayList(u32 address, u32 size); #endif // _OPCODE_DECODING_H diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index e5ba554678..974fdb8203 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -33,7 +33,6 @@ #include "HW/ProcessorInterface.h" #include "DLCache.h" #include "State.h" -#include "PerfQueryBase.h" namespace PixelEngine { @@ -113,14 +112,14 @@ static UPEAlphaReadReg m_AlphaRead; static UPECtrlReg m_Control; //static u16 m_Token; // token value most recently encountered -static bool g_bSignalTokenInterrupt; -static bool g_bSignalFinishInterrupt; +volatile u32 g_bSignalTokenInterrupt; +volatile u32 g_bSignalFinishInterrupt; static int et_SetTokenOnMainThread; static int et_SetFinishOnMainThread; -volatile bool interruptSetToken = false; -volatile bool interruptSetFinish = false; +volatile u32 interruptSetToken = 0; +volatile u32 interruptSetFinish = 0; u16 bbox[4]; bool bbox_active; @@ -164,10 +163,10 @@ void Init() m_AlphaModeConf.Hex = 0; m_AlphaRead.Hex = 0; - g_bSignalTokenInterrupt = false; - g_bSignalFinishInterrupt = false; - interruptSetToken = false; - interruptSetFinish = false; + g_bSignalTokenInterrupt = 0; + g_bSignalFinishInterrupt = 0; + interruptSetToken = 0; + interruptSetFinish = 0; et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread); et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread); @@ -214,7 +213,7 @@ void Read16(u16& _uReturnValue, const u32 _iAddress) break; case PE_TOKEN_REG: - _uReturnValue = CommandProcessor::fifo.PEToken; + _uReturnValue = Common::AtomicLoad(*(volatile u32*)&CommandProcessor::fifo.PEToken); INFO_LOG(PIXELENGINE, "(r16) TOKEN_REG : %04x", _uReturnValue); break; @@ -351,8 +350,8 @@ void Write16(const u16 _iValue, const u32 _iAddress) { UPECtrlReg tmpCtrl(_iValue); - if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = false; - if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = false; + if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = 0; + if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = 0; m_Control.PETokenEnable = tmpCtrl.PETokenEnable; m_Control.PEFinishEnable = tmpCtrl.PEFinishEnable; @@ -398,14 +397,14 @@ void UpdateInterrupts() void UpdateTokenInterrupt(bool active) { - ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active); - interruptSetToken = active; + ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active); + Common::AtomicStore(interruptSetToken, active ? 1 : 0); } void UpdateFinishInterrupt(bool active) { - ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); - interruptSetFinish = active; + ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); + Common::AtomicStore(interruptSetFinish, active ? 1 : 0); } // TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate). @@ -415,20 +414,23 @@ void UpdateFinishInterrupt(bool active) // Called only if BPMEM_PE_TOKEN_INT_ID is ack by GP void SetToken_OnMainThread(u64 userdata, int cyclesLate) { - //if (userdata >> 16) - //{ - g_bSignalTokenInterrupt = true; - //_dbg_assert_msg_(PIXELENGINE, (CommandProcessor::fifo.PEToken == (userdata&0xFFFF)), "WTF? BPMEM_PE_TOKEN_INT_ID's token != BPMEM_PE_TOKEN_ID's token" ); - INFO_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken); + // XXX: No 16-bit atomic store available, so cheat and use 32-bit. + // That's what we've always done. We're counting on fifo.PEToken to be + // 4-byte padded. + Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, userdata & 0xffff); + INFO_LOG(PIXELENGINE, "VIDEO Backend raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken); + if (userdata >> 16) + { + Common::AtomicStore(*(volatile u32*)&g_bSignalTokenInterrupt, 1); UpdateInterrupts(); - CommandProcessor::interruptTokenWaiting = false; - IncrementCheckContextId(); - //} + } + CommandProcessor::interruptTokenWaiting = false; + IncrementCheckContextId(); } void SetFinish_OnMainThread(u64 userdata, int cyclesLate) { - g_bSignalFinishInterrupt = 1; + Common::AtomicStore(*(volatile u32*)&g_bSignalFinishInterrupt, 1); UpdateInterrupts(); CommandProcessor::interruptFinishWaiting = false; CommandProcessor::isPossibleWaitingSetDrawDone = false; @@ -438,23 +440,13 @@ void SetFinish_OnMainThread(u64 userdata, int cyclesLate) // THIS IS EXECUTED FROM VIDEO THREAD void SetToken(const u16 _token, const int _bSetTokenAcknowledge) { - // TODO?: set-token-value and set-token-INT could be merged since set-token-INT own the token value. if (_bSetTokenAcknowledge) // set token INT { - - Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token); - CommandProcessor::interruptTokenWaiting = true; - CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); - } - else // set token value - { - // we do it directly from videoThread because of - // Super Monkey Ball - // XXX: No 16-bit atomic store available, so cheat and use 32-bit. - // That's what we've always done. We're counting on fifo.PEToken to be - // 4-byte padded. - Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token); + Common::AtomicStore(*(volatile u32*)&g_bSignalTokenInterrupt, 1); } + + CommandProcessor::interruptTokenWaiting = true; + CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); IncrementCheckContextId(); } @@ -477,7 +469,6 @@ void ResetSetFinish() { UpdateFinishInterrupt(false); g_bSignalFinishInterrupt = false; - } else { @@ -491,8 +482,7 @@ void ResetSetToken() if (g_bSignalTokenInterrupt) { UpdateTokenInterrupt(false); - g_bSignalTokenInterrupt = false; - + g_bSignalTokenInterrupt = 0; } else { @@ -500,17 +490,4 @@ void ResetSetToken() } CommandProcessor::interruptTokenWaiting = false; } - -bool WaitingForPEInterrupt() -{ - return !CommandProcessor::waitingForPEInterruptDisable && (CommandProcessor::interruptFinishWaiting || CommandProcessor::interruptTokenWaiting || interruptSetFinish || interruptSetToken); -} - -void ResumeWaitingForPEInterrupt() -{ - interruptSetFinish = false; - interruptSetToken = false; - CommandProcessor::interruptFinishWaiting = false; - CommandProcessor::interruptTokenWaiting = false; -} } // end of namespace PixelEngine diff --git a/Source/Core/VideoCommon/Src/PixelEngine.h b/Source/Core/VideoCommon/Src/PixelEngine.h index eaf55f0031..9689104b8b 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.h +++ b/Source/Core/VideoCommon/Src/PixelEngine.h @@ -81,8 +81,6 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge); void SetFinish(void); void ResetSetFinish(void); void ResetSetToken(void); -bool WaitingForPEInterrupt(); -void ResumeWaitingForPEInterrupt(); // Bounding box functionality. Paper Mario (both) are a couple of the few games that use it. extern u16 bbox[4]; |
