Files
ppsspp/Core/MIPS/ARM/ArmAsm.cpp

204 lines
6.1 KiB
C++
Raw Normal View History

2012-11-01 16:19:01 +01:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 23:58:25 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
#include "Core/System.h"
#include "Core/CoreTiming.h"
2012-11-01 16:19:01 +01:00
#include "MemoryUtil.h"
#include "ArmEmitter.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/MIPS/ARM/ArmJit.h"
#include "Core/MIPS/ARM/ArmAsm.h"
2012-11-01 16:19:01 +01:00
using namespace ArmGen;
//static int temp32; // unused?
static const bool enableDebug = false;
2012-11-01 16:19:01 +01:00
//static bool enableStatistics = false; //unused?
//The standard ARM calling convention allocates the 16 ARM registers as:
// r15 is the program counter.
// r14 is the link register. (The BL instruction, used in a subroutine call, stores the return address in this register).
// r13 is the stack pointer. (The Push/Pop instructions in "Thumb" operating mode use this register only).
// r12 is the Intra-Procedure-call scratch register.
// r4 to r11: used to hold local variables.
// r0 to r3: used to hold argument values passed to a subroutine, and also hold results returned from a subroutine.
// Mappable registers:
// R2, R3, R4, R5, R6, R8, R11
2012-11-01 16:19:01 +01:00
// STATIC ALLOCATION ARM:
2013-01-08 00:26:42 +01:00
// R10 : MIPS state
// R11 : Memory base pointer.
// R7 : Down counter
2012-11-01 16:19:01 +01:00
extern volatile CoreState coreState;
void JitAt()
2012-11-01 16:19:01 +01:00
{
MIPSComp::jit->Compile(currentMIPS->pc);
}
void ShowPC(u32 sp) {
2013-01-08 21:24:42 +01:00
if (currentMIPS) {
ERROR_LOG(JIT, "ShowPC : %08x ArmSP : %08x", currentMIPS->pc, sp);
2013-01-08 21:24:42 +01:00
} else {
ERROR_LOG(JIT, "Universe corrupt?");
2013-01-08 21:24:42 +01:00
}
}
2013-01-08 21:24:42 +01:00
void DisassembleArm(const u8 *data, int size);
2012-11-01 16:19:01 +01:00
// PLAN: no more block numbers - crazy opcodes just contain offset within
// dynarec buffer
// At this offset - 4, there is an int specifying the block number.
namespace MIPSComp {
void Jit::GenerateFixedCode()
2012-11-01 16:19:01 +01:00
{
enterCode = AlignCode16();
2013-12-09 13:45:17 +01:00
DEBUG_LOG(JIT, "Base: %08x", (u32)Memory::base);
2012-11-01 16:19:01 +01:00
SetCC(CC_AL);
PUSH(9, R4, R5, R6, R7, R8, R9, R10, R11, R_LR);
// Take care to 8-byte align stack for function calls.
2013-01-30 00:02:04 +01:00
// We are misaligned here because of an odd number of args for PUSH.
// It's not like x86 where you need to account for an extra 4 bytes
// consumed by CALL.
SUB(R_SP, R_SP, 4);
2013-01-30 00:02:04 +01:00
// Now we are correctly aligned and plan to stay that way.
// TODO: R12 should be usable for regalloc but will get thrashed by C code.
// Fixed registers, these are always kept when in Jit context.
// R8 is used to hold flags during delay slots. Not always needed.
// R13 cannot be used as it's the stack pointer.
2013-01-30 00:02:04 +01:00
// TODO: Consider statically allocating:
// * r2-r4
2013-01-30 00:02:04 +01:00
// Really starting to run low on registers already though...
2013-01-30 21:49:20 +01:00
MOVI2R(R11, (u32)Memory::base);
MOVI2R(R10, (u32)mips_);
MOVI2R(R9, (u32)GetBasePtr());
RestoreDowncount();
MovFromPC(R0);
outerLoopPCInR0 = GetCodePtr();
MovToPC(R0);
2012-11-01 16:19:01 +01:00
outerLoop = GetCodePtr();
SaveDowncount();
QuickCallFunction(R0, (void *)&CoreTiming::Advance);
RestoreDowncount();
2012-11-01 16:19:01 +01:00
FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time
dispatcherCheckCoreState = GetCodePtr();
// The result of slice decrementation should be in flags if somebody jumped here
// IMPORTANT - We jump on negative, not carry!!!
FixupBranch bailCoreState = B_CC(CC_MI);
2013-01-30 21:49:20 +01:00
MOVI2R(R0, (u32)&coreState);
2013-01-08 00:26:42 +01:00
LDR(R0, R0);
CMP(R0, 0);
FixupBranch badCoreState = B_CC(CC_NEQ);
FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time
dispatcherPCInR0 = GetCodePtr();
2013-12-09 13:45:17 +01:00
// TODO: Do we always need to write PC to RAM here?
MovToPC(R0);
// At this point : flags = EQ. Fine for the next check, no need to jump over it.
2012-11-01 16:19:01 +01:00
dispatcher = GetCodePtr();
2013-01-08 21:24:42 +01:00
2012-11-01 16:19:01 +01:00
// The result of slice decrementation should be in flags if somebody jumped here
// IMPORTANT - We jump on negative, not carry!!!
2013-01-08 21:24:42 +01:00
FixupBranch bail = B_CC(CC_MI);
2013-01-08 00:26:42 +01:00
2012-11-01 16:19:01 +01:00
SetJumpTarget(skipToRealDispatch);
SetJumpTarget(skipToRealDispatch2);
2012-11-01 16:19:01 +01:00
dispatcherNoCheck = GetCodePtr();
2013-01-08 00:26:42 +01:00
// Debug
if (enableDebug) {
MOV(R0, R13);
QuickCallFunction(R1, (void *)&ShowPC);
}
LDR(R0, CTXREG, offsetof(MIPSState, pc));
2013-12-09 13:45:17 +01:00
// TODO: In practice, do we ever run code from uncached space (| 0x40000000)? If not, we can remove this BIC.
BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF
LDR(R0, MEMBASEREG, R0);
2013-12-18 00:09:08 +01:00
AND(R1, R0, Operand2(0xFF, 4)); // rotation is to the right, in 2-bit increments.
BIC(R0, R0, Operand2(0xFF, 4));
2013-01-08 00:26:42 +01:00
CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4));
SetCC(CC_EQ);
// IDEA - we have 26 bits, why not just use offsets from base of code?
// Another idea: Shift the bloc number left by two in the op, this would let us do
// LDR(R0, R9, R0); here, replacing the next instructions.
#ifdef IOS
// TODO: Fix me, I'm ugly.
MOVI2R(R9, (u32)GetBasePtr());
#endif
ADD(R0, R0, R9);
B(R0);
SetCC(CC_AL);
2012-11-01 16:19:01 +01:00
2013-12-09 13:45:17 +01:00
// No block found, let's jit
SaveDowncount();
QuickCallFunction(R2, (void *)&JitAt);
RestoreDowncount();
2013-01-08 00:26:42 +01:00
B(dispatcherNoCheck); // no point in special casing this
2012-11-01 16:19:01 +01:00
SetJumpTarget(bail);
SetJumpTarget(bailCoreState);
2012-11-01 16:19:01 +01:00
2013-01-30 21:49:20 +01:00
MOVI2R(R0, (u32)&coreState);
2013-01-08 00:26:42 +01:00
LDR(R0, R0);
CMP(R0, 0);
B_CC(CC_EQ, outerLoop);
2012-11-01 16:19:01 +01:00
2013-01-08 00:26:42 +01:00
SetJumpTarget(badCoreState);
2013-01-08 21:24:42 +01:00
breakpointBailout = GetCodePtr();
SaveDowncount();
2012-11-01 16:19:01 +01:00
ADD(R_SP, R_SP, 4);
POP(9, R4, R5, R6, R7, R8, R9, R10, R11, R_PC); // Returns
2013-01-08 00:26:42 +01:00
2013-01-09 00:12:02 +01:00
// Uncomment if you want to see the output...
// INFO_LOG(JIT, "THE DISASM ========================");
2013-01-09 00:12:02 +01:00
// DisassembleArm(enterCode, GetCodePtr() - enterCode);
// INFO_LOG(JIT, "END OF THE DISASM ========================");
// Don't forget to zap the instruction cache!
FlushLitPool();
FlushIcache();
}
} // namespace MIPSComp