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:01:49 +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/.
# pragma once
2019-02-18 14:02:39 +01:00
# include "ppsspp_config.h"
2017-07-05 16:46:47 +02:00
# include <cstddef>
2013-12-30 10:17:11 +01:00
# include "Common/CommonTypes.h"
2014-03-15 10:45:39 +01:00
# include "Core/Opcode.h"
2012-11-01 16:19:01 +01:00
2014-03-15 11:22:19 -07:00
class PointerWrap ;
2013-08-24 14:43:49 -07:00
typedef Memory : : Opcode MIPSOpcode ;
2014-12-12 23:48:48 +01:00
// Unlike on the PPC, opcode 0 is not unused and thus we have to choose another fake
// opcode to represent JIT blocks and other emu hacks.
// I've chosen 0x68000000.
# define MIPS_EMUHACK_OPCODE 0x68000000
# define MIPS_EMUHACK_MASK 0xFC000000
# define MIPS_JITBLOCK_MASK 0xFF000000
# define MIPS_EMUHACK_VALUE_MASK 0x00FFFFFF
// There are 2 bits available for sub-opcodes, 0x03000000.
# define EMUOP_RUNBLOCK 0 // Runs a JIT block
# define EMUOP_RETKERNEL 1 // Returns to the simulated PSP kernel from a thread
# define EMUOP_CALL_REPLACEMENT 2
# define MIPS_IS_EMUHACK(op) (((op) & 0xFC000000) == MIPS_EMUHACK_OPCODE) // masks away the subop
# define MIPS_IS_RUNBLOCK(op) (((op) & 0xFF000000) == MIPS_EMUHACK_OPCODE) // masks away the subop
# define MIPS_IS_REPLACEMENT(op) (((op) & 0xFF000000) == (MIPS_EMUHACK_OPCODE | (EMUOP_CALL_REPLACEMENT << 24))) // masks away the subop
# define MIPS_EMUHACK_CALL_REPLACEMENT (MIPS_EMUHACK_OPCODE | (EMUOP_CALL_REPLACEMENT << 24))
2014-10-09 21:38:25 +02:00
enum MIPSGPReg {
2012-11-01 16:19:01 +01:00
MIPS_REG_ZERO = 0 ,
MIPS_REG_COMPILER_SCRATCH = 1 ,
MIPS_REG_V0 = 2 ,
MIPS_REG_V1 = 3 ,
MIPS_REG_A0 = 4 ,
MIPS_REG_A1 = 5 ,
MIPS_REG_A2 = 6 ,
MIPS_REG_A3 = 7 ,
2014-10-05 14:20:30 +02:00
MIPS_REG_A4 = 8 ,
2012-11-01 16:19:01 +01:00
MIPS_REG_A5 = 9 ,
2015-02-19 11:14:23 +01:00
MIPS_REG_T0 = 8 , //alternate names for A4/A5
MIPS_REG_T1 = 9 ,
2014-10-05 14:20:30 +02:00
MIPS_REG_T2 = 10 ,
MIPS_REG_T3 = 11 ,
2013-11-08 12:30:31 +01:00
MIPS_REG_T4 = 12 ,
MIPS_REG_T5 = 13 ,
MIPS_REG_T6 = 14 ,
MIPS_REG_T7 = 15 ,
2012-11-01 16:19:01 +01:00
MIPS_REG_S0 = 16 ,
MIPS_REG_S1 = 17 ,
MIPS_REG_S2 = 18 ,
MIPS_REG_S3 = 19 ,
MIPS_REG_S4 = 20 ,
MIPS_REG_S5 = 21 ,
MIPS_REG_S6 = 22 ,
MIPS_REG_S7 = 23 ,
2013-11-08 12:30:31 +01:00
MIPS_REG_T8 = 24 ,
MIPS_REG_T9 = 25 ,
2012-11-01 16:19:01 +01:00
MIPS_REG_K0 = 26 ,
MIPS_REG_K1 = 27 ,
MIPS_REG_GP = 28 ,
MIPS_REG_SP = 29 ,
MIPS_REG_FP = 30 ,
MIPS_REG_RA = 31 ,
2013-11-08 18:51:49 -08:00
// Not real regs, just for convenience/jit mapping.
2016-05-09 23:47:56 +02:00
// NOTE: These are not the same as the offsets the IR has to use!
2013-11-08 18:51:49 -08:00
MIPS_REG_HI = 32 ,
MIPS_REG_LO = 33 ,
2013-11-11 16:46:22 +01:00
MIPS_REG_FPCOND = 34 ,
2013-11-12 11:48:38 +01:00
MIPS_REG_VFPUCC = 35 ,
2014-10-09 21:38:25 +02:00
MIPS_REG_INVALID = - 1 ,
2012-11-01 16:19:01 +01:00
} ;
2014-10-09 21:38:25 +02:00
enum {
2012-11-01 16:19:01 +01:00
VFPU_CTRL_SPREFIX ,
VFPU_CTRL_TPREFIX ,
VFPU_CTRL_DPREFIX ,
VFPU_CTRL_CC ,
VFPU_CTRL_INF4 ,
VFPU_CTRL_RSV5 ,
VFPU_CTRL_RSV6 ,
VFPU_CTRL_REV ,
VFPU_CTRL_RCX0 ,
VFPU_CTRL_RCX1 ,
VFPU_CTRL_RCX2 ,
VFPU_CTRL_RCX3 ,
VFPU_CTRL_RCX4 ,
VFPU_CTRL_RCX5 ,
VFPU_CTRL_RCX6 ,
VFPU_CTRL_RCX7 ,
2012-11-22 20:14:24 +01:00
VFPU_CTRL_MAX ,
2012-11-01 16:19:01 +01:00
//unknown....
} ;
2013-07-31 22:29:16 +02:00
enum VCondition
{
VC_FL ,
VC_EQ ,
VC_LT ,
VC_LE ,
VC_TR ,
VC_NE ,
VC_GE ,
VC_GT ,
VC_EZ ,
VC_EN ,
VC_EI ,
VC_ES ,
VC_NZ ,
VC_NN ,
VC_NI ,
VC_NS
} ;
2013-11-27 22:45:17 +01:00
// In memory, we order the VFPU registers differently.
// Games use columns a whole lot more than rows, and it would thus be good if columns
// were contiguous in memory. Also, matrices aren't but should be.
extern u8 voffset [ 128 ] ;
extern u8 fromvoffset [ 128 ] ;
2017-03-02 12:36:54 +01:00
enum class CPUCore ;
2019-02-18 14:02:39 +01:00
# if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
2017-07-05 13:12:42 +02:00
// Note that CTXREG is offset to point at the first floating point register, intentionally. This is so that a byte offset
// can reach both GPR and FPR regs.
2017-07-07 12:48:17 +02:00
# define MIPSSTATE_VAR(x) MDisp(X64JitConstants::CTXREG, \
( int ) ( offsetof ( MIPSState , x ) - offsetof ( MIPSState , f [ 0 ] ) ) )
// Workaround for compilers that don't like dynamic indexing in offsetof
# define MIPSSTATE_VAR_ELEM32(x, i) MDisp(X64JitConstants::CTXREG, \
2017-07-07 15:46:14 +02:00
( int ) ( offsetof ( MIPSState , x ) - offsetof ( MIPSState , f [ 0 ] ) + ( i ) * 4 ) )
2017-07-05 13:12:42 +02:00
2017-07-05 13:51:27 +02:00
// To get RIP/relative addressing (requires tight memory control so generated code isn't too far from the binary, and a reachable variable called mips):
2021-02-25 20:20:36 -08:00
// #define MIPSSTATE_VAR(x) M(&mips_->x)
2017-07-05 13:51:27 +02:00
2017-07-05 13:12:42 +02:00
# endif
2017-07-05 14:23:52 +02:00
enum {
NUM_X86_FPU_TEMPS = 16 ,
} ;
2013-01-08 14:20:06 +01:00
class MIPSState
2012-11-01 16:19:01 +01:00
{
public :
MIPSState ( ) ;
~ MIPSState ( ) ;
2014-01-28 11:32:54 +01:00
void Init ( ) ;
void Shutdown ( ) ;
2012-11-01 16:19:01 +01:00
void Reset ( ) ;
2014-05-26 23:55:48 -07:00
void UpdateCore ( CPUCore desired ) ;
2014-01-28 11:32:54 +01:00
2012-12-27 20:33:10 -08:00
void DoState ( PointerWrap & p ) ;
2012-11-01 16:19:01 +01:00
2016-05-09 23:47:56 +02:00
// MUST start with r and be followed by f, v, and t!
2012-11-01 16:19:01 +01:00
u32 r [ 32 ] ;
2013-06-11 21:36:59 +02:00
union {
float f [ 32 ] ;
u32 fi [ 32 ] ;
int fs [ 32 ] ;
} ;
union {
float v [ 128 ] ;
u32 vi [ 128 ] ;
} ;
2016-05-06 23:45:37 +02:00
2016-05-09 23:47:56 +02:00
// Register-allocated JIT Temps don't get flushed so we don't reserve space for them.
// However, the IR interpreter needs some temps that can stick around between ops.
// Can be indexed through r[] using indices 192+.
u32 t [ 16 ] ; //192
2013-07-31 10:33:44 +02:00
// If vfpuCtrl (prefixes) get mysterious values, check the VFPU regcache code.
2016-05-09 23:47:56 +02:00
u32 vfpuCtrl [ 16 ] ; // 208
2012-11-01 16:19:01 +01:00
2016-05-11 00:16:07 +02:00
float vt [ 16 ] ; //224 TODO: VFPU temp
2015-07-02 20:23:27 -07:00
// ARM64 wants lo/hi to be aligned to 64 bits from the base of this struct.
2016-05-11 00:16:07 +02:00
u32 padLoHi ; // 240
2015-07-02 20:23:27 -07:00
2013-08-15 01:26:16 -07:00
union {
struct {
2016-05-11 00:16:07 +02:00
u32 pc ; //241
2013-08-15 01:26:16 -07:00
2016-05-11 00:16:07 +02:00
u32 lo ; //242
u32 hi ; //243
2013-08-15 01:26:16 -07:00
2016-05-11 00:16:07 +02:00
u32 fcr31 ; //244 fpu control register
u32 fpcond ; //245 cache the cond flag of fcr31 (& 1 << 23)
2013-08-15 01:26:16 -07:00
} ;
u32 other [ 6 ] ;
} ;
2012-11-01 16:19:01 +01:00
u32 nextPC ;
2013-01-17 01:14:49 -08:00
int downcount ; // This really doesn't belong here, it belongs in CoreTiming. But you gotta do what you gotta do, this needs to be reachable in the ARM JIT.
2013-01-12 00:44:18 +01:00
2012-11-01 16:19:01 +01:00
bool inDelaySlot ;
2012-11-07 17:34:25 +01:00
int llBit ; // ll/sc
2013-11-12 14:42:42 +01:00
u32 temp ; // can be used to save temporaries during calculations when we need more than R0 and R1
2017-07-05 14:10:36 +02:00
u32 mxcsrTemp ;
2017-07-05 12:45:56 +02:00
// Temporary used around delay slots and similar.
u64 saved_flags ;
2012-11-01 16:19:01 +01:00
// Debug stuff
u32 debugCount ; // can be used to count basic blocks before crashes, etc.
2017-07-05 14:19:54 +02:00
// Temps needed for JitBranch.cpp experiments
u32 intBranchExit ;
u32 jitBranchExit ;
2017-07-05 15:06:44 +02:00
u32 savedPC ;
2017-08-31 01:14:51 +02:00
alignas ( 16 ) u32 vcmpResult [ 4 ] ;
2017-07-05 15:59:17 +02:00
2017-07-05 16:44:22 +02:00
float sincostemp [ 2 ] ;
2013-11-13 22:41:25 -08:00
static const u32 FCR0_VALUE = 0x00003351 ;
2019-02-18 14:02:39 +01:00
# if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64)
2017-07-05 14:23:52 +02:00
// FPU TEMP0, etc. are swapped in here if necessary (e.g. on x86.)
float tempValues [ NUM_X86_FPU_TEMPS ] ;
# endif
2013-02-15 22:56:38 +01:00
u8 VfpuWriteMask ( ) const {
return ( vfpuCtrl [ VFPU_CTRL_DPREFIX ] > > 8 ) & 0xF ;
}
bool VfpuWriteMask ( int i ) const {
return ( vfpuCtrl [ VFPU_CTRL_DPREFIX ] > > ( 8 + i ) ) & 1 ;
}
2012-11-01 16:19:01 +01:00
2014-06-27 23:21:22 -07:00
bool HasDefaultPrefix ( ) const ;
2012-11-01 16:19:01 +01:00
void SingleStep ( ) ;
2012-11-19 14:16:37 +01:00
int RunLoopUntil ( u64 globalTicks ) ;
2013-09-01 00:32:17 -07:00
// To clear jit caches, etc.
void InvalidateICache ( u32 address , int length = 4 ) ;
2014-12-12 23:48:48 +01:00
void ClearJitCache ( ) ;
2022-10-09 21:26:13 -07:00
void ProcessPendingClears ( ) ;
// Doesn't need save stating.
volatile bool insideJit = false ;
volatile bool hasPendingClears = false ;
2012-11-01 16:19:01 +01:00
} ;
class MIPSDebugInterface ;
//The one we are compiling or running currently
extern MIPSState * currentMIPS ;
extern MIPSDebugInterface * currentDebugMIPS ;
extern MIPSState mipsr4k ;
2013-07-27 22:14:01 +02:00
extern const float cst_constants [ 32 ] ;