2019-09-09 07:01:26 +00:00
|
|
|
#pragma once
|
|
|
|
#include "common/bitfield.h"
|
|
|
|
#include "cpu_types.h"
|
2020-07-31 07:09:18 +00:00
|
|
|
#include "gte_types.h"
|
2019-09-09 07:01:26 +00:00
|
|
|
#include "types.h"
|
2019-09-14 03:52:57 +00:00
|
|
|
#include <array>
|
2019-10-04 03:41:12 +00:00
|
|
|
#include <optional>
|
2019-09-09 07:01:26 +00:00
|
|
|
|
|
|
|
class StateWrapper;
|
|
|
|
|
|
|
|
namespace CPU {
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
enum : VirtualMemoryAddress
|
|
|
|
{
|
|
|
|
RESET_VECTOR = UINT32_C(0xBFC00000)
|
|
|
|
};
|
|
|
|
enum : PhysicalMemoryAddress
|
|
|
|
{
|
|
|
|
DCACHE_LOCATION = UINT32_C(0x1F800000),
|
|
|
|
DCACHE_LOCATION_MASK = UINT32_C(0xFFFFFC00),
|
|
|
|
DCACHE_OFFSET_MASK = UINT32_C(0x000003FF),
|
2020-08-29 12:07:33 +00:00
|
|
|
DCACHE_SIZE = UINT32_C(0x00000400),
|
|
|
|
ICACHE_SIZE = UINT32_C(0x00001000),
|
|
|
|
ICACHE_SLOTS = ICACHE_SIZE / sizeof(u32),
|
|
|
|
ICACHE_LINE_SIZE = 16,
|
|
|
|
ICACHE_LINES = ICACHE_SIZE / ICACHE_LINE_SIZE,
|
|
|
|
ICACHE_SLOTS_PER_LINE = ICACHE_SLOTS / ICACHE_LINES,
|
2020-10-29 13:04:36 +00:00
|
|
|
ICACHE_TAG_ADDRESS_MASK = 0xFFFFFFF0u,
|
|
|
|
ICACHE_INVALID_BITS = 0x0Fu,
|
2020-08-29 12:07:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
union CacheControl
|
|
|
|
{
|
|
|
|
u32 bits;
|
|
|
|
|
|
|
|
BitField<u32, bool, 0, 1> lock_mode;
|
|
|
|
BitField<u32, bool, 1, 1> invalidate_mode;
|
|
|
|
BitField<u32, bool, 2, 1> tag_test_mode;
|
|
|
|
BitField<u32, bool, 3, 1> dcache_scratchpad;
|
|
|
|
BitField<u32, bool, 7, 1> dcache_enable;
|
|
|
|
BitField<u32, u8, 8, 2> icache_fill_size; // actually dcache? icache always fills to 16 bytes
|
|
|
|
BitField<u32, bool, 11, 1> icache_enable;
|
2020-07-31 07:09:18 +00:00
|
|
|
};
|
2019-11-19 10:30:04 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
struct State
|
2019-09-09 07:01:26 +00:00
|
|
|
{
|
2020-07-31 07:09:18 +00:00
|
|
|
// ticks the CPU has executed
|
|
|
|
TickCount pending_ticks = 0;
|
2020-09-29 13:29:28 +00:00
|
|
|
TickCount downcount = 0;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
Registers regs = {};
|
|
|
|
Cop0Registers cop0_regs = {};
|
|
|
|
Instruction next_instruction = {};
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
// address of the instruction currently being executed
|
|
|
|
Instruction current_instruction = {};
|
|
|
|
u32 current_instruction_pc = 0;
|
|
|
|
bool current_instruction_in_branch_delay_slot = false;
|
|
|
|
bool current_instruction_was_branch_taken = false;
|
|
|
|
bool next_instruction_is_branch_delay_slot = false;
|
|
|
|
bool branch_was_taken = false;
|
|
|
|
bool exception_raised = false;
|
|
|
|
bool interrupt_delay = false;
|
|
|
|
bool frame_done = false;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
// load delays
|
|
|
|
Reg load_delay_reg = Reg::count;
|
|
|
|
u32 load_delay_value = 0;
|
|
|
|
Reg next_load_delay_reg = Reg::count;
|
|
|
|
u32 next_load_delay_value = 0;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-08-29 12:07:33 +00:00
|
|
|
CacheControl cache_control{ 0 };
|
2019-09-21 16:06:47 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
// GTE registers are stored here so we can access them on ARM with a single instruction
|
|
|
|
GTE::Regs gte_regs = {};
|
2019-09-17 04:25:25 +00:00
|
|
|
|
2020-10-18 04:43:55 +00:00
|
|
|
u8* fastmem_base = nullptr;
|
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
// data cache (used as scratchpad)
|
|
|
|
std::array<u8, DCACHE_SIZE> dcache = {};
|
2020-08-29 12:07:33 +00:00
|
|
|
std::array<u32, ICACHE_LINES> icache_tags = {};
|
|
|
|
std::array<u8, ICACHE_SIZE> icache_data = {};
|
2020-07-31 07:09:18 +00:00
|
|
|
};
|
2019-09-17 04:25:25 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
extern State g_state;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
void Initialize();
|
|
|
|
void Shutdown();
|
|
|
|
void Reset();
|
|
|
|
bool DoState(StateWrapper& sw);
|
2020-08-29 12:07:33 +00:00
|
|
|
void ClearICache();
|
2019-09-14 04:29:23 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
/// Executes interpreter loop.
|
|
|
|
void Execute();
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
ALWAYS_INLINE Registers& GetRegs() { return g_state.regs; }
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
ALWAYS_INLINE TickCount GetPendingTicks() { return g_state.pending_ticks; }
|
|
|
|
ALWAYS_INLINE void ResetPendingTicks() { g_state.pending_ticks = 0; }
|
|
|
|
ALWAYS_INLINE void AddPendingTicks(TickCount ticks) { g_state.pending_ticks += ticks; }
|
2019-09-17 13:35:17 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
// state helpers
|
|
|
|
ALWAYS_INLINE bool InUserMode() { return g_state.cop0_regs.sr.KUc; }
|
|
|
|
ALWAYS_INLINE bool InKernelMode() { return !g_state.cop0_regs.sr.KUc; }
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
// 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);
|
|
|
|
|
|
|
|
// External IRQs
|
|
|
|
void SetExternalInterrupt(u8 bit);
|
|
|
|
void ClearExternalInterrupt(u8 bit);
|
|
|
|
|
|
|
|
void DisassembleAndPrint(u32 addr);
|
|
|
|
void DisassembleAndLog(u32 addr);
|
|
|
|
void DisassembleAndPrint(u32 addr, u32 instructions_before, u32 instructions_after);
|
2019-11-03 14:55:07 +00:00
|
|
|
|
|
|
|
// Write to CPU execution log file.
|
|
|
|
void WriteToExecutionLog(const char* format, ...);
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
extern bool TRACE_EXECUTION;
|
|
|
|
extern bool LOG_EXECUTION;
|
2019-09-09 07:01:26 +00:00
|
|
|
|
2020-07-31 07:09:18 +00:00
|
|
|
} // namespace CPU
|