mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-09 07:39:26 +01:00
Merge branch 'master' into GLSL-master
Conflicts: .gitignore
This commit is contained in:
commit
382be2aabd
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
|
||||||
|
@ -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 =
|
||||||
|
@ -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 =
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
17
Data/User/GameConfig/GSZP41.ini
Normal file
17
Data/User/GameConfig/GSZP41.ini
Normal 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]
|
10
Data/User/GameConfig/GWLE6L.ini
Normal file
10
Data/User/GameConfig/GWLE6L.ini
Normal 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.
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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]
|
||||||
|
2465
Languages/po/ar.po
2465
Languages/po/ar.po
File diff suppressed because it is too large
Load Diff
3009
Languages/po/ca.po
3009
Languages/po/ca.po
File diff suppressed because it is too large
Load Diff
2357
Languages/po/cs.po
2357
Languages/po/cs.po
File diff suppressed because it is too large
Load Diff
3111
Languages/po/de.po
3111
Languages/po/de.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1874
Languages/po/el.po
1874
Languages/po/el.po
File diff suppressed because it is too large
Load Diff
1630
Languages/po/en.po
1630
Languages/po/en.po
File diff suppressed because it is too large
Load Diff
2504
Languages/po/es.po
2504
Languages/po/es.po
File diff suppressed because it is too large
Load Diff
1960
Languages/po/fa.po
1960
Languages/po/fa.po
File diff suppressed because it is too large
Load Diff
2492
Languages/po/fr.po
2492
Languages/po/fr.po
File diff suppressed because it is too large
Load Diff
1708
Languages/po/he.po
1708
Languages/po/he.po
File diff suppressed because it is too large
Load Diff
2553
Languages/po/hu.po
2553
Languages/po/hu.po
File diff suppressed because it is too large
Load Diff
3118
Languages/po/it.po
3118
Languages/po/it.po
File diff suppressed because it is too large
Load Diff
2464
Languages/po/ja.po
2464
Languages/po/ja.po
File diff suppressed because it is too large
Load Diff
2470
Languages/po/ko.po
2470
Languages/po/ko.po
File diff suppressed because it is too large
Load Diff
2499
Languages/po/nb.po
2499
Languages/po/nb.po
File diff suppressed because it is too large
Load Diff
2790
Languages/po/nl.po
2790
Languages/po/nl.po
File diff suppressed because it is too large
Load Diff
2507
Languages/po/pl.po
2507
Languages/po/pl.po
File diff suppressed because it is too large
Load Diff
2434
Languages/po/pt.po
2434
Languages/po/pt.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3099
Languages/po/ru.po
3099
Languages/po/ru.po
File diff suppressed because it is too large
Load Diff
1826
Languages/po/sr.po
1826
Languages/po/sr.po
File diff suppressed because it is too large
Load Diff
2538
Languages/po/tr.po
2538
Languages/po/tr.po
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
@ -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
|
MOVW(reg, val & 0xFFFF);
|
||||||
if (cpu_info.bArmV7) {
|
MOVT(reg, val, true);
|
||||||
MOVW(reg, val & 0xFFFF);
|
}
|
||||||
MOVT(reg, val, true);
|
else if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) {
|
||||||
}
|
inverse ? MVN(reg, op2) : MOV(reg, op2);
|
||||||
else
|
|
||||||
{
|
|
||||||
// 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)
|
||||||
sys_cache_control(kCacheFunctionPrepareForExecution, start, end - start);
|
if (start != NULL)
|
||||||
|
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 Add = op2 >= 0 ? true : false;
|
// bool Index = op2 != 0 ? true : false;
|
||||||
u32 imm = abs(op2);
|
bool Index = true;
|
||||||
Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (src << 12) | imm);
|
bool Add = op2 >= 0 ? true : false;
|
||||||
|
u32 imm = abs(op2);
|
||||||
|
Write32(condition | (op << 20) | (Index << 24) | (Add << 23) | (src << 16) | (dest << 12) | imm);
|
||||||
}
|
}
|
||||||
void ARMXEmitter::STR (ARMReg 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, ARMReg offset, bool Index, bool Add)
|
void ARMXEmitter::LDR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add)
|
||||||
|
{
|
||||||
|
Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | op2.IMMSR());
|
||||||
|
}
|
||||||
|
void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add)
|
||||||
{
|
{
|
||||||
Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset);
|
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,22 +1079,40 @@ 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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, ARMReg offset, bool Index, bool Add);
|
void LDR (ARMReg dest, ARMReg base, Operand2 op2, bool Index, bool Add);
|
||||||
void LDRH(ARMReg dest, ARMReg src, Operand2 op = 0);
|
void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
||||||
void LDRB(ARMReg dest, ARMReg src, s16 op2 = 0);
|
void LDRH (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
||||||
void STR (ARMReg dest, ARMReg src, s16 op2 = 0);
|
void LDRSH(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
||||||
// Offset adds on to the destination register in STR
|
void LDRB (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
||||||
void STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
void LDRSB(ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
||||||
|
void LDRLIT(ARMReg dest, u32 offset, bool Add);
|
||||||
|
|
||||||
|
void STR (ARMReg result, ARMReg base, s16 op2 = 0);
|
||||||
|
void STRH (ARMReg result, ARMReg base, Operand2 op2 = 0);
|
||||||
|
void STRB (ARMReg result, ARMReg base, s16 op2 = 0);
|
||||||
|
// Offset adds on to the destination register in STR
|
||||||
|
void STR (ARMReg 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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
@ -458,8 +464,17 @@ void Update()
|
|||||||
const u32 Samples = static_cast<u32>(Diff / g_CPUCyclesPerSample);
|
const u32 Samples = static_cast<u32>(Diff / g_CPUCyclesPerSample);
|
||||||
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
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -98,12 +98,17 @@ 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()))
|
||||||
{
|
{
|
||||||
jit->js.fifoWriteAddresses.insert(PC);
|
// Log only stores, fp stores and ps stores, filtering out other instructions arrived via optimizeGatherPipe
|
||||||
|
int type = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type;
|
||||||
|
if (type == OPTYPE_STORE || type == OPTYPE_STOREFP || (type == OPTYPE_PS && GetOpInfo(Memory::ReadUnchecked_U32(PC))->opname=="psq_st"))
|
||||||
|
{
|
||||||
|
jit->js.fifoWriteAddresses.insert(PC);
|
||||||
|
|
||||||
// Invalidate the JIT block so that it gets recompiled with the external exception check included.
|
// Invalidate the JIT block so that it gets recompiled with the external exception check included.
|
||||||
jit->GetBlockCache()->InvalidateICache(PC, 4);
|
jit->GetBlockCache()->InvalidateICache(PC, 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,7 +303,8 @@ 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)
|
||||||
JitBlock *b = js.curBlock;
|
JitBlock *b = js.curBlock;
|
||||||
@ -561,6 +564,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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: {
|
||||||
|
@ -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)
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
// ==============
|
// ==============
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
|
@ -161,13 +161,18 @@ void Init(int cpu_core)
|
|||||||
|
|
||||||
switch (cpu_core)
|
switch (cpu_core)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
cpu_core_base = interpreter;
|
cpu_core_base = interpreter;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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,30 +525,33 @@ void SetCpStatus()
|
|||||||
fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark);
|
fifo.bFF_LoWatermark = (fifo.CPReadWriteDistance < fifo.CPLoWatermark);
|
||||||
|
|
||||||
// breakpoint
|
// breakpoint
|
||||||
if (fifo.bFF_BPEnable)
|
if (Core::IsGPUThread())
|
||||||
{
|
{
|
||||||
if (fifo.CPBreakpoint == fifo.CPReadPointer)
|
if (fifo.bFF_BPEnable)
|
||||||
{
|
{
|
||||||
if (!fifo.bFF_Breakpoint)
|
if (fifo.CPBreakpoint == fifo.CPReadPointer)
|
||||||
{
|
{
|
||||||
INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer);
|
if (!fifo.bFF_Breakpoint)
|
||||||
fifo.bFF_Breakpoint = true;
|
{
|
||||||
IncrementCheckContextId();
|
INFO_LOG(COMMANDPROCESSOR, "Hit breakpoint at %i", fifo.CPReadPointer);
|
||||||
|
fifo.bFF_Breakpoint = true;
|
||||||
|
IncrementCheckContextId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
if (fifo.bFF_Breakpoint)
|
||||||
|
INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer);
|
||||||
|
fifo.bFF_Breakpoint = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (fifo.bFF_Breakpoint)
|
if (fifo.bFF_Breakpoint)
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (fifo.bFF_Breakpoint)
|
|
||||||
INFO_LOG(COMMANDPROCESSOR, "Cleared breakpoint at %i", fifo.CPReadPointer);
|
|
||||||
fifo.bFF_Breakpoint = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bpInt = fifo.bFF_Breakpoint && fifo.bFF_BPInt;
|
bool bpInt = fifo.bFF_Breakpoint && fifo.bFF_BPInt;
|
||||||
bool ovfInt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt;
|
bool ovfInt = fifo.bFF_HiWatermark && fifo.bFF_HiWatermarkInt;
|
||||||
@ -562,17 +559,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)
|
||||||
{
|
{
|
||||||
interruptWaiting = true;
|
if (Core::IsGPUThread())
|
||||||
CommandProcessor::UpdateInterruptsFromVideoBackend(userdata);
|
{
|
||||||
|
interruptWaiting = true;
|
||||||
|
CommandProcessor::UpdateInterruptsFromVideoBackend(userdata);
|
||||||
|
}
|
||||||
|
else if (Core::IsCPUThread())
|
||||||
|
{
|
||||||
|
interruptSet = interrupt;
|
||||||
|
INFO_LOG(COMMANDPROCESSOR,"Interrupt set");
|
||||||
|
ProcessorInterface::SetInterrupt(INT_CAUSE_CP, interrupt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,31 +158,41 @@ 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;
|
||||||
|
|
||||||
u32 readPtr = fifo.CPReadPointer;
|
|
||||||
u8 *uData = Memory::GetPointer(readPtr);
|
|
||||||
|
|
||||||
if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase;
|
if (Common::AtomicLoad(CommandProcessor::VITicks) > CommandProcessor::m_cpClockOrigin || !Core::g_CoreStartupParameter.bSyncGPU)
|
||||||
|
{
|
||||||
|
u32 readPtr = fifo.CPReadPointer;
|
||||||
|
u8 *uData = Memory::GetPointer(readPtr);
|
||||||
|
|
||||||
|
if (readPtr == fifo.CPEnd) readPtr = fifo.CPBase;
|
||||||
else readPtr += 32;
|
else readPtr += 32;
|
||||||
|
|
||||||
_assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 ,
|
|
||||||
"Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce inestabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32);
|
|
||||||
|
|
||||||
ReadDataFromFifo(uData, 32);
|
|
||||||
|
|
||||||
OpcodeDecoder_Run(g_bSkipCurrentFrame);
|
|
||||||
|
|
||||||
Common::AtomicStore(fifo.CPReadPointer, readPtr);
|
_assert_msg_(COMMANDPROCESSOR, (s32)fifo.CPReadWriteDistance - 32 >= 0 ,
|
||||||
Common::AtomicAdd(fifo.CPReadWriteDistance, -32);
|
"Negative fifo.CPReadWriteDistance = %i in FIFO Loop !\nThat can produce instabilty in the game. Please report it.", fifo.CPReadWriteDistance - 32);
|
||||||
if((GetVideoBufferEndPtr() - g_pVideoData) == 0)
|
|
||||||
Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer);
|
ReadDataFromFifo(uData, 32);
|
||||||
|
|
||||||
|
cyclesExecuted = OpcodeDecoder_Run(g_bSkipCurrentFrame);
|
||||||
|
|
||||||
|
if (Common::AtomicLoad(CommandProcessor::VITicks) > cyclesExecuted && Core::g_CoreStartupParameter.bSyncGPU)
|
||||||
|
Common::AtomicAdd(CommandProcessor::VITicks, -(s32)cyclesExecuted);
|
||||||
|
|
||||||
|
Common::AtomicStore(fifo.CPReadPointer, readPtr);
|
||||||
|
Common::AtomicAdd(fifo.CPReadWriteDistance, -32);
|
||||||
|
if((GetVideoBufferEndPtr() - g_pVideoData) == 0)
|
||||||
|
Common::AtomicStore(fifo.SafeCPReadPointer, fifo.CPReadPointer);
|
||||||
|
}
|
||||||
|
|
||||||
CommandProcessor::SetCpStatus();
|
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.
|
||||||
@ -188,7 +201,7 @@ void RunGpuLoop()
|
|||||||
VideoFifo_CheckAsyncRequest();
|
VideoFifo_CheckAsyncRequest();
|
||||||
CommandProcessor::isPossibleWaitingSetDrawDone = false;
|
CommandProcessor::isPossibleWaitingSetDrawDone = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fifo.isGpuReadingData = false;
|
fifo.isGpuReadingData = false;
|
||||||
|
|
||||||
|
|
||||||
@ -217,23 +230,23 @@ bool AtBreakpoint()
|
|||||||
|
|
||||||
void RunGpu()
|
void RunGpu()
|
||||||
{
|
{
|
||||||
SCPFifoStruct &fifo = CommandProcessor::fifo;
|
SCPFifoStruct &fifo = CommandProcessor::fifo;
|
||||||
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() )
|
while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance && !AtBreakpoint() )
|
||||||
{
|
{
|
||||||
u8 *uData = Memory::GetPointer(fifo.CPReadPointer);
|
u8 *uData = Memory::GetPointer(fifo.CPReadPointer);
|
||||||
|
|
||||||
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");
|
||||||
|
|
||||||
if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase;
|
if (fifo.CPReadPointer == fifo.CPEnd) fifo.CPReadPointer = fifo.CPBase;
|
||||||
else fifo.CPReadPointer += 32;
|
else fifo.CPReadPointer += 32;
|
||||||
|
|
||||||
fifo.CPReadWriteDistance -= 32;
|
fifo.CPReadWriteDistance -= 32;
|
||||||
}
|
}
|
||||||
CommandProcessor::SetCpStatus();
|
CommandProcessor::SetCpStatus();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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:
|
||||||
command_size = 9;
|
{
|
||||||
|
// FIXME: Calculate the cycle time of the display list.
|
||||||
|
//u32 address = DataPeek32(1);
|
||||||
|
//u32 size = DataPeek32(5);
|
||||||
|
//u8* old_pVideoData = g_pVideoData;
|
||||||
|
//u8* startAddress = Memory::GetPointer(address);
|
||||||
|
|
||||||
|
//// Avoid the crash if Memory::GetPointer failed ..
|
||||||
|
//if (startAddress != 0)
|
||||||
|
//{
|
||||||
|
// g_pVideoData = startAddress;
|
||||||
|
// u8 *end = g_pVideoData + size;
|
||||||
|
// u32 step = 0;
|
||||||
|
// while (g_pVideoData < end)
|
||||||
|
// {
|
||||||
|
// cycleTime += FifoCommandRunnable(step);
|
||||||
|
// g_pVideoData += step;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// cycleTime = 45;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// reset to the old pointer
|
||||||
|
//g_pVideoData = old_pVideoData;
|
||||||
|
command_size = 9;
|
||||||
|
cycleTime = 45; // This is unverified
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GX_LOAD_XF_REG:
|
case GX_LOAD_XF_REG:
|
||||||
@ -180,11 +218,12 @@ bool FifoCommandRunnable()
|
|||||||
command_size = 1 + 4;
|
command_size = 1 + 4;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
@ -398,14 +397,14 @@ 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;
|
}
|
||||||
IncrementCheckContextId();
|
CommandProcessor::interruptTokenWaiting = false;
|
||||||
//}
|
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
|
||||||
|
@ -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];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user