Duckstation/src/core/cpu_core.h

183 lines
5.6 KiB
C
Raw Normal View History

2019-09-09 07:01:26 +00:00
#pragma once
#include "common/bitfield.h"
#include "cpu_types.h"
#include "gte.h"
2019-09-09 07:01:26 +00:00
#include "types.h"
2019-09-14 03:52:57 +00:00
#include <array>
#include <optional>
2019-09-09 07:01:26 +00:00
class StateWrapper;
class Bus;
class System;
2019-09-09 07:01:26 +00:00
namespace CPU {
class CodeCache;
namespace Recompiler {
class CodeGenerator;
class Thunks;
} // namespace Recompiler
2019-09-09 07:01:26 +00:00
class Core
{
public:
2019-09-14 03:52:57 +00:00
static constexpr VirtualMemoryAddress RESET_VECTOR = UINT32_C(0xBFC00000);
static constexpr PhysicalMemoryAddress DCACHE_LOCATION = UINT32_C(0x1F800000);
static constexpr PhysicalMemoryAddress DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00);
static constexpr PhysicalMemoryAddress DCACHE_OFFSET_MASK = UINT32_C(0x000003FF);
static constexpr PhysicalMemoryAddress DCACHE_SIZE = UINT32_C(0x00000400);
friend CodeCache;
friend Recompiler::CodeGenerator;
friend Recompiler::Thunks;
2019-09-09 07:01:26 +00:00
Core();
~Core();
2019-11-11 08:19:57 +00:00
void Initialize(Bus* bus);
2019-09-09 07:01:26 +00:00
void Reset();
bool DoState(StateWrapper& sw);
void Execute();
2019-09-17 04:25:25 +00:00
ALWAYS_INLINE Bus* GetBus() const { return m_bus; }
ALWAYS_INLINE const Registers& GetRegs() const { return m_regs; }
ALWAYS_INLINE Registers& GetRegs() { return m_regs; }
ALWAYS_INLINE TickCount GetPendingTicks() const { return m_pending_ticks; }
ALWAYS_INLINE void ResetPendingTicks() { m_pending_ticks = 0; }
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { m_pending_ticks += ticks; }
ALWAYS_INLINE TickCount GetDowncount() const { return m_downcount; }
ALWAYS_INLINE void SetDowncount(TickCount downcount) { m_downcount = downcount; }
2019-09-17 06:26:00 +00:00
// Sets the PC and flushes the pipeline.
void SetPC(u32 new_pc);
// Memory reads variants which do not raise exceptions.
bool SafeReadMemoryByte(VirtualMemoryAddress addr, u8* value);
bool SafeReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
bool SafeReadMemoryWord(VirtualMemoryAddress addr, u32* value);
bool SafeWriteMemoryByte(VirtualMemoryAddress addr, u8 value);
bool SafeWriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
bool SafeWriteMemoryWord(VirtualMemoryAddress addr, u32 value);
2019-09-17 06:26:00 +00:00
// External IRQs
void SetExternalInterrupt(u8 bit);
void ClearExternalInterrupt(u8 bit);
2019-09-09 07:01:26 +00:00
private:
template<MemoryAccessType type, MemoryAccessSize size>
2019-10-04 10:23:47 +00:00
TickCount DoMemoryAccess(VirtualMemoryAddress address, u32& value);
2019-09-09 07:01:26 +00:00
template<MemoryAccessType type, MemoryAccessSize size>
bool DoAlignmentCheck(VirtualMemoryAddress address);
2019-09-14 03:52:57 +00:00
template<MemoryAccessType type, MemoryAccessSize size>
void DoScratchpadAccess(PhysicalMemoryAddress address, u32& value);
bool ReadMemoryByte(VirtualMemoryAddress addr, u8* value);
bool ReadMemoryHalfWord(VirtualMemoryAddress addr, u16* value);
bool ReadMemoryWord(VirtualMemoryAddress addr, u32* value);
bool WriteMemoryByte(VirtualMemoryAddress addr, u8 value);
bool WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
bool WriteMemoryWord(VirtualMemoryAddress addr, u32 value);
2019-09-09 07:01:26 +00:00
// state helpers
ALWAYS_INLINE bool InUserMode() const { return m_cop0_regs.sr.KUc; }
ALWAYS_INLINE bool InKernelMode() const { return !m_cop0_regs.sr.KUc; }
2019-09-09 07:01:26 +00:00
void DisassembleAndPrint(u32 addr);
2019-11-03 14:55:07 +00:00
void DisassembleAndLog(u32 addr);
void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);
2019-09-09 07:01:26 +00:00
// Updates load delays - call after each instruction
ALWAYS_INLINE void UpdateLoadDelay()
{
// the old value is needed in case the delay slot instruction overwrites the same register
if (m_load_delay_reg != Reg::count)
m_regs.r[static_cast<u8>(m_load_delay_reg)] = m_load_delay_value;
m_load_delay_reg = m_next_load_delay_reg;
m_load_delay_value = m_next_load_delay_value;
m_next_load_delay_reg = Reg::count;
}
2019-09-09 07:01:26 +00:00
// Fetches the instruction at m_regs.npc
bool FetchInstruction();
void ExecuteInstruction();
void ExecuteCop0Instruction();
void ExecuteCop2Instruction();
2019-09-09 07:01:26 +00:00
void Branch(u32 target);
// exceptions
u32 GetExceptionVector(Exception excode) const;
void RaiseException(Exception excode);
2019-10-03 16:48:19 +00:00
void RaiseException(Exception excode, u32 EPC, bool BD, bool BT, u8 CE);
bool HasPendingInterrupt();
void DispatchInterrupt();
2019-09-09 07:01:26 +00:00
// clears pipeline of load/branch delays
void FlushPipeline();
// helper functions for registers which aren't writable
u32 ReadReg(Reg rs);
void WriteReg(Reg rd, u32 value);
// helper for generating a load delay write
void WriteRegDelayed(Reg rd, u32 value);
// write to cache control register
void WriteCacheControl(u32 value);
2019-09-21 16:06:47 +00:00
// read/write cop0 regs
std::optional<u32> ReadCop0Reg(Cop0Reg reg);
2019-09-21 16:06:47 +00:00
void WriteCop0Reg(Cop0Reg reg, u32 value);
2019-09-09 07:01:26 +00:00
Bus* m_bus = nullptr;
2019-09-17 04:25:25 +00:00
// ticks the CPU has executed
TickCount m_pending_ticks = 0;
TickCount m_downcount = MAX_SLICE_SIZE;
2019-09-17 04:25:25 +00:00
2019-09-09 07:01:26 +00:00
Registers m_regs = {};
2019-09-17 06:26:00 +00:00
Cop0Registers m_cop0_regs = {};
2019-09-09 07:01:26 +00:00
Instruction m_next_instruction = {};
// address of the instruction currently being executed
Instruction m_current_instruction = {};
u32 m_current_instruction_pc = 0;
2019-10-03 16:48:19 +00:00
bool m_current_instruction_in_branch_delay_slot = false;
bool m_current_instruction_was_branch_taken = false;
bool m_next_instruction_is_branch_delay_slot = false;
bool m_branch_was_taken = false;
bool m_exception_raised = false;
2019-09-09 07:01:26 +00:00
// load delays
Reg m_load_delay_reg = Reg::count;
u32 m_load_delay_value = 0;
2019-09-09 07:01:26 +00:00
Reg m_next_load_delay_reg = Reg::count;
u32 m_next_load_delay_value = 0;
2019-09-09 07:01:26 +00:00
u32 m_cache_control = 0;
System* m_system = nullptr;
2019-09-09 07:01:26 +00:00
2019-09-14 03:52:57 +00:00
// data cache (used as scratchpad)
std::array<u8, DCACHE_SIZE> m_dcache = {};
GTE::Core m_cop2;
2019-09-09 07:01:26 +00:00
};
2019-10-03 06:45:54 +00:00
extern bool TRACE_EXECUTION;
2019-11-03 14:55:07 +00:00
extern bool LOG_EXECUTION;
// Write to CPU execution log file.
void WriteToExecutionLog(const char* format, ...);
2019-09-09 07:01:26 +00:00
} // namespace CPU
#include "cpu_core.inl"