mirror of
https://github.com/DaedalusX64/daedalus.git
synced 2025-04-02 10:21:48 -04:00
272 lines
8.3 KiB
C++
272 lines
8.3 KiB
C++
/*
|
|
Copyright (C) 2001 StrmnNrmn
|
|
|
|
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 the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
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 for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#ifndef CORE_CPU_H_
|
|
#define CORE_CPU_H_
|
|
|
|
#include <stdlib.h>
|
|
#include <stdalign.h>
|
|
#include "Core/Memory.h"
|
|
#include "Core/R4300Instruction.h"
|
|
#include "Core/R4300OpCode.h"
|
|
#include "Core/TLB.h"
|
|
#include "System/SpinLock.h"
|
|
#include "Utility/Paths.h"
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
enum EDelayType
|
|
{
|
|
NO_DELAY = 0,
|
|
EXEC_DELAY,
|
|
DO_DELAY
|
|
};
|
|
|
|
#ifdef DAEDALUS_BREAKPOINTS_ENABLED
|
|
struct DBG_BreakPoint
|
|
{
|
|
OpCode mOriginalOp;
|
|
bool mEnabled;
|
|
bool mTemporaryDisable; // Set to true when BP is activated - this lets us type
|
|
// go immediately after a bp and execution will resume.
|
|
// otherwise it would keep activating the bp
|
|
// The patch bp r4300 instruction clears this
|
|
// when it is next executed
|
|
};
|
|
#endif
|
|
//
|
|
// CPU Jobs
|
|
//
|
|
#define CPU_CHECK_EXCEPTIONS 0x00000001
|
|
#define CPU_CHECK_INTERRUPTS 0x00000002
|
|
#define CPU_STOP_RUNNING 0x00000008
|
|
#define CPU_CHANGE_CORE 0x00000010
|
|
|
|
//*****************************************************************************
|
|
// External declarations
|
|
//*****************************************************************************
|
|
#ifdef DAEDALUS_BREAKPOINTS_ENABLED
|
|
#include <vector>
|
|
extern std::vector< DBG_BreakPoint > g_BreakPoints;
|
|
#endif
|
|
|
|
// Arbitrary unique numbers for different timing related events:
|
|
enum ECPUEventType
|
|
{
|
|
CPU_EVENT_VBL = 1,
|
|
CPU_EVENT_COMPARE,
|
|
CPU_EVENT_AUDIO,
|
|
CPU_EVENT_SPINT,
|
|
};
|
|
|
|
// In practice there should only ever be 2
|
|
#define MAX_CPU_EVENTS 4
|
|
|
|
struct CPUEvent
|
|
{
|
|
s32 mCount;
|
|
ECPUEventType mEventType;
|
|
};
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
DAEDALUS_STATIC_ASSERT( sizeof( CPUEvent ) == 8 );
|
|
#endif
|
|
|
|
using register_32x64 = REG64[32];
|
|
using register_16x64 = REG64[16];
|
|
using register_32x32 = std::array<REG32,32>;
|
|
//
|
|
// We declare various bits of the CPU state in a struct.
|
|
// During dynarec compilation we can keep the base address of this
|
|
// structure cached in a spare register to avoid expensive
|
|
// address-calculations (primarily on the PSP)
|
|
//
|
|
// Make sure to reflect changes here to DynaRecStubs.S as well //Corn
|
|
//
|
|
// Define to use scratch pad memory located at 0x10000
|
|
//
|
|
//#define USE_SCRATCH_PAD
|
|
|
|
#ifdef USE_SCRATCH_PAD
|
|
struct SCPUState
|
|
#else
|
|
struct alignas(CACHE_ALIGN) SCPUState
|
|
#endif
|
|
{
|
|
register_32x64 CPU; // 0x000 .. 0x100
|
|
register_32x32 CPUControl; // 0x100 .. 0x180
|
|
union
|
|
{
|
|
register_32x32 FPU; // 0x180 .. 0x200 Access FPU as 32 x floats
|
|
register_16x64 FPUD; // 0x180 .. 0x200 Access FPU as 16 x doubles
|
|
};
|
|
register_32x32 FPUControl; // 0x200 .. 0x280
|
|
u32 CurrentPC; // 0x280 .. The current program counter
|
|
u32 TargetPC; // 0x284 .. The PC to branch to
|
|
u32 Delay; // 0x288 .. Delay state (NO_DELAY, EXEC_DELAY, DO_DELAY)
|
|
volatile u32 StuffToDo; // 0x28c .. CPU jobs (see above)
|
|
|
|
REG64 MultLo; // 0x290 ..
|
|
REG64 MultHi; // 0x298
|
|
|
|
REG32 Temp1; // 0x2A0 Temp storage Dynarec
|
|
REG32 Temp2; // 0x2A4 Temp storage Dynarec
|
|
REG32 Temp3; // 0x2A8 Temp storage Dynarec
|
|
REG32 Temp4; // 0x2AC Temp storage Dynarec
|
|
|
|
std::array<CPUEvent, MAX_CPU_EVENTS> Events;
|
|
// CPUEvent Events[ MAX_CPU_EVENTS ]; // 0x2B0 //In practice there should only ever be 2 CPU_EVENTS
|
|
u32 NumEvents;
|
|
|
|
void AddJob( u32 job );
|
|
void ClearJob( u32 job );
|
|
inline u32 GetStuffToDo() const { return StuffToDo; }
|
|
inline bool IsJobSet( u32 job ) const { return ( StuffToDo & job ) != 0; }
|
|
void ClearStuffToDo();
|
|
#ifdef DAEDALUS_ENABLE_SYNCHRONISATION
|
|
void Dump();
|
|
#endif
|
|
};
|
|
|
|
#ifdef USE_SCRATCH_PAD
|
|
extern SCPUState *gPtrCPUState;
|
|
#define gCPUState (*gPtrCPUState)
|
|
#else //USE_SCRATCH_PAD
|
|
extern struct SCPUState gCPUState;
|
|
// ALIGNED_EXTERN(SCPUState, gCPUState, CACHE_ALIGN);
|
|
#endif //USE_SCRATCH_PAD
|
|
|
|
#define gGPR (gCPUState.CPU)
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
bool CPU_RomOpen();
|
|
void CPU_RomClose();
|
|
void CPU_Step();
|
|
void CPU_Skip();
|
|
bool CPU_Run();
|
|
bool CPU_RequestSaveState( const std::filesystem::path &filename );
|
|
bool CPU_RequestLoadState( const std::filesystem::path &filename );
|
|
void CPU_Halt( const char * reason );
|
|
void CPU_SelectCore();
|
|
u32 CPU_GetVideoInterruptEventCount();
|
|
void CPU_SetVideoInterruptEventCount( u32 count );
|
|
void CPU_DynarecEnable();
|
|
void CPU_InvalidateICacheRange( u32 address, u32 length );
|
|
void CPU_InvalidateICache();
|
|
void CPU_SetCompare(u32 value);
|
|
#ifdef DAEDALUS_BREAKPOINTS_ENABLED
|
|
void CPU_AddBreakPoint( u32 address ); // Add a break point at address dwAddress
|
|
void CPU_EnableBreakPoint( u32 address, bool enable ); // Enable/Disable the breakpoint as the specified address
|
|
#endif
|
|
bool CPU_IsRunning();
|
|
void CPU_AddEvent( s32 count, ECPUEventType event_type );
|
|
void CPU_SkipToNextEvent();
|
|
bool CPU_CheckStuffToDo();
|
|
using VblCallbackFn = void(*) (void * arg);
|
|
|
|
void CPU_RegisterVblCallback(VblCallbackFn fn, void * arg);
|
|
void CPU_UnregisterVblCallback(VblCallbackFn fn, void * arg);
|
|
|
|
// For PSP, we just keep running forever. For other platforms we need to bail when the user quits.
|
|
#ifdef DAEDALUS_PSP
|
|
#define CPU_KeepRunning() (1)
|
|
#else
|
|
#define CPU_KeepRunning() (CPU_IsRunning())
|
|
#endif
|
|
|
|
inline void CPU_SetPC( u32 pc ) { gCPUState.CurrentPC = pc; }
|
|
inline void INCREMENT_PC() { gCPUState.CurrentPC += 4; }
|
|
inline void DECREMENT_PC() { gCPUState.CurrentPC -= 4; }
|
|
|
|
inline void CPU_TakeBranch( u32 new_pc ) { gCPUState.TargetPC = new_pc; gCPUState.Delay = DO_DELAY; }
|
|
|
|
|
|
#define COUNTER_INCREMENT_PER_OP 1
|
|
|
|
extern u8 * gLastAddress;
|
|
|
|
// Take advantage of the cooperative multitasking
|
|
// of the PSP to make locking/unlocking as fast as possible.
|
|
//
|
|
extern volatile u32 eventQueueLocked;
|
|
|
|
#define LOCK_EVENT_QUEUE() CSpinLock _lock( &eventQueueLocked )
|
|
#define RESET_EVENT_QUEUE_LOCK() eventQueueLocked = 0;
|
|
|
|
#ifdef FRAGMENT_SIMULATE_EXECUTION
|
|
void CPU_ExecuteOpRaw( u32 count, u32 address, OpCode op_code, CPU_Instruction p_instruction, bool * p_branch_taken );
|
|
#endif
|
|
// Needs to be callable from assembly
|
|
extern "C"
|
|
{
|
|
void CPU_UpdateCounter( u32 ops_executed );
|
|
#ifdef FRAGMENT_SIMULATE_EXECUTION
|
|
void CPU_UpdateCounterNoInterrupt( u32 ops_exexuted );
|
|
#endif
|
|
void CPU_HANDLE_COUNT_INTERRUPT();
|
|
}
|
|
|
|
extern void (* g_pCPUCore)();
|
|
|
|
//***********************************************
|
|
// This function gets called *alot* //Corn
|
|
//***********************************************
|
|
//
|
|
// From ReadAddress
|
|
//
|
|
#define CPU_FETCH_INSTRUCTION(ptr, pc) \
|
|
const MemFuncRead & m( g_MemoryLookupTableRead[ pc >> 18 ] ); \
|
|
if((m.pRead != nullptr) ) \
|
|
{ \
|
|
/* Access through pointer with no function calls at all (Fast) */ \
|
|
ptr = ( m.pRead + pc ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
/* ROM or TLB and possible trigger for an exception (Slow)*/ \
|
|
ptr = (u8*)m.ReadFunc( pc ); \
|
|
if( gCPUState.GetStuffToDo() ) \
|
|
return; \
|
|
}
|
|
|
|
//***********************************************
|
|
//This function gets called *alot* //Corn
|
|
//CPU_ProcessEventCycles
|
|
//***********************************************
|
|
inline bool CPU_ProcessEventCycles( u32 cycles )
|
|
{
|
|
LOCK_EVENT_QUEUE();
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
DAEDALUS_ASSERT( gCPUState.NumEvents > 0, "There are no events" );
|
|
#endif
|
|
gCPUState.Events[ 0 ].mCount -= cycles;
|
|
return gCPUState.Events[ 0 ].mCount <= 0;
|
|
}
|
|
|
|
#ifdef DAEDALUS_PROFILE_EXECUTION
|
|
extern u64 gTotalInstructionsExecuted;
|
|
extern u64 gTotalInstructionsEmulated;
|
|
extern u32 g_HardwareInterrupt;
|
|
#endif
|
|
|
|
#ifdef DAEDALUS_ENABLE_SYNCHRONISATION
|
|
extern u32 CPU_ProduceRegisterHash();
|
|
#endif
|
|
|
|
#endif // CORE_CPU_H_
|