Merge branch 'master' into GLSL-master

Conflicts:
	.gitignore
This commit is contained in:
degasus 2013-03-12 11:28:56 +01:00
commit 382be2aabd
87 changed files with 23863 additions and 38776 deletions

2
.gitignore vendored
View file

@ -37,3 +37,5 @@ Source/Core/Common/Src/scmrev.h
Externals/scons-local/* Externals/scons-local/*
.DS_Store .DS_Store
*~ *~
#Ignore transifix configuration directory
.tx

View file

@ -1,5 +1,6 @@
# GKBEAF - Baten Kaitos # GKBEAF - Baten Kaitos
[Core] Values set here will override the main dolphin settings. [Core] Values set here will override the main dolphin settings.
SyncGPU = 1
[EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set. [EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set.
EmulationStateId = 4 EmulationStateId = 4
EmulationIssues = EmulationIssues =

View file

@ -1,5 +1,6 @@
# GKBPAF - Baten Kaitos # GKBPAF - Baten Kaitos
[Core] Values set here will override the main dolphin settings. [Core] Values set here will override the main dolphin settings.
SyncGPU = 1
[EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set. [EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set.
EmulationStateId = 4 EmulationStateId = 4
EmulationIssues = EmulationIssues =

View file

@ -1,8 +1,9 @@
# GLSE64 - LucasArts Gladius # GLSE64 - LucasArts Gladius
[Core] Values set here will override the main dolphin settings. [Core] Values set here will override the main dolphin settings.
TLBHack = 1 TLBHack = 1
SyncGPU = 1
[EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set. [EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set.
EmulationStateId = 1 EmulationStateId = 4
EmulationIssues = EmulationIssues =
[OnFrame] Add memory patches to be applied every frame here. [OnFrame] Add memory patches to be applied every frame here.
[ActionReplay] Add action replay cheats here. [ActionReplay] Add action replay cheats here.

View file

@ -3,8 +3,8 @@
#Values set here will override the main dolphin settings. #Values set here will override the main dolphin settings.
[EmuState] [EmuState]
#The Emulation State. 1 is worst, 5 is best, 0 is not set. #The Emulation State. 1 is worst, 5 is best, 0 is not set.
EmulationStateId = 1 EmulationStateId = 4
EmulationIssues = EmulationIssues = Enable the GameCube BIOS to allow the game to boot.
[OnFrame] [OnFrame]
+$Nop Hack +$Nop Hack
0x80025BA0:dword:0x60000000 0x80025BA0:dword:0x60000000

View file

@ -0,0 +1,17 @@
# GSZP41 - SPEED CHALLENGE - Jacques Villeneuve's Racing Vision
[Core] Values set here will override the main dolphin settings.
SyncGPU = 1
[EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set.
EmulationStateId = 4
EmulationIssues =
[OnFrame] Add memory patches to be applied every frame here.
[ActionReplay] Add action replay cheats here.
[Video]
ProjectionHack = 0
PH_SZNear = 0
PH_SZFar = 0
PH_ExtraParam = 0
PH_ZNear =
PH_ZFar =
UseBBox = 1
[Gecko]

View file

@ -0,0 +1,10 @@
# GWLE6L - Project Zoo
[Core] Values set here will override the main dolphin settings.
TLBHack = 1
[EmuState] The Emulation State. 1 is worst, 5 is best, 0 is not set.
EmulationStateId = 3
EmulationIssues =
[OnFrame] Add memory patches to be applied every frame here.
+$Bypass FIFO reset
0x8028EF00:dword:0x48000638
[ActionReplay] Add action replay cheats here.

View file

@ -1,6 +1,5 @@
# GZ2E01 - The Legend of Zelda: Twilight Princess # GZ2E01 - The Legend of Zelda: Twilight Princess
[Core] [Core]
FastDiscSpeed = 1
[EmuState] [EmuState]
#The Emulation State. #The Emulation State.
EmulationStateId = 4 EmulationStateId = 4

View file

@ -1,6 +1,5 @@
# GZ2J01 - The Legend of Zelda: Twilight Princess # GZ2J01 - The Legend of Zelda: Twilight Princess
[Core] [Core]
FastDiscSpeed = 1
[EmuState] [EmuState]
#The Emulation State. #The Emulation State.
EmulationStateId = 4 EmulationStateId = 4

View file

@ -1,7 +1,6 @@
# GZ2P01 - The Legend of Zelda Twilight Princess # GZ2P01 - The Legend of Zelda Twilight Princess
[Core] [Core]
#Values set here will override the main dolphin settings. #Values set here will override the main dolphin settings.
FastDiscSpeed = 1
[Speedhacks] [Speedhacks]
0x803420bc=200 0x803420bc=200
[EmuState] [EmuState]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -83,65 +83,104 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated)
} }
} }
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 (TryMakeOperand2_AllowInverse(val, op2, &inverse)) {
if (!inverse) {
AND(rd, rs, op2);
} else {
BIC(rd, rs, op2);
}
} 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)
{
(*it).loc = (s32)code;
Write32((*it).val);
}
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) void ARMXEmitter::MOVI2R(ARMReg reg, u32 val, bool optimize)
{ {
Operand2 op2; Operand2 op2;
bool inverse; bool inverse;
if (!optimize)
if (cpu_info.bArmV7 && !optimize)
{ {
// Only used in backpatch atm // For backpatching on ARMv7
// Only support ARMv7 right now
if (cpu_info.bArmV7) {
MOVW(reg, val & 0xFFFF); MOVW(reg, val & 0xFFFF);
MOVT(reg, val, true); MOVT(reg, val, true);
} }
else else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) {
{ inverse ? MVN(reg, op2) : MOV(reg, op2);
// ARMv6 version won't use backpatch for now
// Run again with optimizations
MOVI2R(reg, val);
}
} else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) {
if (!inverse)
MOV(reg, op2);
else
MVN(reg, op2);
} else { } else {
if (cpu_info.bArmV7) { if (cpu_info.bArmV7)
// ARMv7 - can use MOVT/MOVW, best choice {
// Use MOVW+MOVT for ARMv7+
MOVW(reg, val & 0xFFFF); MOVW(reg, val & 0xFFFF);
if(val & 0xFFFF0000) if(val & 0xFFFF0000)
MOVT(reg, val, true); MOVT(reg, val, true);
} else { } else {
// ARMv6 - fallback sequence. // Use literal pool for ARMv6.
// TODO: Optimize further. Can for example choose negation etc. AddNewLit(val);
// Literal pools is another way to do this but much more complicated LDRLIT(reg, 0, 0); // To be backpatched later
// 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;
}
} }
} }
} }
// 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) { void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) {
MOVI2R(reg, (u32)(func)); MOVI2R(reg, (u32)(func));
BL(reg); BL(reg);
@ -151,6 +190,9 @@ void ARMXEmitter::SetCodePtr(u8 *ptr)
{ {
code = ptr; code = ptr;
startcode = code; startcode = code;
#ifdef IOS
lastCacheFlushEnd = ptr;
#endif
} }
const u8 *ARMXEmitter::GetCodePtr() const const u8 *ARMXEmitter::GetCodePtr() const
@ -194,10 +236,11 @@ void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end)
#elif defined(BLACKBERRY) #elif defined(BLACKBERRY)
msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE); msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE);
#elif defined(IOS) #elif defined(IOS)
if (start != NULL)
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start); sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
#elif !defined(_WIN32) #elif !defined(_WIN32)
#ifndef ANDROID #ifndef ANDROID
start = startcode; start = startcode; // Should be Linux Only
#endif #endif
__builtin___clear_cache(start, end); __builtin___clear_cache(start, end);
#endif #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::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::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::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) void ARMXEmitter::MUL (ARMReg dest, ARMReg src, ARMReg op2)
{ {
Write32(condition | (dest << 16) | (src << 8) | (9 << 4) | 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); 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) void ARMXEmitter::SXTB (ARMReg dest, ARMReg op2)
{ {
Write32(condition | (0x6AF << 16) | (dest << 12) | (7 << 4) | op2); Write32(condition | (0x6AF << 16) | (dest << 12) | (7 << 4) | op2);
} }
void ARMXEmitter::SXTH (ARMReg dest, ARMReg op2, u8 rotation) void ARMXEmitter::SXTH (ARMReg dest, ARMReg op2, u8 rotation)
{ {
SXTAH(dest, (ARMReg)15, op2, rotation); SXTAH(dest, (ARMReg)15, op2, rotation);
@ -511,9 +583,13 @@ void ARMXEmitter::SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation)
// information // information
Write32(condition | (0x6B << 20) | (src << 16) | (dest << 12) | (rotation << 10) | (7 << 4) | op2); 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) void ARMXEmitter::REV16(ARMReg dest, ARMReg src)
{ {
@ -533,27 +609,46 @@ void ARMXEmitter::MRS (ARMReg dest)
Write32(condition | (16 << 20) | (15 << 16) | (dest << 12)); 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)
{ {
bool Index = op2 != 0 ? true : false; // 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; bool Add = op2 >= 0 ? true : false;
u32 imm = abs(op2); u32 imm = abs(op2);
Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (src << 12) | imm); Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (src << 16) | (dest << 12) | imm);
} }
void ARMXEmitter::STR (ARMReg dest, ARMReg src, s16 op) { WriteStoreOp(0x40, dest, src, op);} void ARMXEmitter::STR (ARMReg result, ARMReg base, s16 op) { WriteStoreOp(0x40, base, result, op);}
void ARMXEmitter::STRB(ARMReg dest, ARMReg src, s16 op) { WriteStoreOp(0x44, dest, src, op);} void ARMXEmitter::STRH (ARMReg result, ARMReg base, Operand2 op)
void ARMXEmitter::STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add)
{ {
Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | offset); 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)
{
Write32(condition | (0x00 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | (0xB << 4) | offset);
}
void ARMXEmitter::STRB (ARMReg result, ARMReg base, ARMReg offset, bool Index, bool Add)
{
Write32(condition | (0x64 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (result << 12) | offset);
} }
void ARMXEmitter::LDREX(ARMReg dest, ARMReg base) void ARMXEmitter::LDREX(ARMReg dest, ARMReg base)
{ {
Write32(condition | (25 << 20) | (base << 16) | (dest << 12) | 0xF9F); 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"); _assert_msg_(DYNA_REC, (result != base && result != op), "STREX dest can't be other two registers");
Write32(condition | (24 << 20) | (base << 16) | (dest << 12) | (0xF9 << 4) | op); Write32(condition | (24 << 20) | (base << 16) | (result << 12) | (0xF9 << 4) | op);
} }
void ARMXEmitter::DMB () void ARMXEmitter::DMB ()
{ {
@ -570,12 +665,44 @@ void ARMXEmitter::LDRH(ARMReg dest, ARMReg src, Operand2 op)
u8 Imm = op.Imm8(); u8 Imm = op.Imm8();
Write32(condition | (0x05 << 20) | (src << 16) | (dest << 12) | ((Imm >> 4) << 8) | (0xB << 4) | (Imm & 0x0F)); 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::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, 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) 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); 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) void ARMXEmitter::WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList)
{ {
Write32(condition | (op << 20) | (WriteBack << 21) | (dest << 16) | 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"); _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); ERROR_LOG(DYNA_REC, "VLDR: Bad offset %08x", imm);
}
bool single_reg = Dest < D0; bool single_reg = Dest < D0;
@ -681,13 +807,13 @@ void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset)
if (single_reg) 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)); | ((Dest & 0x1E) << 11) | (10 << 8) | (imm >> 2));
} }
else 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)); | ((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"); _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); ERROR_LOG(DYNA_REC, "VSTR: Bad offset %08x", imm);
}
bool single_reg = Src < D0; bool single_reg = Src < D0;
@ -711,17 +836,16 @@ void ARMXEmitter::VSTR(ARMReg Src, ARMReg Base, s16 offset)
if (single_reg) 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)); | ((Src & 0x1E) << 11) | (10 << 8) | (imm >> 2));
} }
else 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)); | ((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"); _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP");
bool single_reg = Vd < D0; bool single_reg = Vd < D0;
@ -731,16 +855,16 @@ void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm)
if (single_reg) if (single_reg)
{ {
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \ Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x34 << 16) | ((Vd & 0x1E) << 11) \
| (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); | (E << 7) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1));
} }
else else
{ {
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \ Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x34 << 16) | ((Vd & 0xF) << 12) \
| (0x2F << 6) | ((Vm & 0x10) << 1) | (Vm & 0xF)); | (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"); _assert_msg_(DYNA_REC, Vd < Q0, "Passed invalid Vd to VCMP");
bool single_reg = Vd < D0; bool single_reg = Vd < D0;
@ -749,15 +873,26 @@ void ARMXEmitter::VCMP(ARMReg Vd)
if (single_reg) if (single_reg)
{ {
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \ Write32(condition | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x35 << 16) | ((Vd & 0x1E) << 11) \
| (0x2B << 6)); | (E << 7) | (0x29 << 6));
} }
else else
{ {
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \ Write32(condition | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x35 << 16) | ((Vd & 0xF) << 12) \
| (0x2F << 6)); | (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) void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm)
{ {
_assert_msg_(DYNA_REC, Vd < Q0, "Pased invalid dest register to VSQRT"); _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) 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) \ | ((Vd & 0x1E) << 11) | (0xA << 8) | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) \
| (Vm >> 1)); | (Vm >> 1));
} }
else 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) \ | ((Vd & 0xF) << 12) | (0xB << 8) | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) \
| (Vm & 0xF)); | (Vm & 0xF));
} }
@ -793,47 +928,17 @@ void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm)
if (single_reg) 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)); | ((Vd & 0x1E) << 11) | (0x2B << 6) | ((Vm & 0x1) << 5) | (Vm >> 1));
} }
else 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)); | ((Vd & 0xF) << 12) | (0x2F << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF));
} }
} }
// VFP and ASIMD // 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) void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm)
{ {
_assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD"); _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) 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 & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \
| ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1));
} }
@ -856,7 +961,7 @@ void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm)
{ {
if (double_reg) 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 & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \
| ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF)); | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF));
} }
@ -883,7 +988,7 @@ void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm)
if (single_reg) 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 & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \
| ((Vn & 0x1) << 7) | (1 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); | ((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) 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 & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \
| ((Vn & 0x10) << 3) | (1 << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); | ((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) void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm)
{ {
_assert_msg_(DYNA_REC, Vd >= S0, "Passed invalid dest register to VADD"); _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) 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 & 0x1E) << 15) | ((Vd & 0x1E) << 11) | (0x5 << 9) \
| ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1)); | ((Vn & 0x1) << 7) | ((Vm & 0x1) << 5) | (Vm >> 1));
} }
@ -926,22 +1032,45 @@ void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm)
{ {
if (double_reg) 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 & 0xF) << 16) | ((Vd & 0xF) << 12) | (0xB << 8) \
| ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF)); | ((Vn & 0x10) << 3) | ((Vm & 0x10) << 2) | (Vm & 0xF));
} }
else else
{ {
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Trying to use VMUL with Quad Reg without support!"); _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, "Passed invalid dest register to VMLA");
_assert_msg_(DYNA_REC, Vd >= S0, "VNEG doesn't support ARM Regs"); _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(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 single_reg = Vd < D0;
bool double_reg = Vd < Q0; bool double_reg = Vd < Q0;
@ -950,25 +1079,43 @@ void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm)
if (single_reg) if (single_reg)
{ {
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x1) << 22) | (0x31 << 16) \ Write32(condition | (0xEB << 20) | ((Vd & 0x1) << 22) | ((Vd & 0x1E) << 11) \
| ((Vd & 0x1E) << 11) | (0x29 << 6) | ((Vm & 0x1) << 5) | (Vm >> 1)); | (0xAC << 4) | ((Vm & 0x1) << 5) | (Vm >> 1));
} }
else else
{ {
if (double_reg) if (double_reg)
{ {
Write32(NO_COND | (0x1D << 23) | ((Vd & 0x10) << 18) | (0x31 << 16) \ Write32(condition | (0xEB << 20) | ((Vd & 0x10) << 18) | ((Vd & 0xF) << 12) \
| ((Vd & 0xF) << 12) | (0x2D << 6) | ((Vm & 0x10) << 2) | (Vm & 0xF)); | (0xBC << 4) | ((Vm & 0x10) << 1) | (Vm & 0xF));
} }
else 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 // 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));
}
}
void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high) void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high)
{ {
_assert_msg_(DYNA_REC, Src < S0, "This VMOV doesn't support SRC other than ARM Reg"); _assert_msg_(DYNA_REC, Src < S0, "This VMOV doesn't support SRC other than ARM Reg");
@ -976,7 +1123,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high)
Dest = SubBase(Dest); 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)); | (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 // Moving to a Neon register FROM ARM Reg
Dest = (ARMReg)(Dest - S0); 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)); | (0xA << 8) | ((Dest & 0x1) << 7) | (1 << 4));
return; return;
} }
@ -1010,7 +1157,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
{ {
// Moving to ARM Reg from Neon Register // Moving to ARM Reg from Neon Register
Src = (ARMReg)(Src - S0); 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)); | (0xA << 8) | ((Src & 0x1) << 7) | (1 << 4));
return; return;
} }
@ -1040,7 +1187,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
if (Single) 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)); | (0x5 << 9) | (1 << 6) | ((Src & 0x1) << 5) | ((Src & 0x1E) >> 1));
} }
else else
@ -1057,10 +1204,28 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
} }
else 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)); | (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));
}
}
} }

View file

@ -25,6 +25,7 @@
#if defined(__SYMBIAN32__) || defined(PANDORA) #if defined(__SYMBIAN32__) || defined(PANDORA)
#include <signal.h> #include <signal.h>
#endif #endif
#include <vector>
#undef _IP #undef _IP
#undef R0 #undef R0
@ -32,6 +33,12 @@
#undef _LR #undef _LR
#undef _PC #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 namespace ArmGen
{ {
enum ARMReg enum ARMReg
@ -171,7 +178,7 @@ public:
Value = base; 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; if(shift == 32) shift = 0;
switch (type) switch (type)
@ -327,6 +334,13 @@ struct FixupBranch
int type; //0 = B 1 = BL int type; //0 = B 1 = BL
}; };
struct LiteralPool
{
s32 loc;
u8* ldr_address;
u32 val;
};
typedef const u8* JumpTarget; typedef const u8* JumpTarget;
class ARMXEmitter class ARMXEmitter
@ -336,8 +350,9 @@ private:
u8 *code, *startcode; u8 *code, *startcode;
u8 *lastCacheFlushEnd; u8 *lastCacheFlushEnd;
u32 condition; 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 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, ARMReg op2);
void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2); void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2);
@ -373,6 +388,10 @@ public:
void FlushIcacheSection(u8 *start, u8 *end); void FlushIcacheSection(u8 *start, u8 *end);
u8 *GetWritableCodePtr(); u8 *GetWritableCodePtr();
void FlushLitPool();
void AddNewLit(u32 val);
CCFlags GetCC() { return CCFlags(condition >> 28); }
void SetCC(CCFlags cond = CC_AL); void SetCC(CCFlags cond = CC_AL);
// Special purpose instructions // Special purpose instructions
@ -425,8 +444,10 @@ public:
void LSL (ARMReg dest, ARMReg src, ARMReg op2); void LSL (ARMReg dest, ARMReg src, ARMReg op2);
void LSLS(ARMReg dest, ARMReg src, Operand2 op2); void LSLS(ARMReg dest, ARMReg src, Operand2 op2);
void LSLS(ARMReg dest, ARMReg src, ARMReg 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 SBC (ARMReg dest, ARMReg src, Operand2 op2);
void SBCS(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 REV (ARMReg dest, ARMReg src);
void REV16 (ARMReg dest, ARMReg src); void REV16 (ARMReg dest, ARMReg src);
void RSC (ARMReg dest, ARMReg src, Operand2 op2); void RSC (ARMReg dest, ARMReg src, Operand2 op2);
@ -457,33 +478,53 @@ public:
void UMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm); void UMULL(ARMReg destLo, ARMReg destHi, ARMReg rn, ARMReg rm);
void SMULL(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 SXTB(ARMReg dest, ARMReg op2);
void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0); void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0);
void SXTAH(ARMReg dest, ARMReg src, 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...) // 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. // Just need to put an underscore here, bit annoying.
void _MSR (bool nzcvq, bool g, Operand2 op2); 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); void MRS (ARMReg dest);
// Memory load/store operations // 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 // Offset adds to the base register in LDR
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 LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
void LDRH(ARMReg dest, ARMReg src, Operand2 op = 0); void LDRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
void LDRB(ARMReg dest, ARMReg src, s16 op2 = 0); void LDRSH(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
void STR (ARMReg dest, ARMReg src, s16 op2 = 0); void LDRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
// Offset adds on to the destination register in STR void LDRSB(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
void STR (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 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 STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...);
void LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...); void LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...);
// Exclusive Access operations // Exclusive Access operations
void LDREX(ARMReg dest, ARMReg base); void LDREX(ARMReg dest, ARMReg base);
// dest contains the result if the instruction managed to store the value // result contains the result if the instruction managed to store the value
void STREX(ARMReg dest, ARMReg base, ARMReg op); void STREX(ARMReg result, ARMReg base, ARMReg op);
void DMB (); void DMB ();
void SVC(Operand2 op); void SVC(Operand2 op);
@ -501,9 +542,9 @@ public:
// VFP Only // VFP Only
void VLDR(ARMReg Dest, ARMReg Base, s16 offset); void VLDR(ARMReg Dest, ARMReg Base, s16 offset);
void VSTR(ARMReg Src, 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 // Compares against zero
void VCMP(ARMReg Vd); void VCMP(ARMReg Vd, bool E);
void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm); void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VSQRT(ARMReg Vd, ARMReg Vm); void VSQRT(ARMReg Vd, ARMReg Vm);
@ -513,13 +554,25 @@ public:
void VABS(ARMReg Vd, ARMReg Vm); void VABS(ARMReg Vd, ARMReg Vm);
void VNEG(ARMReg Vd, ARMReg Vm); void VNEG(ARMReg Vd, ARMReg Vm);
void VMUL(ARMReg Vd, ARMReg Vn, 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, bool high);
void VMOV(ARMReg Dest, ARMReg Src); 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); void QuickCallFunction(ARMReg scratchreg, void *func);
// Utility functions
// Wrapper around MOVT/MOVW with fallbacks.
void MOVI2R(ARMReg reg, u32 val, bool optimize = true); 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 }; // 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. // Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
void FreeCodeSpace() void FreeCodeSpace()
{ {
#ifndef __SYMBIAN32__
FreeMemoryPages(region, region_size); FreeMemoryPages(region, region_size);
#endif
region = NULL; region = NULL;
region_size = 0; region_size = 0;
} }

View file

@ -55,7 +55,7 @@ namespace BootManager
struct ConfigCache struct ConfigCache
{ {
bool valid, bCPUThread, bSkipIdle, bEnableFPRF, bMMU, bDCBZOFF, bool valid, bCPUThread, bSkipIdle, bEnableFPRF, bMMU, bDCBZOFF,
bVBeam, bFastDiscSpeed, bMergeBlocks, bDSPHLE, bHLE_BS2; bVBeam, bSyncGPU, bFastDiscSpeed, bMergeBlocks, bDSPHLE, bHLE_BS2;
int iTLBHack, iCPUCore; int iTLBHack, iCPUCore;
std::string strBackend; std::string strBackend;
}; };
@ -95,6 +95,7 @@ bool BootCore(const std::string& _rFilename)
config_cache.bDCBZOFF = StartUp.bDCBZOFF; config_cache.bDCBZOFF = StartUp.bDCBZOFF;
config_cache.iTLBHack = StartUp.iTLBHack; config_cache.iTLBHack = StartUp.iTLBHack;
config_cache.bVBeam = StartUp.bVBeam; config_cache.bVBeam = StartUp.bVBeam;
config_cache.bSyncGPU = StartUp.bSyncGPU;
config_cache.bFastDiscSpeed = StartUp.bFastDiscSpeed; config_cache.bFastDiscSpeed = StartUp.bFastDiscSpeed;
config_cache.bMergeBlocks = StartUp.bMergeBlocks; config_cache.bMergeBlocks = StartUp.bMergeBlocks;
config_cache.bDSPHLE = StartUp.bDSPHLE; 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", "TLBHack", &StartUp.iTLBHack, StartUp.iTLBHack);
game_ini.Get("Core", "DCBZ", &StartUp.bDCBZOFF, StartUp.bDCBZOFF); game_ini.Get("Core", "DCBZ", &StartUp.bDCBZOFF, StartUp.bDCBZOFF);
game_ini.Get("Core", "VBeam", &StartUp.bVBeam, StartUp.bVBeam); 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", "FastDiscSpeed", &StartUp.bFastDiscSpeed, StartUp.bFastDiscSpeed);
game_ini.Get("Core", "BlockMerging", &StartUp.bMergeBlocks, StartUp.bMergeBlocks); game_ini.Get("Core", "BlockMerging", &StartUp.bMergeBlocks, StartUp.bMergeBlocks);
game_ini.Get("Core", "DSPHLE", &StartUp.bDSPHLE, StartUp.bDSPHLE); game_ini.Get("Core", "DSPHLE", &StartUp.bDSPHLE, StartUp.bDSPHLE);
@ -168,6 +170,7 @@ void Stop()
StartUp.bDCBZOFF = config_cache.bDCBZOFF; StartUp.bDCBZOFF = config_cache.bDCBZOFF;
StartUp.iTLBHack = config_cache.iTLBHack; StartUp.iTLBHack = config_cache.iTLBHack;
StartUp.bVBeam = config_cache.bVBeam; StartUp.bVBeam = config_cache.bVBeam;
StartUp.bSyncGPU = config_cache.bSyncGPU;
StartUp.bFastDiscSpeed = config_cache.bFastDiscSpeed; StartUp.bFastDiscSpeed = config_cache.bFastDiscSpeed;
StartUp.bMergeBlocks = config_cache.bMergeBlocks; StartUp.bMergeBlocks = config_cache.bMergeBlocks;
StartUp.bDSPHLE = config_cache.bDSPHLE; StartUp.bDSPHLE = config_cache.bDSPHLE;

View file

@ -404,6 +404,7 @@ void SConfig::LoadSettings()
ini.Get("Core", "MMU", &m_LocalCoreStartupParameter.bMMU, false); ini.Get("Core", "MMU", &m_LocalCoreStartupParameter.bMMU, false);
ini.Get("Core", "TLBHack", &m_LocalCoreStartupParameter.iTLBHack, 0); ini.Get("Core", "TLBHack", &m_LocalCoreStartupParameter.iTLBHack, 0);
ini.Get("Core", "VBeam", &m_LocalCoreStartupParameter.bVBeam, false); 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", "FastDiscSpeed", &m_LocalCoreStartupParameter.bFastDiscSpeed, false);
ini.Get("Core", "DCBZ", &m_LocalCoreStartupParameter.bDCBZOFF, false); ini.Get("Core", "DCBZ", &m_LocalCoreStartupParameter.bDCBZOFF, false);
ini.Get("Core", "FrameLimit", &m_Framelimit, 1); // auto frame limit by default 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, ""); ini.Get("Core", "GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, "");
// Movie // Movie
ini.Get("General", "PauseMovie", &m_PauseMovie, false); ini.Get("Movie", "PauseMovie", &m_PauseMovie, false);
ini.Get("Movie", "Author", &m_strMovieAuthor, ""); ini.Get("Movie", "Author", &m_strMovieAuthor, "");
// DSP // DSP

View file

@ -50,7 +50,7 @@ SCoreStartupParameter::SCoreStartupParameter()
bDPL2Decoder(false), iLatency(14), bDPL2Decoder(false), iLatency(14),
bRunCompareServer(false), bRunCompareClient(false), bRunCompareServer(false), bRunCompareClient(false),
bMMU(false), bDCBZOFF(false), iTLBHack(0), bVBeam(false), bMMU(false), bDCBZOFF(false), iTLBHack(0), bVBeam(false),
bFastDiscSpeed(false), bSyncGPU(false), bFastDiscSpeed(false),
SelectedLanguage(0), bWii(false), SelectedLanguage(0), bWii(false),
bConfirmStop(false), bHideCursor(false), bConfirmStop(false), bHideCursor(false),
bAutoHideCursor(false), bUsePanicHandlers(true), bOnScreenDisplayMessages(true), bAutoHideCursor(false), bUsePanicHandlers(true), bOnScreenDisplayMessages(true),
@ -78,6 +78,7 @@ void SCoreStartupParameter::LoadDefaults()
bDCBZOFF = false; bDCBZOFF = false;
iTLBHack = 0; iTLBHack = 0;
bVBeam = false; bVBeam = false;
bSyncGPU = false;
bFastDiscSpeed = false; bFastDiscSpeed = false;
bMergeBlocks = false; bMergeBlocks = false;
SelectedLanguage = 0; SelectedLanguage = 0;

View file

@ -116,6 +116,7 @@ struct SCoreStartupParameter
bool bDCBZOFF; bool bDCBZOFF;
int iTLBHack; int iTLBHack;
bool bVBeam; bool bVBeam;
bool bSyncGPU;
bool bFastDiscSpeed; bool bFastDiscSpeed;
int SelectedLanguage; int SelectedLanguage;

View file

@ -145,6 +145,8 @@ static void GenerateAudioInterrupt();
static void UpdateInterrupts(); static void UpdateInterrupts();
static void IncreaseSampleCount(const u32 _uAmount); static void IncreaseSampleCount(const u32 _uAmount);
void ReadStreamBlock(s16* _pPCM); void ReadStreamBlock(s16* _pPCM);
u64 GetAIPeriod();
int et_AI;
void Init() void Init()
{ {
@ -159,6 +161,8 @@ void Init()
g_AISSampleRate = 48000; g_AISSampleRate = 48000;
g_AIDSampleRate = 32000; g_AIDSampleRate = 32000;
et_AI = CoreTiming::RegisterEvent("AICallback", Update);
} }
void Shutdown() void Shutdown()
@ -178,11 +182,8 @@ void Read32(u32& _rReturnValue, const u32 _Address)
break; break;
case AI_SAMPLE_COUNTER: case AI_SAMPLE_COUNTER:
Update(0, 0);
_rReturnValue = m_SampleCounter; _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; break;
case AI_INTERRUPT_TIMING: case AI_INTERRUPT_TIMING:
@ -236,6 +237,9 @@ void Write32(const u32 _Value, const u32 _Address)
// Tell Drive Interface to start/stop streaming // Tell Drive Interface to start/stop streaming
DVDInterface::g_bStream = tmpAICtrl.PSTAT; DVDInterface::g_bStream = tmpAICtrl.PSTAT;
CoreTiming::RemoveEvent(et_AI);
CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI);
} }
// AI Interrupt // AI Interrupt
@ -271,6 +275,8 @@ void Write32(const u32 _Value, const u32 _Address)
case AI_INTERRUPT_TIMING: case AI_INTERRUPT_TIMING:
m_InterruptTiming = _Value; m_InterruptTiming = _Value;
CoreTiming::RemoveEvent(et_AI);
CoreTiming::ScheduleEvent(((int)GetAIPeriod() / 2), et_AI);
DEBUG_LOG(AUDIO_INTERFACE, "Set interrupt: %08x samples", m_InterruptTiming); DEBUG_LOG(AUDIO_INTERFACE, "Set interrupt: %08x samples", m_InterruptTiming);
break; break;
@ -448,7 +454,7 @@ unsigned int GetAIDSampleRate()
return g_AIDSampleRate; return g_AIDSampleRate;
} }
void Update() void Update(u64 userdata, int cyclesLate)
{ {
if (m_Control.PSTAT) if (m_Control.PSTAT)
{ {
@ -459,7 +465,16 @@ void Update()
g_LastCPUTime += Samples * g_CPUCyclesPerSample; g_LastCPUTime += Samples * g_CPUCyclesPerSample;
IncreaseSampleCount(Samples); 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 } // end of namespace AudioInterface

View file

@ -31,7 +31,7 @@ void Init();
void Shutdown(); void Shutdown();
void DoState(PointerWrap &p); void DoState(PointerWrap &p);
void Update(); void Update(u64 userdata, int cyclesLate);
// Called by DSP emulator // Called by DSP emulator
void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate); void Callback_GetSampleRate(unsigned int &_AISampleRate, unsigned int &_DACSampleRate);

View file

@ -436,10 +436,6 @@ void Write16(const u16 _Value, const u32 _Address)
if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0; if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0;
if (tmpControl.DSP) g_dspState.DSPControl.DSP = 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 // unknown
g_dspState.DSPControl.unk3 = tmpControl.unk3; g_dspState.DSPControl.unk3 = tmpControl.unk3;
g_dspState.DSPControl.pad = tmpControl.pad; 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 // Send silence. Yeah, it's a bit of a waste to sample rate convert
// silence. or hm. Maybe we shouldn't do this :) // 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()) if (!GetDSPEmulator()->IsLLE())
g_dspState.DSPControl.DMAState = 1; 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 // Real hardware DMAs in 32byte chunks, but we can get by with 8byte chunks
if (g_arDMA.Cnt.dir) if (g_arDMA.Cnt.dir)

View file

@ -32,11 +32,7 @@
#include "../Movie.h" #include "../Movie.h"
// Disc transfer rate measured in bytes per second // Disc transfer rate measured in bytes per second
static const u32 DISC_TRANSFER_RATE_GC = 3125 * 1024; static const u32 DISC_TRANSFER_RATE_GC = 4 * 1024 * 1024;
static const u32 DISC_TRANSFER_RATE_WII = 7926 * 1024;
// Disc access time measured in milliseconds
static const u32 DISC_ACCESS_TIME_MS = 128;
namespace DVDInterface namespace DVDInterface
{ {
@ -501,8 +497,7 @@ void Write32(const u32 _iValue, const u32 _iAddress)
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed) if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed)
{ {
u64 ticksUntilTC = m_DILENGTH.Length * u64 ticksUntilTC = m_DILENGTH.Length *
(SystemTimers::GetTicksPerSecond() / (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii?DISC_TRANSFER_RATE_WII:DISC_TRANSFER_RATE_GC)) + (SystemTimers::GetTicksPerSecond() / (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii ? 1 : DISC_TRANSFER_RATE_GC));
(SystemTimers::GetTicksPerSecond() * DISC_ACCESS_TIME_MS / 1000);
CoreTiming::ScheduleEvent((int)ticksUntilTC, tc); CoreTiming::ScheduleEvent((int)ticksUntilTC, tc);
} }
else else

View file

@ -25,9 +25,9 @@
#define EXI_WRITE 1 #define EXI_WRITE 1
#define EXI_READWRITE 2 #define EXI_READWRITE 2
#include "ProcessorInterface.h" #include "ProcessorInterface.h"
#include "../PowerPC/PowerPC.h" #include "../PowerPC/PowerPC.h"
#include "CoreTiming.h"
CEXIChannel::CEXIChannel(u32 ChannelId) : CEXIChannel::CEXIChannel(u32 ChannelId) :
m_DMAMemoryAddress(0), m_DMAMemoryAddress(0),
@ -45,6 +45,8 @@ CEXIChannel::CEXIChannel(u32 ChannelId) :
for (int i = 0; i < NUM_DEVICES; i++) for (int i = 0; i < NUM_DEVICES; i++)
m_pDevices[i] = EXIDevice_Create(EXIDEVICE_NONE, m_ChannelId); m_pDevices[i] = EXIDevice_Create(EXIDEVICE_NONE, m_ChannelId);
updateInterrupts = CoreTiming::RegisterEvent("EXIInterrupt", UpdateInterrupts);
} }
CEXIChannel::~CEXIChannel() CEXIChannel::~CEXIChannel()
@ -88,12 +90,12 @@ void CEXIChannel::AddDevice(IEXIDevice* pDevice, const int device_num, bool noti
if (m_ChannelId != 2) if (m_ChannelId != 2)
{ {
m_Status.EXTINT = 1; m_Status.EXTINT = 1;
UpdateInterrupts(); CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
} }
} }
} }
void CEXIChannel::UpdateInterrupts() void CEXIChannel::UpdateInterrupts(u64 userdata, int cyclesLate)
{ {
ExpansionInterface::UpdateInterrupts(); ExpansionInterface::UpdateInterrupts();
} }
@ -149,7 +151,9 @@ void CEXIChannel::Read32(u32& _uReturnValue, const u32 _iRegister)
if (m_ChannelId == 2) if (m_ChannelId == 2)
m_Status.EXT = 0; m_Status.EXT = 0;
else else
{
m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0; m_Status.EXT = GetDevice(1)->IsPresent() ? 1 : 0;
}
_uReturnValue = m_Status.Hex; _uReturnValue = m_Status.Hex;
break; break;
@ -213,7 +217,7 @@ void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister)
if (pDevice != NULL) if (pDevice != NULL)
pDevice->SetCS(m_Status.CHIP_SELECT); pDevice->SetCS(m_Status.CHIP_SELECT);
UpdateInterrupts(); CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
} }
break; break;
@ -264,7 +268,7 @@ void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister)
if(!m_Control.TSTART) // completed ! if(!m_Control.TSTART) // completed !
{ {
m_Status.TCINT = 1; m_Status.TCINT = 1;
UpdateInterrupts(); CoreTiming::ScheduleEvent_Threadsafe_Immediate(updateInterrupts, 0);
} }
} }
break; break;

View file

@ -110,6 +110,9 @@ private:
// Since channels operate a bit differently from each other // Since channels operate a bit differently from each other
u32 m_ChannelId; u32 m_ChannelId;
int updateInterrupts;
static void UpdateInterrupts(u64 userdata, int cyclesLate);
public: public:
// get device // get device
IEXIDevice* GetDevice(const u8 _CHIP_SELECT); IEXIDevice* GetDevice(const u8 _CHIP_SELECT);
@ -129,7 +132,6 @@ public:
void Update(); void Update();
bool IsCausingInterrupt(); bool IsCausingInterrupt();
void UpdateInterrupts();
void DoState(PointerWrap &p); void DoState(PointerWrap &p);
void PauseAndLock(bool doLock, bool unpauseOnUnlock); void PauseAndLock(bool doLock, bool unpauseOnUnlock);

View file

@ -47,6 +47,14 @@ void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate)
pThis->Flush(); 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) CEXIMemoryCard::CEXIMemoryCard(const int index)
: card_index(index) : card_index(index)
, m_bDirty(false) , m_bDirty(false)
@ -56,7 +64,8 @@ CEXIMemoryCard::CEXIMemoryCard(const int index)
m_strFilename = "Movie.raw"; m_strFilename = "Movie.raw";
// we're potentially leaking events here, since there's no UnregisterEvent until emu shutdown, but I guess it's inconsequential // 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; interruptSwitch = 0;
m_bInterruptSet = 0; m_bInterruptSet = 0;
@ -178,6 +187,21 @@ bool CEXIMemoryCard::IsPresent()
return true; 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) void CEXIMemoryCard::SetCS(int cs)
{ {
// So that memory card won't be invalidated during flushing // So that memory card won't be invalidated during flushing
@ -201,11 +225,7 @@ void CEXIMemoryCard::SetCS(int cs)
//??? //???
status |= MC_STATUS_READY; CmdDoneLater(5000);
status &= ~MC_STATUS_BUSY;
m_bInterruptSet = 1;
m_bDirty = true;
} }
break; break;
@ -232,11 +252,7 @@ void CEXIMemoryCard::SetCS(int cs)
address = (address & ~0x1FF) | ((address+1) & 0x1FF); address = (address & ~0x1FF) | ((address+1) & 0x1FF);
} }
status |= MC_STATUS_READY; CmdDoneLater(5000);
status &= ~MC_STATUS_BUSY;
m_bInterruptSet = 1;
m_bDirty = true;
} }
// Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec) // Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec)

View file

@ -47,9 +47,18 @@ private:
// through the userdata parameter, so that it can then call Flush on the right card. // through the userdata parameter, so that it can then call Flush on the right card.
static void FlushCallback(u64 userdata, int cyclesLate); 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. // Flushes the memory card contents to disk.
void Flush(bool exiting = false); 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 enum
{ {
cmdNintendoID = 0x00, cmdNintendoID = 0x00,
@ -71,7 +80,7 @@ private:
std::string m_strFilename; std::string m_strFilename;
int card_index; int card_index;
int et_this_card; int et_this_card, et_cmd_done;
//! memory card state //! memory card state
// STATE_TO_SAVE // STATE_TO_SAVE

View file

@ -98,7 +98,11 @@ void STACKALIGN CheckGatherPipe()
memmove(m_gatherPipe, m_gatherPipe + cnt, m_gatherPipeCount); memmove(m_gatherPipe, m_gatherPipe + cnt, m_gatherPipeCount);
// Profile where the FIFO writes are occurring. // 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()))
{
// 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); jit->js.fifoWriteAddresses.insert(PC);
@ -106,6 +110,7 @@ void STACKALIGN CheckGatherPipe()
jit->GetBlockCache()->InvalidateICache(PC, 4); jit->GetBlockCache()->InvalidateICache(PC, 4);
} }
} }
}
} }
void Write8(const u8 _iValue, const u32 _iAddress) void Write8(const u8 _iValue, const u32 _iAddress)

View file

@ -99,7 +99,7 @@ void Init()
m_ResetCode = 0x80000000; // Cold reset m_ResetCode = 0x80000000; // Cold reset
m_InterruptCause = INT_CAUSE_RST_BUTTON | INT_CAUSE_VI; 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) void Read16(u16& _uReturnValue, const u32 _iAddress)

View file

@ -69,6 +69,19 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength)
*(u32*)&_pBuffer[0] = SI_GC_CONTROLLER; *(u32*)&_pBuffer[0] = SI_GC_CONTROLLER;
break; 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: case CMD_ORIGIN:
{ {
INFO_LOG(SERIALINTERFACE, "PAD - Get Origin"); INFO_LOG(SERIALINTERFACE, "PAD - Get Origin");
@ -96,7 +109,7 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength)
default: default:
{ {
ERROR_LOG(SERIALINTERFACE, "unknown SI command (0x%x)", command); ERROR_LOG(SERIALINTERFACE, "unknown SI command (0x%x)", command);
PanicAlert("SI: Unknown command"); PanicAlert("SI: Unknown command (0x%x)", command);
} }
break; break;
} }

View file

@ -31,6 +31,7 @@ private:
enum EBufferCommands enum EBufferCommands
{ {
CMD_RESET = 0x00, CMD_RESET = 0x00,
CMD_DIRECT = 0x40,
CMD_ORIGIN = 0x41, CMD_ORIGIN = 0x41,
CMD_RECALIBRATE = 0x42, CMD_RECALIBRATE = 0x42,
}; };

View file

@ -74,6 +74,7 @@ IPC_HLE_PERIOD: For the Wiimote this is the call schedule:
#include "Thread.h" #include "Thread.h"
#include "Timer.h" #include "Timer.h"
#include "VideoBackendBase.h" #include "VideoBackendBase.h"
#include "CommandProcessor.h"
namespace SystemTimers namespace SystemTimers
@ -109,7 +110,7 @@ enum
int et_Dec; int et_Dec;
int et_VI; int et_VI;
int et_SI; int et_SI;
int et_AI; int et_CP;
int et_AudioDMA; int et_AudioDMA;
int et_DSP; int et_DSP;
int et_IPC_HLE; 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 // These are badly educated guesses
// Feel free to experiment. Set these in Init below. // Feel free to experiment. Set these in Init below.
int int
// This one should simply be determined by the increasing counter in AI.
AI_PERIOD,
// These shouldn't be period controlled either, most likely. // These shouldn't be period controlled either, most likely.
DSP_PERIOD, DSP_PERIOD,
// This is a fixed value, don't change it // This is a fixed value, don't change it
AUDIO_DMA_PERIOD, 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 // This is completely arbitrary. If we find that we need lower latency, we can just
// increase this number. // increase this number.
IPC_HLE_PERIOD; IPC_HLE_PERIOD;
@ -143,12 +144,6 @@ u32 ConvertMillisecondsToTicks(u32 _Milliseconds)
return GetTicksPerSecond() / 1000 * _Milliseconds; return GetTicksPerSecond() / 1000 * _Milliseconds;
} }
void AICallback(u64 userdata, int cyclesLate)
{
AudioInterface::Update();
CoreTiming::ScheduleEvent(AI_PERIOD - cyclesLate, et_AI);
}
// DSP/CPU timeslicing. // DSP/CPU timeslicing.
void DSPCallback(u64 userdata, int cyclesLate) void DSPCallback(u64 userdata, int cyclesLate)
{ {
@ -187,6 +182,12 @@ void SICallback(u64 userdata, int cyclesLate)
CoreTiming::ScheduleEvent(SerialInterface::GetTicksToNextSIPoll() - cyclesLate, et_SI); 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) void DecrementerCallback(u64 userdata, int cyclesLate)
{ {
PowerPC::ppcState.spr[SPR_DEC] = 0xFFFFFFFF; PowerPC::ppcState.spr[SPR_DEC] = 0xFFFFFFFF;
@ -266,12 +267,12 @@ void Init()
if (DSP::GetDSPEmulator()->IsLLE()) if (DSP::GetDSPEmulator()->IsLLE())
DSP_PERIOD = 12000; // TO BE TWEAKED 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 // System internal sample rate is fixed at 32KHz * 4 (16bit Stereo) / 32 bytes DMA
AUDIO_DMA_PERIOD = CPU_CORE_CLOCK / (AudioInterface::GetAIDSampleRate() * 4 / 32); 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(); Common::Timer::IncreaseResolution();
// store and convert localtime at boot to timebase ticks // store and convert localtime at boot to timebase ticks
CoreTiming::SetFakeTBStartValue((u64)(CPU_CORE_CLOCK / TIMER_RATIO) * (u64)CEXIIPL::GetGCTime()); CoreTiming::SetFakeTBStartValue((u64)(CPU_CORE_CLOCK / TIMER_RATIO) * (u64)CEXIIPL::GetGCTime());
@ -281,19 +282,21 @@ void Init()
CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks()); CoreTiming::SetFakeDecStartTicks(CoreTiming::GetTicks());
et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback); et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback);
et_AI = CoreTiming::RegisterEvent("AICallback", AICallback);
et_VI = CoreTiming::RegisterEvent("VICallback", VICallback); et_VI = CoreTiming::RegisterEvent("VICallback", VICallback);
et_SI = CoreTiming::RegisterEvent("SICallback", SICallback); 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_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback);
et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback); et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback);
et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback); et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback);
et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback); et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback);
CoreTiming::ScheduleEvent(AI_PERIOD, et_AI);
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine(), et_VI); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerLine(), et_VI);
CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP); CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP);
CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_SI); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_SI);
CoreTiming::ScheduleEvent(AUDIO_DMA_PERIOD, et_AudioDMA); 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); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerFrame(), et_PatchEngine);

View file

@ -1043,7 +1043,7 @@ bool PlayWiimote(int wiimote, u8 *data, const WiimoteEmu::ReportFeatures& rptf,
if (size != sizeInMovie) 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); EndPlayInput(!g_bReadOnly);
return false; return false;
} }

View file

@ -193,6 +193,7 @@ void Interpreter::mtmsr(UGeckoInstruction _inst)
{ {
// Privileged? // Privileged?
MSR = m_GPR[_inst.RS]; MSR = m_GPR[_inst.RS];
PowerPC::CheckExceptions();
m_EndBlock = true; m_EndBlock = true;
} }

View file

@ -291,7 +291,9 @@ static void ImHere()
void Jit64::Cleanup() void Jit64::Cleanup()
{ {
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
{
ABI_CallFunction((void *)&GPFifo::CheckGatherPipe); ABI_CallFunction((void *)&GPFifo::CheckGatherPipe);
}
// SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time. // SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time.
if (MMCR0.Hex || MMCR1.Hex) if (MMCR0.Hex || MMCR1.Hex)
@ -301,6 +303,7 @@ void Jit64::Cleanup()
void Jit64::WriteExit(u32 destination, int exit_num) void Jit64::WriteExit(u32 destination, int exit_num)
{ {
Cleanup(); 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) //If nobody has taken care of this yet (this can be removed when all branches are done)
@ -561,6 +564,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32) if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
{ {
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)); ABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
} }

View file

@ -302,6 +302,7 @@ void Jit64::stX(UGeckoInstruction inst)
addr += offset; addr += offset;
if ((addr & 0xFFFFF000) == 0xCC008000 && jo.optimizeGatherPipe) 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); gpr.FlushLockX(ABI_PARAM1);
MOV(32, R(ABI_PARAM1), gpr.R(s)); MOV(32, R(ABI_PARAM1), gpr.R(s));
if (update) if (update)
@ -329,6 +330,7 @@ void Jit64::stX(UGeckoInstruction inst)
} }
else else
{ {
MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write
switch (accessSize) switch (accessSize)
{ {
case 32: ABI_CallFunctionAC(thunks.ProtectFunction(true ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), 2), gpr.R(s), addr); break; case 32: ABI_CallFunctionAC(thunks.ProtectFunction(true ? ((void *)&Memory::Write_U32) : ((void *)&Memory::Write_U32_Swap), 2), gpr.R(s), addr); break;

View file

@ -42,6 +42,7 @@ const u8 GC_ALIGNED16(pbswapShuffle2x4[16]) = {3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10,
#if 0 #if 0
static void WriteDual32(u64 value, u32 address) 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 >> 32), address);
Memory::Write_U32((u32)value, address + 4); Memory::Write_U32((u32)value, address + 4);
} }

View file

@ -28,6 +28,7 @@
#include "Jit.h" #include "Jit.h"
#include "JitRegCache.h" #include "JitRegCache.h"
#include "HW/ProcessorInterface.h"
void Jit64::mtspr(UGeckoInstruction inst) void Jit64::mtspr(UGeckoInstruction inst)
{ {
@ -122,7 +123,29 @@ void Jit64::mtmsr(UGeckoInstruction inst)
gpr.UnlockAll(); gpr.UnlockAll();
gpr.Flush(FLUSH_ALL); gpr.Flush(FLUSH_ALL);
fpr.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); WriteExit(js.compilerPC + 4, 0);
js.firstFPInstructionFound = false; js.firstFPInstructionFound = false;
} }

View file

@ -276,8 +276,8 @@ public:
InstLoc EmitLoadMSR() { InstLoc EmitLoadMSR() {
return FoldZeroOp(LoadMSR, 0); return FoldZeroOp(LoadMSR, 0);
} }
InstLoc EmitStoreMSR(InstLoc val) { InstLoc EmitStoreMSR(InstLoc val, InstLoc pc) {
return FoldUOp(StoreMSR, val); return FoldBiOp(StoreMSR, val, pc);
} }
InstLoc EmitStoreFPRF(InstLoc value) { InstLoc EmitStoreFPRF(InstLoc value) {
return FoldUOp(StoreFPRF, value); return FoldUOp(StoreFPRF, value);

View file

@ -994,8 +994,26 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak
break; break;
} }
case StoreMSR: { case StoreMSR: {
unsigned InstLoc = ibuild->GetImmValue(getOp2(I));
regStoreInstToConstLoc(RI, 32, getOp1(I), &MSR); regStoreInstToConstLoc(RI, 32, getOp1(I), &MSR);
regNormalRegClear(RI, I); 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; break;
} }
case StoreGQR: { case StoreGQR: {

View file

@ -389,7 +389,9 @@ static void ImHere()
void JitIL::Cleanup() void JitIL::Cleanup()
{ {
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
{
ABI_CallFunction((void *)&GPFifo::CheckGatherPipe); ABI_CallFunction((void *)&GPFifo::CheckGatherPipe);
}
// SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time. // SPEED HACK: MMCR0/MMCR1 should be checked at run-time, not at compile time.
if (MMCR0.Hex || MMCR1.Hex) if (MMCR0.Hex || MMCR1.Hex)

View file

@ -106,7 +106,7 @@ void JitIL::mfspr(UGeckoInstruction inst)
// -------------- // --------------
void JitIL::mtmsr(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)); ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4));
} }
// ============== // ==============

View file

@ -148,20 +148,20 @@ void JitArm::DoDownCount()
if(js.downcountAmount < 255) // We can enlarge this if we used rotations if(js.downcountAmount < 255) // We can enlarge this if we used rotations
{ {
SUBS(rB, rB, js.downcountAmount); SUBS(rB, rB, js.downcountAmount);
STR(rA, rB); STR(rB, rA);
} }
else else
{ {
ARMReg rC = gpr.GetReg(false); ARMReg rC = gpr.GetReg(false);
MOVI2R(rC, js.downcountAmount); MOVI2R(rC, js.downcountAmount);
SUBS(rB, rB, rC); SUBS(rB, rB, rC);
STR(rA, rB); STR(rB, rA);
} }
gpr.Unlock(rA, rB); gpr.Unlock(rA, rB);
} }
void JitArm::WriteExitDestInR(ARMReg Reg) void JitArm::WriteExitDestInR(ARMReg Reg)
{ {
STR(R9, Reg, PPCSTATE_OFF(pc)); STR(Reg, R9, PPCSTATE_OFF(pc));
Cleanup(); Cleanup();
DoDownCount(); DoDownCount();
MOVI2R(Reg, (u32)asm_routines.dispatcher); MOVI2R(Reg, (u32)asm_routines.dispatcher);
@ -170,7 +170,7 @@ void JitArm::WriteExitDestInR(ARMReg Reg)
} }
void JitArm::WriteRfiExitDestInR(ARMReg Reg) void JitArm::WriteRfiExitDestInR(ARMReg Reg)
{ {
STR(R9, Reg, PPCSTATE_OFF(pc)); STR(Reg, R9, PPCSTATE_OFF(pc));
Cleanup(); Cleanup();
DoDownCount(); DoDownCount();
@ -209,7 +209,7 @@ void JitArm::WriteExit(u32 destination, int exit_num)
{ {
ARMReg A = gpr.GetReg(false); ARMReg A = gpr.GetReg(false);
MOVI2R(A, destination); MOVI2R(A, destination);
STR(R9, A, PPCSTATE_OFF(pc)); STR(A, R9, PPCSTATE_OFF(pc));
MOVI2R(A, (u32)asm_routines.dispatcher); MOVI2R(A, (u32)asm_routines.dispatcher);
B(A); 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 // Downcount flag check, Only valid for linked blocks
{ {
FixupBranch skip = B_CC(CC_PL); FixupBranch skip = B_CC(CC_PL);
ARMABI_MOVI2M((u32)&PC, js.blockStart);
ARMReg rA = gpr.GetReg(false); ARMReg rA = gpr.GetReg(false);
MOVI2R(rA, js.blockStart);
STR(rA, R9, PPCSTATE_OFF(pc));
MOVI2R(rA, (u32)asm_routines.doTiming); MOVI2R(rA, (u32)asm_routines.doTiming);
B(rA); B(rA);
SetJumpTarget(skip); SetJumpTarget(skip);
@ -384,7 +385,7 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo
LDR(A, R9, PPCSTATE_OFF(msr)); LDR(A, R9, PPCSTATE_OFF(msr));
TST(A, Shift); TST(A, Shift);
FixupBranch b1 = B_CC(CC_NEQ); FixupBranch b1 = B_CC(CC_NEQ);
STR(R9, C, PPCSTATE_OFF(pc)); STR(C, R9, PPCSTATE_OFF(pc));
MOVI2R(A, (u32)asm_routines.fpException); MOVI2R(A, (u32)asm_routines.fpException);
B(A); B(A);
SetJumpTarget(b1); 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 MOVI2R(rA, (u32)&b->runCount); // Load in to register
LDR(rB, rA); // Load the actual value in to R11. LDR(rB, rA); // Load the actual value in to R11.
ADD(rB, rB, 1); // Add one to the value 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 // get start tic
PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStart); PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStart);
gpr.Unlock(rA, rB); 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); MOVI2R(RA, (u32)&opinfo->runCount);
LDR(RB, RA); LDR(RB, RA);
ADD(RB, RB, 1); ADD(RB, RB, 1);
STR(RA, RB); STR(RB, RA);
gpr.Unlock(RA, RB); gpr.Unlock(RA, RB);
} }
if (!ops[i].skip) if (!ops[i].skip)

View file

@ -23,6 +23,7 @@
// locating performance issues. // locating performance issues.
#include "../JitInterface.h" #include "../JitInterface.h"
#include "Jit.h"
#include "JitArmCache.h" #include "JitArmCache.h"
@ -38,7 +39,7 @@ using namespace ArmGen;
ARMXEmitter emit((u8 *)location); ARMXEmitter emit((u8 *)location);
emit.MOVI2R(R11, address); emit.MOVI2R(R11, address);
emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher); emit.MOVI2R(R12, (u32)jit->GetAsmRoutines()->dispatcher);
emit.STR(R9, R11, PPCSTATE_OFF(pc)); emit.STR(R11, R9, PPCSTATE_OFF(pc));
emit.B(R12); emit.B(R12);
} }

View file

@ -48,11 +48,12 @@ void JitArm::sc(UGeckoInstruction inst)
gpr.Flush(); gpr.Flush();
fpr.Flush(); fpr.Flush();
ARMABI_MOVI2M((u32)&PC, js.compilerPC + 4); // Destroys R12 and R14
ARMReg rA = gpr.GetReg(); ARMReg rA = gpr.GetReg();
MOVI2R(rA, js.compilerPC + 4);
STR(rA, R9, PPCSTATE_OFF(pc));
LDR(rA, R9, PPCSTATE_OFF(Exceptions)); LDR(rA, R9, PPCSTATE_OFF(Exceptions));
ORR(rA, rA, EXCEPTION_SYSCALL); ORR(rA, rA, EXCEPTION_SYSCALL);
STR(R9, rA, PPCSTATE_OFF(Exceptions)); STR(rA, R9, PPCSTATE_OFF(Exceptions));
gpr.Unlock(rA); gpr.Unlock(rA);
WriteExceptionExit(); WriteExceptionExit();
@ -84,14 +85,14 @@ void JitArm::rfi(UGeckoInstruction inst)
LDR(rD, R9, PPCSTATE_OFF(msr)); LDR(rD, R9, PPCSTATE_OFF(msr));
AND(rD, rD, rB); // rD = Masked 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 LDR(rB, R9, PPCSTATE_OFF(spr[SPR_SRR1])); // rB contains SRR1 here
AND(rB, rB, rC); // rB contains masked SRR1 here AND(rB, rB, rC); // rB contains masked SRR1 here
ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1 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])); LDR(rA, R9, PPCSTATE_OFF(spr[SPR_SRR0]));
@ -117,7 +118,7 @@ void JitArm::bx(UGeckoInstruction inst)
ARMReg rA = gpr.GetReg(false); ARMReg rA = gpr.GetReg(false);
u32 Jumpto = js.compilerPC + 4; u32 Jumpto = js.compilerPC + 4;
MOVI2R(rA, Jumpto); 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); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
} }
// If this is not the last instruction of a block, // 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])); LDR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR]));
SUBS(rB, rB, 1); 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)); //SUB(32, M(&CTR), Imm8(1));
if (inst.BO & BO_BRANCH_IF_CTR_0) if (inst.BO & BO_BRANCH_IF_CTR_0)
@ -193,7 +194,7 @@ void JitArm::bcx(UGeckoInstruction inst)
{ {
u32 Jumpto = js.compilerPC + 4; u32 Jumpto = js.compilerPC + 4;
MOVI2R(rB, Jumpto); 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 //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4); // Careful, destroys R14, R12
} }
gpr.Unlock(rA, rB); gpr.Unlock(rA, rB);
@ -235,7 +236,7 @@ void JitArm::bcctrx(UGeckoInstruction inst)
{ {
u32 Jumpto = js.compilerPC + 4; u32 Jumpto = js.compilerPC + 4;
MOVI2R(rA, Jumpto); 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); // ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
} }
MVN(rB, 0x3); // 0xFFFFFFFC MVN(rB, 0x3); // 0xFFFFFFFC
@ -269,7 +270,7 @@ void JitArm::bcctrx(UGeckoInstruction inst)
if (inst.LK_3){ if (inst.LK_3){
u32 Jumpto = js.compilerPC + 4; u32 Jumpto = js.compilerPC + 4;
MOVI2R(rB, Jumpto); 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); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
} }
gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR
@ -290,7 +291,7 @@ void JitArm::bclrx(UGeckoInstruction inst)
ARMReg rA = gpr.GetReg(false); ARMReg rA = gpr.GetReg(false);
u32 Jumpto = js.compilerPC + 4; u32 Jumpto = js.compilerPC + 4;
MOVI2R(rA, Jumpto); 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); // ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
} }
return; return;
@ -305,7 +306,7 @@ void JitArm::bclrx(UGeckoInstruction inst)
{ {
LDR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR])); LDR(rB, R9, PPCSTATE_OFF(spr[SPR_CTR]));
SUBS(rB, rB, 1); 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)); //SUB(32, M(&CTR), Imm8(1));
if (inst.BO & BO_BRANCH_IF_CTR_0) if (inst.BO & BO_BRANCH_IF_CTR_0)
@ -341,7 +342,7 @@ void JitArm::bclrx(UGeckoInstruction inst)
if (inst.LK){ if (inst.LK){
u32 Jumpto = js.compilerPC + 4; u32 Jumpto = js.compilerPC + 4;
MOVI2R(rB, Jumpto); 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); //ARMABI_MOVI2M((u32)&LR, js.compilerPC + 4);
} }
gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR gpr.Unlock(rB); // rA gets unlocked in WriteExitDestInR

View file

@ -40,7 +40,7 @@ void JitArm::GenerateRC(int cr) {
SetCC(CC_MI); MOV(rB, 0x8); // Result < 0 SetCC(CC_MI); MOV(rB, 0x8); // Result < 0
SetCC(); SetCC();
STRB(R9, rB, PPCSTATE_OFF(cr_fast) + cr); STRB(rB, R9, PPCSTATE_OFF(cr_fast) + cr);
gpr.Unlock(rB); gpr.Unlock(rB);
} }
void JitArm::ComputeRC(int cr) { void JitArm::ComputeRC(int cr) {
@ -51,7 +51,7 @@ void JitArm::ComputeRC(int cr) {
SetCC(CC_GT); MOV(rB, 0x4); // Result > 0 SetCC(CC_GT); MOV(rB, 0x4); // Result > 0
SetCC(); SetCC();
STRB(R9, rB, PPCSTATE_OFF(cr_fast) + cr); STRB(rB, R9, PPCSTATE_OFF(cr_fast) + cr);
gpr.Unlock(rB); gpr.Unlock(rB);
} }
@ -232,7 +232,7 @@ void JitArm::cmpli(UGeckoInstruction inst)
SetCC(CC_HI); MOV(rA, 0x4); // Result > 0 SetCC(CC_HI); MOV(rA, 0x4); // Result > 0
SetCC(); SetCC();
STRB(R9, rA, PPCSTATE_OFF(cr_fast) + crf); STRB(rA, R9, PPCSTATE_OFF(cr_fast) + crf);
gpr.Unlock(rA); gpr.Unlock(rA);
} }
@ -269,7 +269,7 @@ void JitArm::rlwimix(UGeckoInstruction inst)
ARMReg rB = gpr.GetReg(); ARMReg rB = gpr.GetReg();
MOVI2R(rA, mask); 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) if (inst.Rc)
{ {
BIC (rB, RA, rA); // RA & ~mask BIC (rB, RA, rA); // RA & ~mask
@ -296,7 +296,7 @@ void JitArm::rlwinmx(UGeckoInstruction inst)
ARMReg rA = gpr.GetReg(); ARMReg rA = gpr.GetReg();
MOVI2R(rA, mask); 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) if (inst.Rc)
{ {
ANDS(RA, rA, Shift); ANDS(RA, rA, Shift);

View file

@ -141,7 +141,7 @@ void JitArm::StoreFromReg(ARMReg dest, ARMReg value, int accessSize, s32 offset)
switch (accessSize) switch (accessSize)
{ {
case 32: case 32:
STR(dest, value); // 8 STR(value, dest); // 8
break; break;
case 16: case 16:
// Not implemented // Not implemented

View file

@ -70,7 +70,7 @@ void JitArm::mtspr(UGeckoInstruction inst)
} }
// OK, this is easy. // 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) 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 // Don't interpret this, if we do we get thrown out
//JITDISABLE(SystemRegisters) //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); WriteExit(js.compilerPC + 4, 0);
} }

View file

@ -102,10 +102,10 @@ void JitArmAsmRoutineManager::Generate()
fpException = GetCodePtr(); fpException = GetCodePtr();
LDR(R0, R9, PPCSTATE_OFF(Exceptions)); LDR(R0, R9, PPCSTATE_OFF(Exceptions));
ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE); ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE);
STR(R9, R0, PPCSTATE_OFF(Exceptions)); STR(R0, R9, PPCSTATE_OFF(Exceptions));
QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions);
LDR(R0, R9, PPCSTATE_OFF(npc)); LDR(R0, R9, PPCSTATE_OFF(npc));
STR(R9, R0, PPCSTATE_OFF(pc)); STR(R0, R9, PPCSTATE_OFF(pc));
B(dispatcher); B(dispatcher);
SetJumpTarget(bail); SetJumpTarget(bail);
@ -117,10 +117,10 @@ void JitArmAsmRoutineManager::Generate()
// Does exception checking // Does exception checking
testExceptions = GetCodePtr(); testExceptions = GetCodePtr();
LDR(R0, R9, PPCSTATE_OFF(pc)); LDR(R0, R9, PPCSTATE_OFF(pc));
STR(R9, R0, PPCSTATE_OFF(npc)); STR(R0, R9, PPCSTATE_OFF(npc));
QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions);
LDR(R0, R9, PPCSTATE_OFF(npc)); 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 // Check the state pointer to see if we are exiting
// Gets checked on every exception check // Gets checked on every exception check
MOVI2R(R0, (u32)PowerPC::GetStatePtr()); MOVI2R(R0, (u32)PowerPC::GetStatePtr());

View file

@ -148,7 +148,7 @@ ARMReg ArmRegCache::R(u32 preg)
return ArmCRegs[a].Reg; return ArmCRegs[a].Reg;
} }
// Alright, we couldn't get a free space, dump that least used register // 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); emit->LDR(ArmCRegs[Num].Reg, R9, PPCSTATE_OFF(gpr) + preg * 4);
ArmCRegs[Num].PPCReg = preg; ArmCRegs[Num].PPCReg = preg;
ArmCRegs[Num].LastLoad = 0; ArmCRegs[Num].LastLoad = 0;
@ -160,7 +160,7 @@ void ArmRegCache::Flush()
for(u8 a = 0; a < NUMPPCREG; ++a) for(u8 a = 0; a < NUMPPCREG; ++a)
if (ArmCRegs[a].PPCReg != 33) 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].PPCReg = 33;
ArmCRegs[a].LastLoad = 0; ArmCRegs[a].LastLoad = 0;
} }

View file

@ -32,6 +32,7 @@
#include "../../HW/GPFifo.h" #include "../../HW/GPFifo.h"
#include "../../Core.h" #include "../../Core.h"
#include "JitAsmCommon.h" #include "JitAsmCommon.h"
#include "JitBase.h"
using namespace Gen; using namespace Gen;

View file

@ -81,6 +81,7 @@ namespace JitInterface
default: default:
{ {
PanicAlert("Unrecognizable cpu_core: %d", core); PanicAlert("Unrecognizable cpu_core: %d", core);
jit = NULL;
return NULL; return NULL;
break; break;
} }

View file

@ -51,6 +51,3 @@ namespace JitInterface
extern bool bFakeVMEM; extern bool bFakeVMEM;
extern bool bMMU; extern bool bMMU;
#ifdef _M_ARM
#include "JitArm32/Jit.h"
#endif

View file

@ -168,6 +168,11 @@ void Init(int cpu_core)
} }
default: default:
cpu_core_base = JitInterface::InitJitCore(cpu_core); 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; break;
} }
@ -213,6 +218,8 @@ void SetMode(CoreMode new_mode)
case MODE_JIT: // Switching from interpreter to JIT. case MODE_JIT: // Switching from interpreter to JIT.
// Don't really need to do much. It'll work, the cache will refill itself. // Don't really need to do much. It'll work, the cache will refill itself.
cpu_core_base = JitInterface::GetCore(); 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; break;
} }
} }

View file

@ -875,7 +875,7 @@ void CConfigMain::OnOk(wxCommandEvent& WXUNUSED (event))
{ {
Close(); 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(); SConfig::GetInstance().SaveSettings();
} }

View file

@ -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.")); 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 = 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)")); 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 = 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)")); 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); 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(CPUThread, 0, wxLEFT, 5);
sbCoreOverrides->Add(SkipIdle, 0, wxLEFT, 5); sbCoreOverrides->Add(SkipIdle, 0, wxLEFT, 5);
sbCoreOverrides->Add(MMU, 0, wxLEFT, 5); sbCoreOverrides->Add(MMU, 0, wxLEFT, 5);
sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5);
sbCoreOverrides->Add(TLBHack, 0, wxLEFT, 5); sbCoreOverrides->Add(TLBHack, 0, wxLEFT, 5);
sbCoreOverrides->Add(DCBZOFF, 0, wxLEFT, 5);
sbCoreOverrides->Add(VBeam, 0, wxLEFT, 5); sbCoreOverrides->Add(VBeam, 0, wxLEFT, 5);
sbCoreOverrides->Add(SyncGPU, 0, wxLEFT, 5);
sbCoreOverrides->Add(FastDiscSpeed, 0, wxLEFT, 5); sbCoreOverrides->Add(FastDiscSpeed, 0, wxLEFT, 5);
sbCoreOverrides->Add(BlockMerging, 0, wxLEFT, 5); sbCoreOverrides->Add(BlockMerging, 0, wxLEFT, 5);
sbCoreOverrides->Add(DSPHLE, 0, wxLEFT, 5); sbCoreOverrides->Add(DSPHLE, 0, wxLEFT, 5);
@ -936,6 +939,11 @@ void CISOProperties::LoadGameConfig()
else else
VBeam->Set3StateValue(wxCHK_UNDETERMINED); 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)) if (GameIni.Get("Core", "FastDiscSpeed", &bTemp))
FastDiscSpeed->Set3StateValue((wxCheckBoxState)bTemp); FastDiscSpeed->Set3StateValue((wxCheckBoxState)bTemp);
else else
@ -1025,6 +1033,11 @@ bool CISOProperties::SaveGameConfig()
else else
GameIni.Set("Core", "VBeam", VBeam->Get3StateValue()); 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) if (FastDiscSpeed->Get3StateValue() == wxCHK_UNDETERMINED)
GameIni.DeleteKey("Core", "FastDiscSpeed"); GameIni.DeleteKey("Core", "FastDiscSpeed");
else else

View file

@ -70,7 +70,7 @@ private:
// Core // Core
wxCheckBox *CPUThread, *SkipIdle, *MMU, *DCBZOFF, *TLBHack; wxCheckBox *CPUThread, *SkipIdle, *MMU, *DCBZOFF, *TLBHack;
wxCheckBox *VBeam, *FastDiscSpeed, *BlockMerging, *DSPHLE; wxCheckBox *VBeam, *SyncGPU, *FastDiscSpeed, *BlockMerging, *DSPHLE;
// Wii // Wii
wxCheckBox *EnableWideScreen; wxCheckBox *EnableWideScreen;
// Video // Video
@ -130,6 +130,7 @@ private:
ID_DCBZOFF, ID_DCBZOFF,
ID_TLBHACK, ID_TLBHACK,
ID_VBEAM, ID_VBEAM,
ID_SYNCGPU,
ID_DISCSPEED, ID_DISCSPEED,
ID_MERGEBLOCKS, ID_MERGEBLOCKS,
ID_AUDIO_DSP_HLE, ID_AUDIO_DSP_HLE,

View file

@ -32,6 +32,8 @@
#include "HW/GPFifo.h" #include "HW/GPFifo.h"
#include "HW/Memmap.h" #include "HW/Memmap.h"
#include "DLCache.h" #include "DLCache.h"
#include "HW/SystemTimers.h"
#include "Core.h"
namespace CommandProcessor namespace CommandProcessor
{ {
@ -57,12 +59,15 @@ static bool bProcessFifoAllDistance = false;
volatile bool isPossibleWaitingSetDrawDone = false; volatile bool isPossibleWaitingSetDrawDone = false;
volatile bool isHiWatermarkActive = false; volatile bool isHiWatermarkActive = false;
volatile bool isLoWatermarkActive = false;
volatile bool interruptSet= false; volatile bool interruptSet= false;
volatile bool interruptWaiting= false; volatile bool interruptWaiting= false;
volatile bool interruptTokenWaiting = false; volatile bool interruptTokenWaiting = false;
volatile bool interruptFinishWaiting = false; volatile bool interruptFinishWaiting = false;
volatile bool waitingForPEInterruptDisable = false; volatile bool waitingForPEInterruptDisable = false;
volatile u32 VITicks = CommandProcessor::m_cpClockOrigin;
bool IsOnThread() bool IsOnThread()
{ {
return SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread; return SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread;
@ -88,6 +93,7 @@ void DoState(PointerWrap &p)
p.Do(bProcessFifoToLoWatermark); p.Do(bProcessFifoToLoWatermark);
p.Do(bProcessFifoAllDistance); p.Do(bProcessFifoAllDistance);
p.Do(isHiWatermarkActive); p.Do(isHiWatermarkActive);
p.Do(isLoWatermarkActive);
p.Do(isPossibleWaitingSetDrawDone); p.Do(isPossibleWaitingSetDrawDone);
p.Do(interruptSet); p.Do(interruptSet);
p.Do(interruptWaiting); p.Do(interruptWaiting);
@ -119,7 +125,7 @@ void Init()
m_tokenReg = 0; m_tokenReg = 0;
memset(&fifo,0,sizeof(fifo)); memset(&fifo,0,sizeof(fifo));
fifo.CPCmdIdle = 1 ; fifo.CPCmdIdle = 1;
fifo.CPReadIdle = 1; fifo.CPReadIdle = 1;
fifo.bFF_Breakpoint = 0; fifo.bFF_Breakpoint = 0;
fifo.bFF_HiWatermark = 0; fifo.bFF_HiWatermark = 0;
@ -136,6 +142,7 @@ void Init()
bProcessFifoAllDistance = false; bProcessFifoAllDistance = false;
isPossibleWaitingSetDrawDone = false; isPossibleWaitingSetDrawDone = false;
isHiWatermarkActive = false; isHiWatermarkActive = false;
isLoWatermarkActive = false;
et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); 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) void Write16(const u16 _Value, const u32 _Address)
{ {
INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address); INFO_LOG(COMMANDPROCESSOR, "(write16): 0x%04x @ 0x%08x",_Value,_Address);
switch (_Address & 0xFFF) switch (_Address & 0xFFF)
@ -405,7 +411,8 @@ void Write16(const u16 _Value, const u32 _Address)
{ {
GPFifo::ResetGatherPipe(); GPFifo::ResetGatherPipe();
ResetVideoBuffer(); ResetVideoBuffer();
}else }
else
{ {
ResetVideoBuffer(); ResetVideoBuffer();
} }
@ -461,7 +468,7 @@ void STACKALIGN GatherPipeBursted()
} }
if (IsOnThread()) if (IsOnThread())
SetOverflowStatusFromGatherPipe(); SetCpStatus();
// update the fifo-pointer // update the fifo-pointer
if (fifo.CPWritePointer >= fifo.CPEnd) 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() void SetCpStatus()
{ {
// overflow & underflow check // overflow & underflow check
@ -531,6 +525,8 @@ void SetCpStatus()
fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark); fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark);
// breakpoint // breakpoint
if (Core::IsGPUThread())
{
if (fifo.bFF_BPEnable) if (fifo.bFF_BPEnable)
{ {
if (fifo.CPBreakpoint == fifo.CPReadPointer) if (fifo.CPBreakpoint == fifo.CPReadPointer)
@ -555,6 +551,7 @@ void SetCpStatus()
INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer); INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer);
fifo.bFF_Breakpoint = false; fifo.bFF_Breakpoint = false;
} }
}
bool bpInt = fifo.bFF_Breakpoint && fifo.bFF_BPInt; bool bpInt = fifo.bFF_Breakpoint && fifo.bFF_BPInt;
bool ovfInt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt; bool ovfInt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt;
@ -563,17 +560,27 @@ void SetCpStatus()
bool interrupt = (bpInt || ovfInt || undfInt) && m_CPCtrlReg.GPReadEnable; 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) if (interrupt != interruptSet && !interruptWaiting)
{ {
u64 userdata = interrupt?1:0; u64 userdata = interrupt?1:0;
if (IsOnThread()) if (IsOnThread())
{ {
if(!interrupt || bpInt || undfInt) if(!interrupt || bpInt || undfInt || ovfInt)
{
if (Core::IsGPUThread())
{ {
interruptWaiting = true; interruptWaiting = true;
CommandProcessor::UpdateInterruptsFromVideoBackend(userdata); CommandProcessor::UpdateInterruptsFromVideoBackend(userdata);
} }
else if (Core::IsCPUThread())
{
interruptSet = interrupt;
INFO_LOG(COMMANDPROCESSOR,"Interrupt set");
ProcessorInterface::SetInterrupt(INT_CAUSE_CP, interrupt);
}
}
} }
else else
CommandProcessor::UpdateInterrupts(userdata); CommandProcessor::UpdateInterrupts(userdata);
@ -596,7 +603,7 @@ void ProcessFifoAllDistance()
if (IsOnThread()) if (IsOnThread())
{ {
while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable && while (!CommandProcessor::interruptWaiting && fifo.bFF_GPReadEnable &&
fifo.CPReadWriteDistance && !AtBreakpoint() && !PixelEngine::WaitingForPEInterrupt()) fifo.CPReadWriteDistance && !AtBreakpoint())
Common::YieldCPU(); Common::YieldCPU();
} }
bProcessFifoAllDistance = false; bProcessFifoAllDistance = false;
@ -617,15 +624,11 @@ void SetCpStatusRegister()
{ {
// Here always there is one fifo attached to the GPU // Here always there is one fifo attached to the GPU
m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint; m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint;
m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer) || (fifo.CPReadPointer == fifo.CPBreakpoint) ; m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || (fifo.CPReadPointer == fifo.CPWritePointer);
m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance; m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || !fifo.bFF_GPReadEnable;
m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark; m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark;
m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark; 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); 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" DEBUG_LOG(COMMANDPROCESSOR, "(r) status: iBP %s | fReadIdle %s | fCmdIdle %s | iOvF %s | iUndF %s"
, m_CPStatusReg.Breakpoint ? "ON" : "OFF" , 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 } // end of namespace CommandProcessor

View file

@ -31,6 +31,7 @@ namespace CommandProcessor
extern SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread. 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 isPossibleWaitingSetDrawDone; //This one is used for sync gfx thread and emulator thread.
extern volatile bool isHiWatermarkActive; extern volatile bool isHiWatermarkActive;
extern volatile bool isLoWatermarkActive;
extern volatile bool interruptSet; extern volatile bool interruptSet;
extern volatile bool interruptWaiting; extern volatile bool interruptWaiting;
extern volatile bool interruptTokenWaiting; extern volatile bool interruptTokenWaiting;
@ -140,6 +141,9 @@ union UCPClearReg
UCPClearReg(u16 _hex) {Hex = _hex; } 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 // Init
void Init(); void Init();
void Shutdown(); void Shutdown();
@ -161,11 +165,14 @@ bool AllowIdleSkipping();
void SetCpClearRegister(); void SetCpClearRegister();
void SetCpControlRegister(); void SetCpControlRegister();
void SetCpStatusRegister(); void SetCpStatusRegister();
void SetOverflowStatusFromGatherPipe();
void ProcessFifoToLoWatermark(); void ProcessFifoToLoWatermark();
void ProcessFifoAllDistance(); void ProcessFifoAllDistance();
void ProcessFifoEvents(); void ProcessFifoEvents();
void AbortFrame(); void AbortFrame();
void Update();
extern volatile u32 VITicks;
} // namespace CommandProcessor } // namespace CommandProcessor
#endif // _COMMANDPROCESSOR_H #endif // _COMMANDPROCESSOR_H

View file

@ -26,6 +26,7 @@
#include "Fifo.h" #include "Fifo.h"
#include "HW/Memmap.h" #include "HW/Memmap.h"
#include "Core.h" #include "Core.h"
#include "CoreTiming.h"
volatile bool g_bSkipCurrentFrame = false; volatile bool g_bSkipCurrentFrame = false;
extern u8* g_pVideoData; extern u8* g_pVideoData;
@ -72,6 +73,7 @@ void Fifo_Init()
videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE); videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE);
size = 0; size = 0;
GpuRunningState = false; GpuRunningState = false;
Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin);
} }
void Fifo_Shutdown() void Fifo_Shutdown()
@ -123,7 +125,7 @@ void ReadDataFromFifo(u8* _uData, u32 len)
size -= pos; size -= pos;
if (size + len > FIFO_SIZE) 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); memmove(&videoBuffer[0], &videoBuffer[pos], size);
g_pVideoData = videoBuffer; g_pVideoData = videoBuffer;
@ -147,6 +149,7 @@ void RunGpuLoop()
std::lock_guard<std::mutex> lk(m_csHWVidOccupied); std::lock_guard<std::mutex> lk(m_csHWVidOccupied);
GpuRunningState = true; GpuRunningState = true;
SCPFifoStruct &fifo = CommandProcessor::fifo; SCPFifoStruct &fifo = CommandProcessor::fifo;
u32 cyclesExecuted = 0;
while (GpuRunningState) while (GpuRunningState)
{ {
@ -155,14 +158,19 @@ void RunGpuLoop()
VideoFifo_CheckAsyncRequest(); VideoFifo_CheckAsyncRequest();
CommandProcessor::SetCpStatus(); CommandProcessor::SetCpStatus();
Common::AtomicStore(CommandProcessor::VITicks, CommandProcessor::m_cpClockOrigin);
// check if we are able to run this buffer // 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; if (!GpuRunningState) break;
fifo.isGpuReadingData = true; fifo.isGpuReadingData = true;
CommandProcessor::isPossibleWaitingSetDrawDone = fifo.bFF_GPLinkEnable ? true : false; CommandProcessor::isPossibleWaitingSetDrawDone = fifo.bFF_GPLinkEnable ? true : false;
if (Common::AtomicLoad(CommandProcessor::VITicks) > CommandProcessor::m_cpClockOrigin || !Core::g_CoreStartupParameter.bSyncGPU)
{
u32 readPtr = fifo.CPReadPointer; u32 readPtr = fifo.CPReadPointer;
u8 *uData = Memory::GetPointer(readPtr); u8 *uData = Memory::GetPointer(readPtr);
@ -170,16 +178,21 @@ void RunGpuLoop()
else readPtr += 32; else readPtr += 32;
_assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 , _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); "Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce instabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32);
ReadDataFromFifo(uData, 32); ReadDataFromFifo(uData, 32);
OpcodeDecoder_Run(g_bSkipCurrentFrame); 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::AtomicStore(fifo.CPReadPointer, readPtr);
Common::AtomicAdd(fifo.CPReadWriteDistance, -32); Common::AtomicAdd(fifo.CPReadWriteDistance, -32);
if((GetVideoBufferEndPtr() - g_pVideoData) == 0) if((GetVideoBufferEndPtr() - g_pVideoData) == 0)
Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer); Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer);
}
CommandProcessor::SetCpStatus(); CommandProcessor::SetCpStatus();
// This call is pretty important in DualCore mode and must be called in the FIFO Loop. // This call is pretty important in DualCore mode and must be called in the FIFO Loop.
@ -225,7 +238,7 @@ void RunGpu()
FPURoundMode::SaveSIMDState(); FPURoundMode::SaveSIMDState();
FPURoundMode::LoadDefaultSIMDState(); FPURoundMode::LoadDefaultSIMDState();
ReadDataFromFifo(uData, 32); ReadDataFromFifo(uData, 32);
OpcodeDecoder_Run(g_bSkipCurrentFrame); u32 count = OpcodeDecoder_Run(g_bSkipCurrentFrame);
FPURoundMode::LoadSIMDState(); FPURoundMode::LoadSIMDState();
//DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base"); //DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base");

View file

@ -23,7 +23,7 @@
class PointerWrap; class PointerWrap;
#define FIFO_SIZE (1024*1024) #define FIFO_SIZE (2*1024*1024)
extern volatile bool g_bSkipCurrentFrame; extern volatile bool g_bSkipCurrentFrame;

View file

@ -136,29 +136,38 @@ void ExecuteDisplayList(u32 address, u32 size)
InterpretDisplayList(address, size); InterpretDisplayList(address, size);
} }
bool FifoCommandRunnable() u32 FifoCommandRunnable(u32 &command_size)
{ {
u32 cycleTime = 0;
u32 buffer_size = (u32)(GetVideoBufferEndPtr() - g_pVideoData); u32 buffer_size = (u32)(GetVideoBufferEndPtr() - g_pVideoData);
if (buffer_size == 0) if (buffer_size == 0)
return false; // can't peek return 0; // can't peek
u8 cmd_byte = DataPeek8(0); u8 cmd_byte = DataPeek8(0);
u32 command_size = 0;
switch (cmd_byte) switch (cmd_byte)
{ {
case GX_NOP: // Hm, this means that we scan over nop streams pretty slowly... 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 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 case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that
command_size = 1; command_size = 1;
cycleTime = 6;
break; break;
case GX_LOAD_BP_REG: case GX_LOAD_BP_REG:
command_size = 5; command_size = 5;
cycleTime = 12;
break; break;
case GX_LOAD_CP_REG: case GX_LOAD_CP_REG:
command_size = 6; command_size = 6;
cycleTime = 12;
break; break;
case GX_LOAD_INDX_A: case GX_LOAD_INDX_A:
@ -166,10 +175,39 @@ bool FifoCommandRunnable()
case GX_LOAD_INDX_C: case GX_LOAD_INDX_C:
case GX_LOAD_INDX_D: case GX_LOAD_INDX_D:
command_size = 5; command_size = 5;
cycleTime = 6; // TODO
break; break;
case GX_CMD_CALL_DL: 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; command_size = 9;
cycleTime = 45; // This is unverified
}
break; break;
case GX_LOAD_XF_REG: case GX_LOAD_XF_REG:
@ -181,10 +219,11 @@ bool FifoCommandRunnable()
u32 Cmd2 = DataPeek32(1); u32 Cmd2 = DataPeek32(1);
int transfer_size = ((Cmd2 >> 16) & 15) + 1; int transfer_size = ((Cmd2 >> 16) & 15) + 1;
command_size += transfer_size * 4; command_size += transfer_size * 4;
cycleTime = 18 + 6 * transfer_size;
} }
else else
{ {
return false; return 0;
} }
} }
break; break;
@ -198,10 +237,11 @@ bool FifoCommandRunnable()
command_size = 1 + 2; command_size = 1 + 2;
u16 numVertices = DataPeek16(1); u16 numVertices = DataPeek16(1);
command_size += numVertices * VertexLoaderManager::GetVertexSize(cmd_byte & GX_VAT_MASK); command_size += numVertices * VertexLoaderManager::GetVertexSize(cmd_byte & GX_VAT_MASK);
cycleTime = 1600; // This depends on the number of pixels rendered
} }
else else
{ {
return false; return 0;
} }
} }
else else
@ -248,11 +288,19 @@ bool FifoCommandRunnable()
} }
if (command_size > buffer_size) 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); // 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() 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) u32 totalCycles = 0;
u32 cycles = FifoCommandRunnable();
while (cycles > 0)
{ {
while (FifoCommandRunnable()) skipped_frame ? DecodeSemiNop() : Decode();
Decode(); totalCycles += cycles;
} cycles = FifoCommandRunnable();
else
{
while (FifoCommandRunnable())
DecodeSemiNop();
} }
return totalCycles;
} }

View file

@ -50,6 +50,6 @@ extern bool g_bRecordFifoData;
void OpcodeDecoder_Init(); void OpcodeDecoder_Init();
void OpcodeDecoder_Shutdown(); void OpcodeDecoder_Shutdown();
void OpcodeDecoder_Run(bool skipped_frame); u32 OpcodeDecoder_Run(bool skipped_frame);
void ExecuteDisplayList(u32 address, u32 size); void ExecuteDisplayList(u32 address, u32 size);
#endif // _OPCODE_DECODING_H #endif // _OPCODE_DECODING_H

View file

@ -33,7 +33,6 @@
#include "HW/ProcessorInterface.h" #include "HW/ProcessorInterface.h"
#include "DLCache.h" #include "DLCache.h"
#include "State.h" #include "State.h"
#include "PerfQueryBase.h"
namespace PixelEngine namespace PixelEngine
{ {
@ -113,14 +112,14 @@ static UPEAlphaReadReg m_AlphaRead;
static UPECtrlReg m_Control; static UPECtrlReg m_Control;
//static u16 m_Token; // token value most recently encountered //static u16 m_Token; // token value most recently encountered
static bool g_bSignalTokenInterrupt; volatile u32 g_bSignalTokenInterrupt;
static bool g_bSignalFinishInterrupt; volatile u32 g_bSignalFinishInterrupt;
static int et_SetTokenOnMainThread; static int et_SetTokenOnMainThread;
static int et_SetFinishOnMainThread; static int et_SetFinishOnMainThread;
volatile bool interruptSetToken = false; volatile u32 interruptSetToken = 0;
volatile bool interruptSetFinish = false; volatile u32 interruptSetFinish = 0;
u16 bbox[4]; u16 bbox[4];
bool bbox_active; bool bbox_active;
@ -164,10 +163,10 @@ void Init()
m_AlphaModeConf.Hex = 0; m_AlphaModeConf.Hex = 0;
m_AlphaRead.Hex = 0; m_AlphaRead.Hex = 0;
g_bSignalTokenInterrupt = false; g_bSignalTokenInterrupt = 0;
g_bSignalFinishInterrupt = false; g_bSignalFinishInterrupt = 0;
interruptSetToken = false; interruptSetToken = 0;
interruptSetFinish = false; interruptSetFinish = 0;
et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread); et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread);
et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread); et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread);
@ -214,7 +213,7 @@ void Read16(u16& _uReturnValue, const u32 _iAddress)
break; break;
case PE_TOKEN_REG: case PE_TOKEN_REG:
_uReturnValue = CommandProcessor::fifo.PEToken; _uReturnValue = Common::AtomicLoad(*(volatile u32*)&CommandProcessor::fifo.PEToken);
INFO_LOG(PIXELENGINE, "(r16) TOKEN_REG : %04x", _uReturnValue); INFO_LOG(PIXELENGINE, "(r16) TOKEN_REG : %04x", _uReturnValue);
break; break;
@ -351,8 +350,8 @@ void Write16(const u16 _iValue, const u32 _iAddress)
{ {
UPECtrlReg tmpCtrl(_iValue); UPECtrlReg tmpCtrl(_iValue);
if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = false; if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = 0;
if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = false; if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = 0;
m_Control.PETokenEnable = tmpCtrl.PETokenEnable; m_Control.PETokenEnable = tmpCtrl.PETokenEnable;
m_Control.PEFinishEnable = tmpCtrl.PEFinishEnable; m_Control.PEFinishEnable = tmpCtrl.PEFinishEnable;
@ -399,13 +398,13 @@ void UpdateInterrupts()
void UpdateTokenInterrupt(bool active) void UpdateTokenInterrupt(bool active)
{ {
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active); ProcessorInterface::SetInterrupt(INT_CAUSE_PE_TOKEN, active);
interruptSetToken = active; Common::AtomicStore(interruptSetToken, active ? 1 : 0);
} }
void UpdateFinishInterrupt(bool active) void UpdateFinishInterrupt(bool active)
{ {
ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active); ProcessorInterface::SetInterrupt(INT_CAUSE_PE_FINISH, active);
interruptSetFinish = active; Common::AtomicStore(interruptSetFinish, active ? 1 : 0);
} }
// TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate). // 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 // Called only if BPMEM_PE_TOKEN_INT_ID is ack by GP
void SetToken_OnMainThread(u64 userdata, int cyclesLate) void SetToken_OnMainThread(u64 userdata, int cyclesLate)
{ {
//if (userdata >> 16) // 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
g_bSignalTokenInterrupt = true; // 4-byte padded.
//_dbg_assert_msg_(PIXELENGINE, (CommandProcessor::fifo.PEToken == (userdata&0xFFFF)), "WTF? BPMEM_PE_TOKEN_INT_ID's token != BPMEM_PE_TOKEN_ID's token" ); 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); 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(); UpdateInterrupts();
}
CommandProcessor::interruptTokenWaiting = false; CommandProcessor::interruptTokenWaiting = false;
IncrementCheckContextId(); IncrementCheckContextId();
//}
} }
void SetFinish_OnMainThread(u64 userdata, int cyclesLate) void SetFinish_OnMainThread(u64 userdata, int cyclesLate)
{ {
g_bSignalFinishInterrupt = 1; Common::AtomicStore(*(volatile u32*)&g_bSignalFinishInterrupt, 1);
UpdateInterrupts(); UpdateInterrupts();
CommandProcessor::interruptFinishWaiting = false; CommandProcessor::interruptFinishWaiting = false;
CommandProcessor::isPossibleWaitingSetDrawDone = false; CommandProcessor::isPossibleWaitingSetDrawDone = false;
@ -438,23 +440,13 @@ void SetFinish_OnMainThread(u64 userdata, int cyclesLate)
// THIS IS EXECUTED FROM VIDEO THREAD // THIS IS EXECUTED FROM VIDEO THREAD
void SetToken(const u16 _token, const int _bSetTokenAcknowledge) 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 if (_bSetTokenAcknowledge) // set token INT
{ {
Common::AtomicStore(*(volatile u32*)&g_bSignalTokenInterrupt, 1);
}
Common::AtomicStore(*(volatile u32*)&CommandProcessor::fifo.PEToken, _token);
CommandProcessor::interruptTokenWaiting = true; CommandProcessor::interruptTokenWaiting = true;
CoreTiming::ScheduleEvent_Threadsafe(0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); 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);
}
IncrementCheckContextId(); IncrementCheckContextId();
} }
@ -477,7 +469,6 @@ void ResetSetFinish()
{ {
UpdateFinishInterrupt(false); UpdateFinishInterrupt(false);
g_bSignalFinishInterrupt = false; g_bSignalFinishInterrupt = false;
} }
else else
{ {
@ -491,8 +482,7 @@ void ResetSetToken()
if (g_bSignalTokenInterrupt) if (g_bSignalTokenInterrupt)
{ {
UpdateTokenInterrupt(false); UpdateTokenInterrupt(false);
g_bSignalTokenInterrupt = false; g_bSignalTokenInterrupt = 0;
} }
else else
{ {
@ -500,17 +490,4 @@ void ResetSetToken()
} }
CommandProcessor::interruptTokenWaiting = false; 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 } // end of namespace PixelEngine

View file

@ -81,8 +81,6 @@ void SetToken(const u16 _token, const int _bSetTokenAcknowledge);
void SetFinish(void); void SetFinish(void);
void ResetSetFinish(void); void ResetSetFinish(void);
void ResetSetToken(void); void ResetSetToken(void);
bool WaitingForPEInterrupt();
void ResumeWaitingForPEInterrupt();
// Bounding box functionality. Paper Mario (both) are a couple of the few games that use it. // Bounding box functionality. Paper Mario (both) are a couple of the few games that use it.
extern u16 bbox[4]; extern u16 bbox[4];