Initial commit

This commit is contained in:
Connor McLaughlin
2019-09-09 17:01:26 +10:00
commit 2149ab4d69
237 changed files with 493349 additions and 0 deletions

16
src/pse/CMakeLists.txt Normal file
View File

@ -0,0 +1,16 @@
add_library(pse
cpu_bus.cpp
cpu_bus.h
cpu_bus.inl
cpu_core.cpp
cpu_core.h
cpu_core.inl
system.cpp
system.h
types.h
)
target_include_directories(pse PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(pse PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(pse Threads::Threads YBaseLib common)

220
src/pse/bus.cpp Normal file
View File

@ -0,0 +1,220 @@
#include "bus.h"
#include "YBaseLib/ByteStream.h"
#include "YBaseLib/Log.h"
#include "YBaseLib/String.h"
#include "dma.h"
#include <cstdio>
Log_SetChannel(Bus);
Bus::Bus() = default;
Bus::~Bus() = default;
bool Bus::Initialize(System* system, DMA* dma, GPU* gpu)
{
if (!LoadBIOS())
return false;
m_dma = dma;
m_gpu = gpu;
return true;
}
void Bus::Reset()
{
m_ram.fill(static_cast<u8>(0));
}
bool Bus::DoState(StateWrapper& sw)
{
return false;
}
bool Bus::ReadByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8* value)
{
u32 temp = 0;
const bool result = DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(cpu_address, bus_address, temp);
*value = Truncate8(temp);
return result;
}
bool Bus::ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value)
{
u32 temp = 0;
const bool result =
DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(cpu_address, bus_address, temp);
*value = Truncate16(temp);
return result;
}
bool Bus::ReadDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value)
{
return DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(cpu_address, bus_address, *value);
}
bool Bus::WriteByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8 value)
{
u32 temp = ZeroExtend32(value);
return DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(cpu_address, bus_address, temp);
}
bool Bus::WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value)
{
u32 temp = ZeroExtend32(value);
return DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(cpu_address, bus_address, temp);
}
bool Bus::WriteDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value)
{
return DispatchAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(cpu_address, bus_address, value);
}
bool Bus::LoadBIOS()
{
std::FILE* fp = std::fopen("SCPH1001.BIN", "rb");
if (!fp)
return false;
std::fseek(fp, 0, SEEK_END);
const u32 size = static_cast<u32>(std::ftell(fp));
std::fseek(fp, 0, SEEK_SET);
if (size != m_bios.size())
{
Log_ErrorPrintf("BIOS image mismatch, expecting %u bytes, got %u bytes", static_cast<u32>(m_bios.size()), size);
std::fclose(fp);
return false;
}
if (std::fread(m_bios.data(), 1, m_bios.size(), fp) != m_bios.size())
{
Log_ErrorPrintf("Failed to read BIOS image");
std::fclose(fp);
return false;
}
std::fclose(fp);
#if 1
auto Patch = [this](u32 address, u32 value) { std::memcpy(&m_bios[address], &value, sizeof(value)); };
Patch(0x6F0C, 0x24010001); // addiu $at, $zero, 1
Patch(0x6F14, 0xaf81a9c0); // sw at, -0x5640(gp)
#endif
return true;
}
bool Bus::DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress cpu_address,
PhysicalMemoryAddress bus_address, u32& value)
{
SmallString str;
str.AppendString("Invalid bus ");
if (size == MemoryAccessSize::Byte)
str.AppendString("byte");
if (size == MemoryAccessSize::HalfWord)
str.AppendString("word");
if (size == MemoryAccessSize::Word)
str.AppendString("dword");
str.AppendCharacter(' ');
if (type == MemoryAccessType::Read)
str.AppendString("read");
else
str.AppendString("write");
str.AppendFormattedString(" at address 0x%08X (virtual address 0x%08X)", bus_address, cpu_address);
if (type == MemoryAccessType::Write)
str.AppendFormattedString(" (value 0x%08X)", value);
Log_ErrorPrint(str);
if (type == MemoryAccessType::Read)
value = UINT32_C(0xFFFFFFFF);
return true;
}
bool Bus::ReadExpansionRegion2(MemoryAccessSize size, u32 offset, u32& value)
{
offset &= EXP2_MASK;
// rx/tx buffer empty
if (offset == 0x21)
{
value = 0x04 | 0x08;
return true;
}
return DoInvalidAccess(MemoryAccessType::Read, size, EXP2_BASE | offset, EXP2_BASE | offset, value);
}
bool Bus::WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value)
{
offset &= EXP2_MASK;
if (offset == 0x23)
{
if (value == '\r')
return true;
if (value == '\n')
{
if (!m_tty_line_buffer.IsEmpty())
Log_InfoPrintf("TTY: %s", m_tty_line_buffer.GetCharArray());
m_tty_line_buffer.Clear();
}
else
{
m_tty_line_buffer.AppendCharacter(Truncate8(value));
}
return true;
}
if (offset == 0x41)
{
Log_WarningPrintf("BIOS POST status: %02X", value & UINT32_C(0x0F));
return true;
}
return DoInvalidAccess(MemoryAccessType::Write, size, EXP2_BASE | offset, EXP2_BASE | offset, value);
}
bool Bus::ReadSPU(MemoryAccessSize size, u32 offset, u32& value)
{
if (offset == 0x1AE)
{
value = 0;
return true;
}
//return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value);
value = 0;
return true;
}
bool Bus::WriteSPU(MemoryAccessSize size, u32 offset, u32 value)
{
// Transfer FIFO
if (offset == 0x1A8)
return true;
// SPUCNT
if (offset == 0x1AA)
return true;
//return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value);
return true;
}
bool Bus::DoReadDMA(MemoryAccessSize size, u32 offset, u32& value)
{
Assert(size == MemoryAccessSize::Word);
value = m_dma->ReadRegister(offset);
return true;
}
bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32& value)
{
Assert(size == MemoryAccessSize::Word);
m_dma->WriteRegister(offset, value);
return true;
}

72
src/pse/bus.h Normal file
View File

@ -0,0 +1,72 @@
#pragma once
#include "YBaseLib/String.h"
#include "types.h"
#include <array>
class StateWrapper;
class DMA;
class GPU;
class System;
class Bus
{
public:
Bus();
~Bus();
bool Initialize(System* system, DMA* dma, GPU* gpu);
void Reset();
bool DoState(StateWrapper& sw);
bool ReadByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8* value);
bool ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value);
bool ReadDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value);
bool WriteByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8 value);
bool WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value);
bool WriteDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value);
template<MemoryAccessType type, MemoryAccessSize size>
bool DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32& value);
private:
static constexpr u32 DMA_BASE = 0x1F801080;
static constexpr u32 DMA_SIZE = 0x80;
static constexpr u32 DMA_MASK = DMA_SIZE - 1;
static constexpr u32 SPU_BASE = 0x1F801C00;
static constexpr u32 SPU_SIZE = 0x300;
static constexpr u32 SPU_MASK = 0x3FF;
static constexpr u32 EXP2_BASE = 0x1F802000;
static constexpr u32 EXP2_SIZE = 0x2000;
static constexpr u32 EXP2_MASK = EXP2_SIZE - 1;
bool LoadBIOS();
template<MemoryAccessType type, MemoryAccessSize size>
bool DoRAMAccess(u32 offset, u32& value);
template<MemoryAccessType type, MemoryAccessSize size>
bool DoBIOSAccess(u32 offset, u32& value);
bool DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress cpu_address,
PhysicalMemoryAddress bus_address, u32& value);
bool ReadExpansionRegion2(MemoryAccessSize size, u32 offset, u32& value);
bool WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value);
bool ReadSPU(MemoryAccessSize size, u32 offset, u32& value);
bool WriteSPU(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadDMA(MemoryAccessSize size, u32 offset, u32& value);
bool DoWriteDMA(MemoryAccessSize size, u32 offset, u32& value);
DMA* m_dma = nullptr;
GPU* m_gpu = nullptr;
std::array<u8, 2097152> m_ram{}; // 2MB RAM
std::array<u8, 524288> m_bios{}; // 512K BIOS ROM
String m_tty_line_buffer;
};
#include "bus.inl"

130
src/pse/bus.inl Normal file
View File

@ -0,0 +1,130 @@
#pragma once
#include "bus.h"
template<MemoryAccessType type, MemoryAccessSize size>
bool Bus::DoRAMAccess(u32 offset, u32& value)
{
// TODO: Configurable mirroring.
offset &= UINT32_C(0x1FFFFF);
if constexpr (type == MemoryAccessType::Read)
{
if constexpr (size == MemoryAccessSize::Byte)
{
value = ZeroExtend32(m_ram[offset]);
}
else if constexpr (size == MemoryAccessSize::HalfWord)
{
u16 temp;
std::memcpy(&temp, &m_ram[offset], sizeof(u16));
value = ZeroExtend32(temp);
}
else if constexpr (size == MemoryAccessSize::Word)
{
std::memcpy(&value, &m_ram[offset], sizeof(u32));
}
}
else
{
if constexpr (size == MemoryAccessSize::Byte)
{
m_ram[offset] = Truncate8(value);
}
else if constexpr (size == MemoryAccessSize::HalfWord)
{
const u16 temp = Truncate16(value);
std::memcpy(&m_ram[offset], &temp, sizeof(u16));
}
else if constexpr (size == MemoryAccessSize::Word)
{
std::memcpy(&m_ram[offset], &value, sizeof(u32));
}
}
return true;
}
template<MemoryAccessType type, MemoryAccessSize size>
bool Bus::DoBIOSAccess(u32 offset, u32& value)
{
// TODO: Configurable mirroring.
if constexpr (type == MemoryAccessType::Read)
{
offset &= UINT32_C(0x7FFFF);
if constexpr (size == MemoryAccessSize::Byte)
{
value = ZeroExtend32(m_bios[offset]);
}
else if constexpr (size == MemoryAccessSize::HalfWord)
{
u16 temp;
std::memcpy(&temp, &m_bios[offset], sizeof(u16));
value = ZeroExtend32(temp);
}
else
{
std::memcpy(&value, &m_bios[offset], sizeof(u32));
}
}
else
{
// Writes are ignored.
}
return true;
}
template<MemoryAccessType type, MemoryAccessSize size>
bool Bus::DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32& value)
{
if (bus_address < 0x800000)
{
return DoRAMAccess<type, size>(bus_address, value);
}
else if (bus_address < DMA_BASE)
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
}
else if (bus_address < (DMA_BASE + DMA_SIZE))
{
return (type == MemoryAccessType::Read) ? DoReadDMA(size, bus_address & DMA_MASK, value) :
DoWriteDMA(size, bus_address & DMA_MASK, value);
}
else if (bus_address < SPU_BASE)
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
}
else if (bus_address < (SPU_BASE + SPU_SIZE))
{
return (type == MemoryAccessType::Read) ? ReadSPU(size, bus_address & SPU_MASK, value) :
WriteSPU(size, bus_address & SPU_MASK, value);
}
else if (bus_address < EXP2_BASE)
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
}
else if (bus_address < (EXP2_BASE + EXP2_SIZE))
{
return (type == MemoryAccessType::Read) ? ReadExpansionRegion2(size, bus_address & EXP2_MASK, value) :
WriteExpansionRegion2(size, bus_address & EXP2_MASK, value);
}
else if (bus_address < 0x1FC00000)
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
}
else if (bus_address < 0x20000000)
{
return DoBIOSAccess<type, size>(static_cast<u32>(bus_address - UINT32_C(0x1FC00000)), value);
}
else if (bus_address < 0x1FFE0000)
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
}
else if (bus_address < 0x1FFE0200) // I/O Ports (Cache Control)
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
}
else
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
}
}

192
src/pse/cpu_bus.cpp Normal file
View File

@ -0,0 +1,192 @@
#include "cpu_bus.h"
#include "YBaseLib/ByteStream.h"
#include "YBaseLib/Log.h"
#include "YBaseLib/String.h"
#include <cstdio>
Log_SetChannel(Bus);
Bus::Bus() = default;
Bus::~Bus() = default;
bool Bus::Initialize(System* system)
{
if (!LoadBIOS())
return false;
return true;
}
void Bus::Reset()
{
m_ram.fill(static_cast<u8>(0));
}
bool Bus::DoState(StateWrapper& sw)
{
return false;
}
bool Bus::ReadByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8* value)
{
u32 temp = 0;
const bool result = DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(cpu_address, bus_address, temp);
*value = Truncate8(temp);
return result;
}
bool Bus::ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value)
{
u32 temp = 0;
const bool result =
DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(cpu_address, bus_address, temp);
*value = Truncate16(temp);
return result;
}
bool Bus::ReadDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value)
{
return DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(cpu_address, bus_address, *value);
}
bool Bus::WriteByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8 value)
{
u32 temp = ZeroExtend32(value);
return DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(cpu_address, bus_address, temp);
}
bool Bus::WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value)
{
u32 temp = ZeroExtend32(value);
return DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord>(cpu_address, bus_address, temp);
}
bool Bus::WriteDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value)
{
return DispatchAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(cpu_address, bus_address, value);
}
bool Bus::LoadBIOS()
{
std::FILE* fp = std::fopen("SCPH1001.BIN", "rb");
if (!fp)
return false;
std::fseek(fp, 0, SEEK_END);
const u32 size = static_cast<u32>(std::ftell(fp));
std::fseek(fp, 0, SEEK_SET);
if (size != m_bios.size())
{
Log_ErrorPrintf("BIOS image mismatch, expecting %u bytes, got %u bytes", static_cast<u32>(m_bios.size()), size);
std::fclose(fp);
return false;
}
if (std::fread(m_bios.data(), 1, m_bios.size(), fp) != m_bios.size())
{
Log_ErrorPrintf("Failed to read BIOS image");
std::fclose(fp);
return false;
}
std::fclose(fp);
#if 1
auto Patch = [this](u32 address, u32 value) { std::memcpy(&m_bios[address], &value, sizeof(value)); };
Patch(0x6F0C, 0x24010001); // addiu $at, $zero, 1
Patch(0x6F14, 0xaf81a9c0); // sw at, -0x5640(gp)
#endif
return true;
}
bool Bus::DoInvalidAccess(MemoryAccessType type, MemoryAccessSize size, PhysicalMemoryAddress cpu_address,
PhysicalMemoryAddress bus_address, u32& value)
{
SmallString str;
str.AppendString("Invalid bus ");
if (size == MemoryAccessSize::Byte)
str.AppendString("byte");
if (size == MemoryAccessSize::HalfWord)
str.AppendString("word");
if (size == MemoryAccessSize::Word)
str.AppendString("dword");
str.AppendCharacter(' ');
if (type == MemoryAccessType::Read)
str.AppendString("read");
else
str.AppendString("write");
str.AppendFormattedString(" at address 0x%08X (virtual address 0x%08X)", bus_address, cpu_address);
if (type == MemoryAccessType::Write)
str.AppendFormattedString(" (value 0x%08X)", value);
Log_ErrorPrint(str);
if (type == MemoryAccessType::Read)
value = UINT32_C(0xFFFFFFFF);
return true;
}
bool Bus::ReadExpansionRegion2(MemoryAccessSize size, u32 offset, u32& value)
{
offset &= EXP2_MASK;
// rx/tx buffer empty
if (offset == 0x21)
{
value = 0x04 | 0x08;
return true;
}
return DoInvalidAccess(MemoryAccessType::Read, size, EXP2_BASE | offset, EXP2_BASE | offset, value);
}
bool Bus::WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value)
{
offset &= EXP2_MASK;
if (offset == 0x23)
{
if (value == '\r')
return true;
if (value == '\n')
{
if (!m_tty_line_buffer.IsEmpty())
Log_InfoPrintf("TTY: %s", m_tty_line_buffer.GetCharArray());
m_tty_line_buffer.Clear();
}
else
{
m_tty_line_buffer.AppendCharacter(Truncate8(value));
}
return true;
}
if (offset == 0x41)
{
Log_WarningPrintf("BIOS POST status: %02X", value & UINT32_C(0x0F));
return true;
}
return DoInvalidAccess(MemoryAccessType::Write, size, EXP2_BASE | offset, EXP2_BASE | offset, value);
}
bool Bus::ReadSPU(MemoryAccessSize size, u32 offset, u32& value)
{
if (offset == 0x1AE)
{
value = 0;
return true;
}
return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value);
}
bool Bus::WriteSPU(MemoryAccessSize size, u32 offset, u32 value)
{
return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value);
}

783
src/pse/cpu_core.cpp Normal file
View File

@ -0,0 +1,783 @@
#include "cpu_core.h"
#include "YBaseLib/Log.h"
#include "common/state_wrapper.h"
#include "cpu_disasm.h"
#include <cstdio>
Log_SetChannel(CPU::Core);
namespace CPU {
bool TRACE_EXECUTION = false;
Core::Core() = default;
Core::~Core() = default;
bool Core::Initialize(Bus* bus)
{
m_bus = bus;
return true;
}
void Core::Reset()
{
m_regs = {};
m_regs.npc = RESET_VECTOR;
FetchInstruction();
}
bool Core::DoState(StateWrapper& sw)
{
return false;
}
u8 Core::ReadMemoryByte(VirtualMemoryAddress addr)
{
u32 value;
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Byte, false>(addr, value);
return Truncate8(value);
}
u16 Core::ReadMemoryHalfWord(VirtualMemoryAddress addr)
{
Assert(Common::IsAlignedPow2(addr, 2));
u32 value;
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::HalfWord, false>(addr, value);
return Truncate16(value);
}
u32 Core::ReadMemoryWord(VirtualMemoryAddress addr)
{
Assert(Common::IsAlignedPow2(addr, 4));
u32 value;
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word, false>(addr, value);
return value;
}
void Core::WriteMemoryByte(VirtualMemoryAddress addr, u8 value)
{
u32 value32 = ZeroExtend32(value);
DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Byte, false>(addr, value32);
}
void Core::WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value)
{
Assert(Common::IsAlignedPow2(addr, 2));
u32 value32 = ZeroExtend32(value);
DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::HalfWord, false>(addr, value32);
}
void Core::WriteMemoryWord(VirtualMemoryAddress addr, u32 value)
{
Assert(Common::IsAlignedPow2(addr, 4));
DoMemoryAccess<MemoryAccessType::Write, MemoryAccessSize::Word, false>(addr, value);
}
void Core::Branch(u32 target)
{
m_regs.npc = target;
m_branched = true;
}
void Core::RaiseException(u32 inst_pc, Exception excode)
{
m_cop0_regs.EPC = m_in_branch_delay_slot ? (inst_pc - UINT32_C(4)) : inst_pc;
m_cop0_regs.cause.Excode = excode;
m_cop0_regs.cause.BD = m_in_branch_delay_slot;
// current -> previous
m_cop0_regs.sr.mode_bits <<= 2;
// flush the pipeline - we don't want to execute the previously fetched instruction
m_regs.npc = m_cop0_regs.sr.BEV ? UINT32_C(0xbfc00180) : UINT32_C(0x80000080);
FlushPipeline();
}
void Core::FlushPipeline()
{
// loads are flushed
m_load_delay_reg = Reg::count;
m_load_delay_old_value = 0;
m_next_load_delay_reg = Reg::count;
m_next_load_delay_old_value = 0;
// not in a branch delay slot
m_branched = false;
m_in_branch_delay_slot = false;
// prefetch the next instruction
FetchInstruction();
}
u32 Core::ReadReg(Reg rs)
{
return rs == m_load_delay_reg ? m_load_delay_old_value : m_regs.r[static_cast<u8>(rs)];
}
void Core::WriteReg(Reg rd, u32 value)
{
if (rd != Reg::zero)
m_regs.r[static_cast<u8>(rd)] = value;
}
void Core::WriteRegDelayed(Reg rd, u32 value)
{
Assert(m_next_load_delay_reg == Reg::count);
if (rd == Reg::zero)
return;
// save the old value, this will be returned if the register is read in the next instruction
m_next_load_delay_reg = rd;
m_next_load_delay_old_value = m_regs.r[static_cast<u8>(rd)];
m_regs.r[static_cast<u8>(rd)] = value;
}
void Core::WriteCacheControl(u32 value)
{
Log_WarningPrintf("Cache control <- 0x%08X", value);
m_cache_control = value;
}
static void PrintInstruction(u32 bits, u32 pc)
{
TinyString instr;
DisassembleInstruction(&instr, pc, bits);
std::printf("%08x: %08x %s\n", pc, bits, instr.GetCharArray());
}
static constexpr bool AddOverflow(u32 old_value, u32 add_value, u32 new_value)
{
return (((new_value ^ old_value) & (new_value ^ add_value)) & UINT32_C(0x80000000)) != 0;
}
void Core::DisassembleAndPrint(u32 addr)
{
u32 bits;
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word, true>(addr, bits);
PrintInstruction(bits, addr);
}
void Core::Execute()
{
// now executing the instruction we previously fetched
const Instruction inst = m_next_instruction;
const u32 inst_pc = m_regs.pc;
// fetch the next instruction
FetchInstruction();
// handle branch delays - we are now in a delay slot if we just branched
m_in_branch_delay_slot = m_branched;
m_branched = false;
// execute the instruction we previously fetched
ExecuteInstruction(inst, inst_pc);
// next load delay
m_load_delay_reg = m_next_load_delay_reg;
m_next_load_delay_reg = Reg::count;
m_load_delay_old_value = m_next_load_delay_old_value;
m_next_load_delay_old_value = 0;
}
void Core::FetchInstruction()
{
DoMemoryAccess<MemoryAccessType::Read, MemoryAccessSize::Word, true>(static_cast<VirtualMemoryAddress>(m_regs.npc),
m_next_instruction.bits);
m_regs.pc = m_regs.npc;
m_regs.npc += sizeof(m_next_instruction.bits);
}
void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
{
if (TRACE_EXECUTION)
PrintInstruction(inst.bits, inst_pc);
#if 0
if (inst_pc == 0x8005ab80)
__debugbreak();
#endif
switch (inst.op)
{
case InstructionOp::funct:
{
switch (inst.r.funct)
{
case InstructionFunct::sll:
{
const u32 new_value = ReadReg(inst.r.rt) << inst.r.shamt;
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::srl:
{
const u32 new_value = ReadReg(inst.r.rt) >> inst.r.shamt;
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::sra:
{
const u32 new_value = static_cast<u32>(static_cast<s32>(ReadReg(inst.r.rt)) >> inst.r.shamt);
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::sllv:
{
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
const u32 new_value = ReadReg(inst.r.rt) << shift_amount;
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::srlv:
{
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
const u32 new_value = ReadReg(inst.r.rt) >> shift_amount;
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::srav:
{
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
const u32 new_value = static_cast<u32>(static_cast<s32>(ReadReg(inst.r.rt)) >> shift_amount);
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::and_:
{
const u32 new_value = ReadReg(inst.r.rs) & ReadReg(inst.r.rt);
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::or_:
{
const u32 new_value = ReadReg(inst.r.rs) | ReadReg(inst.r.rt);
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::xor_:
{
const u32 new_value = ReadReg(inst.r.rs) ^ ReadReg(inst.r.rt);
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::nor:
{
const u32 new_value = ~(ReadReg(inst.r.rs) | ReadReg(inst.r.rt));
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::add:
{
const u32 old_value = ReadReg(inst.r.rs);
const u32 add_value = ReadReg(inst.r.rt);
const u32 new_value = old_value + add_value;
if (AddOverflow(old_value, add_value, new_value))
RaiseException(inst_pc, Exception::Ov);
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::addu:
{
const u32 new_value = ReadReg(inst.r.rs) + ReadReg(inst.r.rt);
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::subu:
{
const u32 new_value = ReadReg(inst.r.rs) - ReadReg(inst.r.rt);
WriteReg(inst.r.rd, new_value);
}
break;
case InstructionFunct::slt:
{
const u32 result = BoolToUInt32(static_cast<s32>(ReadReg(inst.r.rs)) < static_cast<s32>(ReadReg(inst.r.rt)));
WriteReg(inst.r.rd, result);
}
break;
case InstructionFunct::sltu:
{
const u32 result = BoolToUInt32(ReadReg(inst.r.rs) < ReadReg(inst.r.rt));
WriteReg(inst.r.rd, result);
}
break;
case InstructionFunct::mfhi:
{
WriteReg(inst.r.rd, m_regs.hi);
}
break;
case InstructionFunct::mthi:
{
const u32 value = ReadReg(inst.r.rs);
m_regs.hi = value;
}
break;
case InstructionFunct::mflo:
{
WriteReg(inst.r.rd, m_regs.lo);
}
break;
case InstructionFunct::mtlo:
{
const u32 value = ReadReg(inst.r.rs);
m_regs.lo = value;
}
break;
case InstructionFunct::mult:
{
const u32 lhs = ReadReg(inst.r.rs);
const u32 rhs = ReadReg(inst.r.rt);
const u64 result =
static_cast<u64>(static_cast<s64>(SignExtend64(lhs)) * static_cast<s64>(SignExtend64(rhs)));
m_regs.hi = Truncate32(result >> 32);
m_regs.lo = Truncate32(result);
}
break;
case InstructionFunct::multu:
{
const u32 lhs = ReadReg(inst.r.rs);
const u32 rhs = ReadReg(inst.r.rt);
const u64 result = ZeroExtend64(lhs) * ZeroExtend64(rhs);
m_regs.hi = Truncate32(result >> 32);
m_regs.lo = Truncate32(result);
}
break;
case InstructionFunct::div:
{
const s32 num = static_cast<s32>(ReadReg(inst.r.rs));
const s32 denom = static_cast<s32>(ReadReg(inst.r.rt));
if (denom == 0)
{
// divide by zero
m_regs.lo = (num >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1);
m_regs.hi = static_cast<u32>(num);
}
else if (static_cast<u32>(num) == UINT32_C(0x80000000) && denom == -1)
{
// unrepresentable
m_regs.lo = UINT32_C(0x80000000);
m_regs.hi = 0;
}
else
{
m_regs.lo = static_cast<u32>(num / denom);
m_regs.hi = static_cast<u32>(num % denom);
}
}
break;
case InstructionFunct::divu:
{
const u32 num = ReadReg(inst.r.rs);
const u32 denom = ReadReg(inst.r.rt);
if (denom == 0)
{
// divide by zero
m_regs.lo = (num >= 0) ? UINT32_C(0xFFFFFFFF) : UINT32_C(1);
m_regs.hi = static_cast<u32>(num);
}
else
{
m_regs.lo = num / denom;
m_regs.hi = num % denom;
}
}
break;
case InstructionFunct::jr:
{
const u32 target = ReadReg(inst.r.rs);
Branch(target);
}
break;
case InstructionFunct::jalr:
{
const u32 target = ReadReg(inst.r.rs);
WriteReg(inst.r.rd, m_regs.npc);
Branch(target);
}
break;
case InstructionFunct::syscall:
{
RaiseException(inst_pc, Exception::Syscall);
}
break;
default:
UnreachableCode();
break;
}
}
break;
case InstructionOp::lui:
{
WriteReg(inst.i.rt, inst.i.imm_zext32() << 16);
}
break;
case InstructionOp::andi:
{
WriteReg(inst.i.rt, ReadReg(inst.i.rs) & inst.i.imm_zext32());
}
break;
case InstructionOp::ori:
{
WriteReg(inst.i.rt, ReadReg(inst.i.rs) | inst.i.imm_zext32());
}
break;
case InstructionOp::addi:
{
const u32 old_value = ReadReg(inst.i.rs);
const u32 add_value = inst.i.imm_sext32();
const u32 new_value = old_value + add_value;
if (AddOverflow(old_value, add_value, new_value))
RaiseException(inst_pc, Exception::Ov);
WriteReg(inst.i.rt, new_value);
}
break;
case InstructionOp::addiu:
{
WriteReg(inst.i.rt, ReadReg(inst.i.rs) + inst.i.imm_sext32());
}
break;
case InstructionOp::slti:
{
const u32 result = BoolToUInt32(static_cast<s32>(ReadReg(inst.i.rs)) < static_cast<s32>(inst.i.imm_sext32()));
WriteReg(inst.i.rt, result);
}
break;
case InstructionOp::sltiu:
{
const u32 result = BoolToUInt32(ReadReg(inst.i.rs) < inst.i.imm_sext32());
WriteReg(inst.i.rt, result);
}
break;
case InstructionOp::lb:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u8 value = ReadMemoryByte(addr);
WriteRegDelayed(inst.i.rt, SignExtend32(value));
}
break;
case InstructionOp::lh:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u16 value = ReadMemoryHalfWord(addr);
WriteRegDelayed(inst.i.rt, SignExtend32(value));
}
break;
case InstructionOp::lw:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u32 value = ReadMemoryWord(addr);
WriteRegDelayed(inst.i.rt, value);
}
break;
case InstructionOp::lbu:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u8 value = ReadMemoryByte(addr);
WriteRegDelayed(inst.i.rt, ZeroExtend32(value));
}
break;
case InstructionOp::lhu:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u16 value = ReadMemoryHalfWord(addr);
WriteRegDelayed(inst.i.rt, ZeroExtend32(value));
}
break;
case InstructionOp::sb:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u8 value = Truncate8(ReadReg(inst.i.rt));
WriteMemoryByte(addr, value);
}
break;
case InstructionOp::sh:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u16 value = Truncate16(ReadReg(inst.i.rt));
WriteMemoryHalfWord(addr, value);
}
break;
case InstructionOp::sw:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const u32 value = ReadReg(inst.i.rt);
WriteMemoryWord(addr, value);
}
break;
case InstructionOp::j:
{
Branch((m_regs.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));
}
break;
case InstructionOp::jal:
{
m_regs.ra = m_regs.npc;
Branch((m_regs.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));
}
break;
case InstructionOp::beq:
{
const bool branch = (ReadReg(inst.i.rs) == ReadReg(inst.i.rt));
if (branch)
Branch(m_regs.pc + (inst.i.imm_sext32() << 2));
}
break;
case InstructionOp::bne:
{
const bool branch = (ReadReg(inst.i.rs) != ReadReg(inst.i.rt));
if (branch)
Branch(m_regs.pc + (inst.i.imm_sext32() << 2));
}
break;
case InstructionOp::bgtz:
{
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) > 0);
if (branch)
Branch(m_regs.pc + (inst.i.imm_sext32() << 2));
}
break;
case InstructionOp::blez:
{
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) <= 0);
if (branch)
Branch(m_regs.pc + (inst.i.imm_sext32() << 2));
}
break;
case InstructionOp::b:
{
const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
// bgez is the inverse of bltz, so simply do ltz and xor the result
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
const bool branch = (static_cast<s32>(ReadReg(inst.i.rs)) < 0) ^ bgez;
if (branch)
{
const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1));
if (link)
m_regs.ra = m_regs.npc;
Branch(m_regs.pc + (inst.i.imm_sext32() << 2));
}
}
break;
case InstructionOp::cop0:
{
if (!m_cop0_regs.sr.CU0 && InUserMode())
{
Log_WarningPrintf("Coprocessor 0 not present in user mode");
RaiseException(inst_pc, Exception::CpU);
return;
}
ExecuteCop0Instruction(inst, inst_pc);
}
break;
// COP1/COP3 are not present
case InstructionOp::cop1:
case InstructionOp::cop2:
{
RaiseException(inst_pc, Exception::CpU);
}
break;
case InstructionOp::cop3:
{
Panic("GTE not implemented");
}
break;
default:
UnreachableCode();
break;
}
}
void Core::ExecuteCop0Instruction(Instruction inst, u32 inst_pc)
{
switch (inst.cop.cop0_op())
{
case Cop0Instruction::mtc0:
{
const u32 value = ReadReg(inst.r.rt);
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
{
case Cop0Reg::BPC:
{
m_cop0_regs.BPC = value;
Log_WarningPrintf("COP0 BPC <- %08X", value);
}
break;
case Cop0Reg::BPCM:
{
m_cop0_regs.BPCM = value;
Log_WarningPrintf("COP0 BPCM <- %08X", value);
}
break;
case Cop0Reg::BDA:
{
m_cop0_regs.BDA = value;
Log_WarningPrintf("COP0 BDA <- %08X", value);
}
break;
case Cop0Reg::BDAM:
{
m_cop0_regs.BDAM = value;
Log_WarningPrintf("COP0 BDAM <- %08X", value);
}
break;
case Cop0Reg::JUMPDEST:
{
Log_WarningPrintf("Ignoring write to Cop0 JUMPDEST");
}
break;
case Cop0Reg::DCIC:
{
m_cop0_regs.dcic.bits =
(m_cop0_regs.dcic.bits & ~Cop0Registers::DCIC::WRITE_MASK) | (value & Cop0Registers::DCIC::WRITE_MASK);
Log_WarningPrintf("COP0 DCIC <- %08X (now %08X)", value, m_cop0_regs.dcic.bits);
}
break;
case Cop0Reg::SR:
{
m_cop0_regs.sr.bits =
(m_cop0_regs.sr.bits & ~Cop0Registers::SR::WRITE_MASK) | (value & Cop0Registers::SR::WRITE_MASK);
Log_WarningPrintf("COP0 SR <- %08X (now %08X)", value, m_cop0_regs.sr.bits);
}
break;
case Cop0Reg::CAUSE:
{
m_cop0_regs.cause.bits =
(m_cop0_regs.cause.bits & ~Cop0Registers::CAUSE::WRITE_MASK) | (value & Cop0Registers::CAUSE::WRITE_MASK);
Log_WarningPrintf("COP0 CAUSE <- %08X (now %08X)", value, m_cop0_regs.cause.bits);
}
break;
default:
Panic("Unknown COP0 reg");
break;
}
}
break;
case Cop0Instruction::mfc0:
{
u32 value;
switch (static_cast<Cop0Reg>(inst.r.rd.GetValue()))
{
case Cop0Reg::BPC:
value = m_cop0_regs.BPC;
break;
case Cop0Reg::BPCM:
value = m_cop0_regs.BPCM;
break;
case Cop0Reg::BDA:
value = m_cop0_regs.BDA;
break;
case Cop0Reg::BDAM:
value = m_cop0_regs.BDAM;
break;
case Cop0Reg::DCIC:
value = m_cop0_regs.dcic.bits;
break;
case Cop0Reg::SR:
value = m_cop0_regs.sr.bits;
break;
case Cop0Reg::CAUSE:
value = m_cop0_regs.cause.bits;
break;
case Cop0Reg::EPC:
value = m_cop0_regs.EPC;
break;
default:
Panic("Unknown COP0 reg");
value = 0;
break;
}
WriteRegDelayed(inst.r.rt, value);
}
break;
case Cop0Instruction::rfe:
{
// restore mode
m_cop0_regs.sr.mode_bits = (m_cop0_regs.sr.mode_bits & UINT32_C(0b110000)) | (m_cop0_regs.sr.mode_bits >> 2);
}
break;
default:
Panic("Unhandled instruction");
break;
}
}
} // namespace CPU

84
src/pse/cpu_core.h Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include "common/bitfield.h"
#include "cpu_types.h"
#include "types.h"
class StateWrapper;
class Bus;
namespace CPU {
class Core
{
public:
static constexpr VirtualMemoryAddress RESET_VECTOR = 0xbfc00000;
Core();
~Core();
bool Initialize(Bus* bus);
void Reset();
bool DoState(StateWrapper& sw);
void Execute();
private:
template<MemoryAccessType type, MemoryAccessSize size, bool is_instruction_fetch>
void DoMemoryAccess(VirtualMemoryAddress address, u32& value);
u8 ReadMemoryByte(VirtualMemoryAddress addr);
u16 ReadMemoryHalfWord(VirtualMemoryAddress addr);
u32 ReadMemoryWord(VirtualMemoryAddress addr);
void WriteMemoryByte(VirtualMemoryAddress addr, u8 value);
void WriteMemoryHalfWord(VirtualMemoryAddress addr, u16 value);
void WriteMemoryWord(VirtualMemoryAddress addr, u32 value);
// state helpers
bool InUserMode() const { return m_cop0_regs.sr.KUc; }
bool InKernelMode() const { return !m_cop0_regs.sr.KUc; }
void DisassembleAndPrint(u32 addr);
// Fetches the instruction at m_regs.npc
void FetchInstruction();
void ExecuteInstruction(Instruction inst, u32 inst_pc);
void ExecuteCop0Instruction(Instruction inst, u32 inst_pc);
void Branch(u32 target);
void RaiseException(u32 inst_pc, Exception excode);
// 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);
Bus* m_bus = nullptr;
Registers m_regs = {};
Instruction m_next_instruction = {};
bool m_in_branch_delay_slot = false;
bool m_branched = false;
// load delays
Reg m_load_delay_reg = Reg::count;
u32 m_load_delay_old_value = 0;
Reg m_next_load_delay_reg = Reg::count;
u32 m_next_load_delay_old_value = 0;
u32 m_cache_control = 0;
Cop0Registers m_cop0_regs = {};
};
extern bool TRACE_EXECUTION;
} // namespace CPU
#include "cpu_core.inl"

78
src/pse/cpu_core.inl Normal file
View File

@ -0,0 +1,78 @@
#pragma once
#include "YBaseLib/Assert.h"
#include "bus.h"
#include "cpu_core.h"
namespace CPU {
template<MemoryAccessType type, MemoryAccessSize size, bool is_instruction_fetch>
void Core::DoMemoryAccess(VirtualMemoryAddress address, u32& value)
{
switch (address >> 29)
{
case 0x00: // KUSEG 0M-512M
{
if constexpr (type == MemoryAccessType::Write)
{
if (m_cop0_regs.sr.Isc)
return;
}
if (!m_bus->DispatchAccess<type, size>(address, address & UINT32_C(0x1FFFFFFF), value))
Panic("Bus error");
}
break;
case 0x01: // KUSEG 512M-1024M
case 0x02: // KUSEG 1024M-1536M
case 0x03: // KUSEG 1536M-2048M
{
// Above 512mb raises an exception.
Panic("Bad user access");
}
break;
case 0x04: // KSEG0 - physical memory cached
{
if constexpr (type == MemoryAccessType::Write)
{
if (m_cop0_regs.sr.Isc)
return;
}
if (!m_bus->DispatchAccess<type, size>(address, address & UINT32_C(0x1FFFFFFF), value))
Panic("Bus error");
}
break;
case 0x05: // KSEG1 - physical memory uncached
{
if (!m_bus->DispatchAccess<type, size>(address, address & UINT32_C(0x1FFFFFFF), value))
Panic("Bus error");
}
break;
case 0x06: // KSEG2
case 0x07: // KSEG2
{
if (address == 0xFFFE0130)
{
if constexpr (type == MemoryAccessType::Read)
value = m_cache_control;
else
WriteCacheControl(value);
}
else
{
Panic("KSEG2 access");
}
}
break;
default:
UnreachableCode();
break;
}
}
} // namespace CPU

310
src/pse/cpu_disasm.cpp Normal file
View File

@ -0,0 +1,310 @@
#include "cpu_disasm.h"
#include <array>
namespace CPU {
enum Operand : u8
{
Operand_None,
i_rs,
i_rt,
i_imm,
j_target,
r_rs,
r_rt,
r_rd,
r_shamt,
r_funct
};
struct TableEntry
{
const char* format;
};
static const std::array<const char*, 32> s_reg_names = {
{"$zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"}};
static const std::array<const char*, 64> s_base_table = {{
"", // 0
"UNKNOWN", // 1
"j $jt", // 2
"jal $jt", // 3
"beq $rs, $rt, $rel", // 4
"bne $rs, $rt, $rel", // 5
"blez $rs, $rel", // 6
"bgtz $rs, $rel", // 7
"addi $rt, $rs, $imm", // 8
"addiu $rt, $rs, $imm", // 9
"slti $rt, $rs, $imm", // 10
"sltiu $rt, $rs, $immu", // 11
"andi $rt, $rs, $immu", // 12
"ori $rt, $rs, $immu", // 13
"UNKNOWN", // 14
"lui $rt, $imm", // 15
"UNKNOWN", // 16
"UNKNOWN", // 17
"UNKNOWN", // 18
"UNKNOWN", // 19
"UNKNOWN", // 20
"UNKNOWN", // 21
"UNKNOWN", // 22
"UNKNOWN", // 23
"UNKNOWN", // 24
"UNKNOWN", // 25
"UNKNOWN", // 26
"UNKNOWN", // 27
"UNKNOWN", // 28
"UNKNOWN", // 29
"UNKNOWN", // 30
"UNKNOWN", // 31
"lb $rt, $offsetrs", // 32
"lh $rt, $offsetrs", // 33
"UNKNOWN", // 34
"lw $rt, $offsetrs", // 35
"lbu $rt, $offsetrs", // 36
"lhu $rt, $offsetrs", // 37
"UNKNOWN", // 38
"UNKNOWN", // 39
"sb $rt, $offsetrs", // 40
"sh $rt, $offsetrs", // 41
"UNKNOWN", // 42
"sw $rt, $offsetrs", // 43
"UNKNOWN", // 44
"UNKNOWN", // 45
"UNKNOWN", // 46
"UNKNOWN", // 47
"UNKNOWN", // 48
"UNKNOWN", // 49
"UNKNOWN", // 50
"UNKNOWN", // 51
"UNKNOWN", // 52
"UNKNOWN", // 53
"UNKNOWN", // 54
"UNKNOWN", // 55
"UNKNOWN", // 56
"UNKNOWN", // 57
"UNKNOWN", // 58
"UNKNOWN", // 59
"UNKNOWN", // 60
"UNKNOWN", // 61
"UNKNOWN", // 62
"UNKNOWN" // 63
}};
static const std::array<const char*, 64> s_special_table = {{
"sll $rd, $rt, $shamt", // 0
"UNKNOWN", // 1
"srl $rd, $rt, $shamt", // 2
"sra $rd, $rt, $shamt", // 3
"sllv $rd, $rt, $rs", // 4
"UNKNOWN", // 5
"srlv $rd, $rt, $rs", // 6
"srav $rd, $rt, $rs", // 7
"jr $rs", // 8
"jalr $rd, $rs", // 9
"UNKNOWN", // 10
"UNKNOWN", // 11
"syscall", // 12
"UNKNOWN", // 13
"UNKNOWN", // 14
"UNKNOWN", // 15
"mfhi $rd", // 16
"mthi $rs", // 17
"mflo $rd", // 18
"mtlo $rs", // 19
"UNKNOWN", // 20
"UNKNOWN", // 21
"UNKNOWN", // 22
"UNKNOWN", // 23
"mult $rs, $rt", // 24
"multu $rs, $rt", // 25
"div $rs, $rt", // 26
"divu $rs, $rt", // 27
"UNKNOWN", // 28
"UNKNOWN", // 29
"UNKNOWN", // 30
"UNKNOWN", // 31
"add $rd, $rs, $rt", // 32
"addu $rd, $rs, $rt", // 33
"sub $rd, $rs, $rt", // 34
"subu $rd, $rs, $rt", // 35
"and $rd, $rs, $rt", // 36
"or $rd, $rs, $rt", // 37
"xor $rd, $rs, $rt", // 38
"nor $rd, $rs, $rt", // 39
"UNKNOWN", // 40
"UNKNOWN", // 41
"slt $rd, $rs, $rt", // 42
"sltu $rd, $rs, $rt", // 43
"UNKNOWN", // 44
"UNKNOWN", // 45
"UNKNOWN", // 46
"UNKNOWN", // 47
"UNKNOWN", // 48
"UNKNOWN", // 49
"UNKNOWN", // 50
"UNKNOWN", // 51
"UNKNOWN", // 52
"UNKNOWN", // 53
"UNKNOWN", // 54
"UNKNOWN", // 55
"UNKNOWN", // 56
"UNKNOWN", // 57
"UNKNOWN", // 58
"UNKNOWN", // 59
"UNKNOWN", // 60
"UNKNOWN", // 61
"UNKNOWN", // 62
"UNKNOWN" // 63
}};
static const std::array<std::pair<Cop0Instruction, const char*>, 6> s_cop0_table = {
{{Cop0Instruction::mfc0, "mfc0 $rt, $coprd"},
{Cop0Instruction::cfc0, "cfc0 $rt, $copcr"},
{Cop0Instruction::mtc0, "mtc0 $rt, $coprd"},
{Cop0Instruction::ctc0, "ctc0 $rt, $copcr"},
{Cop0Instruction::bc0c, "bc0$copcc $rel"},
{Cop0Instruction::rfe, "rfe"}}};
static void FormatInstruction(String* dest, const Instruction inst, u32 pc, const char* format)
{
dest->Clear();
const char* str = format;
while (*str != '\0')
{
const char ch = *(str++);
if (ch != '$')
{
dest->AppendCharacter(ch);
continue;
}
if (std::strncmp(str, "rs", 2) == 0)
{
dest->AppendString(s_reg_names[static_cast<u8>(inst.r.rs.GetValue())]);
str += 2;
}
else if (std::strncmp(str, "rt", 2) == 0)
{
dest->AppendString(s_reg_names[static_cast<u8>(inst.r.rt.GetValue())]);
str += 2;
}
else if (std::strncmp(str, "rd", 2) == 0)
{
dest->AppendString(s_reg_names[static_cast<u8>(inst.r.rd.GetValue())]);
str += 2;
}
else if (std::strncmp(str, "shamt", 5) == 0)
{
dest->AppendFormattedString("%d", ZeroExtend32(inst.r.shamt.GetValue()));
str += 5;
}
else if (std::strncmp(str, "immu", 4) == 0)
{
dest->AppendFormattedString("%u", inst.i.imm_zext32());
str += 4;
}
else if (std::strncmp(str, "imm", 3) == 0)
{
// dest->AppendFormattedString("%d", static_cast<int>(inst.i.imm_sext32()));
dest->AppendFormattedString("%04x", inst.i.imm_zext32());
str += 3;
}
else if (std::strncmp(str, "rel", 3) == 0)
{
const u32 target = (pc + UINT32_C(4)) + (inst.i.imm_sext32() << 2);
dest->AppendFormattedString("%08x", target);
str += 3;
}
else if (std::strncmp(str, "offsetrs", 8) == 0)
{
const s32 offset = static_cast<s32>(inst.i.imm_sext32());
dest->AppendFormattedString("%d(%s)", offset, s_reg_names[static_cast<u8>(inst.i.rs.GetValue())]);
str += 8;
}
else if (std::strncmp(str, "jt", 2) == 0)
{
const u32 target = ((pc + UINT32_C(4)) & UINT32_C(0xF0000000)) | (inst.j.target << 2);
dest->AppendFormattedString("%08x", target);
str += 2;
}
else if (std::strncmp(str, "copcc", 5) == 0)
{
dest->AppendCharacter(((inst.bits & (UINT32_C(1) << 24)) != 0) ? 't' : 'f');
str += 5;
}
else if (std::strncmp(str, "coprd", 5) == 0 || std::strncmp(str, "copcr", 5) == 0)
{
dest->AppendFormattedString("%u", ZeroExtend32(static_cast<u8>(inst.r.rd.GetValue())));
str += 5;
}
else if (std::strncmp(str, "cop", 3) == 0)
{
dest->AppendFormattedString("%u", static_cast<u8>(inst.op.GetValue()) & INSTRUCTION_COP_N_MASK);
str += 3;
}
else
{
Panic("Unknown operand");
}
}
}
template<typename T>
void FormatCopInstruction(String* dest, u32 pc, const Instruction inst, const std::pair<T, const char*>* table,
size_t table_size, T table_key)
{
for (size_t i = 0; i < table_size; i++)
{
if (table[i].first == table_key)
{
FormatInstruction(dest, inst, pc, table[i].second);
return;
}
}
dest->Format("<cop%u 0x%07X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
}
void DisassembleInstruction(String* dest, u32 pc, u32 bits)
{
const Instruction inst{bits};
switch (inst.op)
{
case InstructionOp::funct:
FormatInstruction(dest, inst, pc, s_special_table[static_cast<u8>(inst.r.funct.GetValue())]);
return;
case InstructionOp::cop0:
FormatCopInstruction(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.cop0_op());
return;
case InstructionOp::cop1:
case InstructionOp::cop2:
case InstructionOp::cop3:
dest->Format("<cop%u 0x%07X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
break;
// special case for bltz/bgez{al}
case InstructionOp::b:
{
const u8 rt = static_cast<u8>(inst.i.rt.GetValue());
const bool bgez = ConvertToBoolUnchecked(rt & u8(1));
const bool link = ConvertToBoolUnchecked((rt >> 4) & u8(1));
if (link)
FormatInstruction(dest, inst, pc, bgez ? "bgezal $rs, $rel" : "bltzal $rs, $rel");
else
FormatInstruction(dest, inst, pc, bgez ? "bgez $rs, $rel" : "bltz $rs, $rel");
}
break;
default:
FormatInstruction(dest, inst, pc, s_base_table[static_cast<u8>(inst.op.GetValue())]);
break;
}
}
} // namespace CPU

7
src/pse/cpu_disasm.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "YBaseLib/String.h"
#include "cpu_types.h"
namespace CPU {
void DisassembleInstruction(String* dest, u32 pc, u32 bits);
} // namespace CPU

330
src/pse/cpu_types.h Normal file
View File

@ -0,0 +1,330 @@
#pragma once
#include "common/bitfield.h"
#include "types.h"
namespace CPU {
enum class Reg : u8
{
zero,
at,
v0,
v1,
a0,
a1,
a2,
a3,
t0,
t1,
t2,
t3,
t4,
t5,
t6,
t7,
s0,
s1,
s2,
s3,
s4,
s5,
s6,
s7,
t8,
t9,
k0,
k1,
gp,
sp,
fp,
ra,
count
};
enum class InstructionOp : u8
{
funct = 0,
b = 1, // i.rt 0 - bltz, 1 - bgez, 16 - bltzal, 17 - bgezal
j = 2,
jal = 3,
beq = 4,
bne = 5,
blez = 6,
bgtz = 7,
addi = 8,
addiu = 9,
slti = 10,
sltiu = 11,
andi = 12,
ori = 13,
xori = 14,
lui = 15,
cop0 = 16,
cop1 = 17,
cop2 = 18,
cop3 = 19,
lb = 32,
lh = 33,
lwl = 34,
lw = 35,
lbu = 36,
lhu = 37,
lwr = 38,
sb = 40,
sh = 41,
swl = 42,
sw = 43,
swr = 46
};
constexpr u8 INSTRUCTION_COP_BITS = 0x10;
constexpr u8 INSTRUCTION_COP_MASK = 0x3C;
constexpr u8 INSTRUCTION_COP_N_MASK = 0x03;
enum class InstructionFunct : u8
{
sll = 0,
srl = 2,
sra = 3,
sllv = 4,
srlv = 6,
srav = 7,
jr = 8,
jalr = 9,
syscall = 12,
break_ = 13,
mfhi = 16,
mthi = 17,
mflo = 18,
mtlo = 19,
mult = 24,
multu = 25,
div = 26,
divu = 27,
add = 32,
addu = 33,
sub = 34,
subu = 35,
and_ = 36,
or_ = 37,
xor_ = 38,
nor = 39,
sh = 41,
slt = 42,
sltu = 43
};
enum class Cop0Instruction : u32 // 25:21 | 0:5
{
mfc0 = 0b00000'000000,
cfc0 = 0b00010'000000,
mtc0 = 0b00100'000000,
ctc0 = 0b00110'000000,
bc0c = 0b01000'000000,
tlbr = 0b10000'000001,
tlbwi = 0b10000'000010,
tlbwr = 0b10000'000100,
tlbp = 0b10000'001000,
rfe = 0b10000'010000,
};
union Instruction
{
u32 bits;
BitField<u32, InstructionOp, 26, 6> op; // function/instruction
union
{
BitField<u32, Reg, 21, 5> rs;
BitField<u32, Reg, 16, 5> rt;
BitField<u32, u16, 0, 16> imm;
u32 imm_sext32() const { return SignExtend32(imm.GetValue()); }
u32 imm_zext32() const { return ZeroExtend32(imm.GetValue()); }
} i;
union
{
BitField<u32, u32, 0, 26> target;
} j;
union
{
BitField<u32, Reg, 21, 5> rs;
BitField<u32, Reg, 16, 5> rt;
BitField<u32, Reg, 11, 5> rd;
BitField<u32, u8, 6, 5> shamt;
BitField<u32, InstructionFunct, 0, 6> funct;
} r;
union
{
u32 bits;
BitField<u32, u8, 26, 2> cop_n;
BitField<u32, u16, 0, 16> imm16;
BitField<u32, u32, 0, 25> imm25;
Cop0Instruction cop0_op() const
{
return static_cast<Cop0Instruction>(((bits >> 15) & UINT32_C(0b11111000000)) | (bits & UINT32_C(0b111111)));
}
} cop;
};
struct Registers
{
union
{
u32 r[32];
struct
{
u32 zero;
u32 at;
u32 v0;
u32 v1;
u32 a0;
u32 a1;
u32 a2;
u32 a3;
u32 t0;
u32 t1;
u32 t2;
u32 t3;
u32 t4;
u32 t5;
u32 t6;
u32 t7;
u32 s0;
u32 s1;
u32 s2;
u32 s3;
u32 s4;
u32 s5;
u32 s6;
u32 s7;
u32 t8;
u32 t9;
u32 k0;
u32 k1;
u32 gp;
u32 sp;
u32 fp;
u32 ra;
};
};
u32 pc;
u32 hi;
u32 lo;
u32 npc;
};
enum class Cop0Reg : u8
{
BPC = 3,
BDA = 5,
JUMPDEST = 6,
DCIC = 7,
BadVaddr = 8,
BDAM = 9,
BPCM = 11,
SR = 12,
CAUSE = 13,
EPC = 14,
PRID = 15
};
enum class Exception : u8
{
INT = 0x00, // interrupt
MOD = 0x01, // tlb modification
TLBL = 0x02, // tlb load
TLBS = 0x03, // tlb store
AdEL = 0x04, // address error, data load/instruction fetch
AdES = 0x05, // address error, data store
IBE = 0x06, // bus error on instruction fetch
DBE = 0x07, // bus error on data load/store
Syscall = 0x08, // system call instruction
BP = 0x09, // break instruction
RI = 0x0A, // reserved instruction
CpU = 0x0B, // coprocessor unusable
Ov = 0x0C, // arithmetic overflow
};
struct Cop0Registers
{
u32 BPC; // breakpoint on execute
u32 BDA; // breakpoint on data access
u32 JUMPDEST; // randomly memorized jump address
u32 BadVaddr; // bad virtual address value
u32 BDAM; // data breakpoint mask
u32 BPCM; // execute breakpoint mask
u32 EPC; // return address from trap
u32 PRID; // processor ID
union SR
{
u32 bits;
BitField<u32, bool, 0, 1> IEc; // current interrupt enable
BitField<u32, bool, 1, 1> KUc; // current kernel/user mode, kernel = 1
BitField<u32, bool, 2, 1> IEp; // previous interrupt enable
BitField<u32, bool, 3, 1> KUp; // previous kernel/user mode, kernel = 1
BitField<u32, bool, 4, 1> IEo; // old interrupt enable
BitField<u32, bool, 5, 1> KUo; // old kernel/user mode, kernel = 1
BitField<u32, u8, 8, 8> Im; // interrupt mask, set to 1 = allowed to trigger
BitField<u32, bool, 16, 1> Isc; // isolate cache, no writes to memory occur
BitField<u32, bool, 17, 1> Swc; // swap data and instruction caches
BitField<u32, bool, 18, 1> PZ; // zero cache parity bits
BitField<u32, bool, 19, 1> CM; // last isolated load contains data from memory (tag matches?)
BitField<u32, bool, 20, 1> PE; // cache parity error
BitField<u32, bool, 21, 1> TS; // tlb shutdown - matched two entries
BitField<u32, bool, 22, 1> BEV; // boot exception vectors, 0 = KSEG0, 1 = KSEG1
BitField<u32, bool, 25, 1> RE; // reverse endianness in user mode
BitField<u32, bool, 28, 1> CU0; // coprocessor 0 enable in user mode
BitField<u32, bool, 29, 1> CU1; // coprocessor 1 enable in user mode
BitField<u32, bool, 30, 1> CU2; // coprocessor 2 enable in user mode
BitField<u32, bool, 31, 1> CU3; // coprocessor 3 enable in user mode
BitField<u32, u8, 0, 6> mode_bits;
BitField<u32, u8, 28, 2> coprocessor_enable_mask;
static constexpr u32 WRITE_MASK = 0b1111'0010'0111'1111'1111'0011'0011'1111;
} sr;
union CAUSE
{
u32 bits;
BitField<u32, Exception, 2, 5> Excode; // which exception occurred
BitField<u32, u8, 8, 8> Ip; // interrupt pending
BitField<u32, u8, 28, 2> CE; // coprocessor number if caused by a coprocessor
BitField<u32, bool, 31, 1> BD; // exception occurred in branch delay slot, but pushed IP is for branch
static constexpr u32 WRITE_MASK = 0b0000'0000'0000'0000'0000'0011'0000'0000;
} cause;
union DCIC
{
u32 bits;
BitField<u32, bool, 0, 1> status_any_break;
BitField<u32, bool, 1, 1> status_bpc_code_break;
BitField<u32, bool, 2, 1> status_bda_data_break;
BitField<u32, bool, 3, 1> status_bda_data_read_break;
BitField<u32, bool, 4, 1> status_bda_data_write_break;
BitField<u32, bool, 5, 1> status_any_jump_break;
BitField<u32, u8, 12, 2> jump_redirection;
BitField<u32, bool, 23, 1> super_master_enable_1;
BitField<u32, bool, 24, 1> execution_breakpoint_enable;
BitField<u32, bool, 25, 1> data_access_breakpoint;
BitField<u32, bool, 26, 1> break_on_data_read;
BitField<u32, bool, 27, 1> break_on_data_write;
BitField<u32, bool, 28, 1> break_on_any_jump;
BitField<u32, bool, 29, 1> master_enable_any_jump;
BitField<u32, bool, 30, 1> master_enable_break;
BitField<u32, bool, 31, 1> super_master_enable_2;
static constexpr u32 WRITE_MASK = 0b1111'1111'1000'0000'1111'0000'0011'1111;
} dcic;
};
} // namespace CPU

56
src/pse/dma.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "dma.h"
#include "YBaseLib/Log.h"
#include "bus.h"
Log_SetChannel(DMA);
DMA::DMA() = default;
DMA::~DMA() = default;
bool DMA::Initialize(Bus* bus, GPU* gpu)
{
m_bus = bus;
m_gpu = gpu;
return true;
}
void DMA::Reset()
{
m_state = {};
m_DPCR.bits = 0;
m_DCIR = 0;
}
u32 DMA::ReadRegister(u32 offset)
{
const u32 channel_index = offset >> 4;
if (channel_index < 7)
{
switch (offset & UINT32_C(0x0F))
{
case 0x00:
return m_state[channel_index].base_address;
case 0x04:
return m_state[channel_index].block_control.bits;
case 0x08:
return m_state[channel_index].channel_control.bits;
default:
break;
}
}
else
{
if (offset == 0x70)
return m_DPCR.bits;
else if (offset == 0x74)
return m_DCIR;
}
Log_ErrorPrintf("Unhandled register read: %02X", offset);
return UINT32_C(0xFFFFFFFF);
}
void DMA::WriteRegister(u32 offset, u32 value)
{
Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value);
}

95
src/pse/dma.h Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include "common/bitfield.h"
#include "types.h"
#include <array>
class Bus;
class GPU;
class DMA
{
public:
enum : u32
{
NUM_CHANNELS = 7
};
enum class Channel : u32
{
MDECin = 0,
MDECout = 1,
GPU = 2,
CDROM = 3,
SPU = 4,
PIO = 5,
OTC = 6
};
DMA();
~DMA();
bool Initialize(Bus* bus, GPU* gpu);
void Reset();
u32 ReadRegister(u32 offset);
void WriteRegister(u32 offset, u32 value);
private:
Bus* m_bus = nullptr;
GPU* m_gpu = nullptr;
enum class SyncMode : u32
{
Word = 0,
Block = 1,
LinkedList = 2,
Reserved = 3
};
struct ChannelState
{
u32 base_address;
union BlockControl
{
u32 bits;
struct
{
BitField<u32, u32, 0, 16> word_count;
} word_mode;
struct
{
BitField<u32, u32, 0, 16> block_size;
BitField<u32, u32, 16, 16> block_count;
} block_mode;
} block_control;
union ChannelControl
{
u32 bits;
BitField<u32, bool, 0, 1> direction_to_ram;
BitField<u32, bool, 1, 1> address_step_forward;
BitField<u32, bool, 8, 1> chopping_enable;
BitField<u32, SyncMode, 9, 2> sync_mode;
BitField<u32, u32, 16, 3> chopping_dma_window_size;
BitField<u32, u32, 20, 3> chopping_cpu_window_size;
BitField<u32, bool, 28, 1> start_trigger;
} channel_control;
};
std::array<ChannelState, NUM_CHANNELS> m_state = {};
struct DPCR
{
u32 bits;
u8 GetPriority(Channel channel) { return ((bits >> (static_cast<u8>(channel) * 4)) & u32(3)); }
bool GetMasterEnable(Channel channel)
{
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(channel) * 4 + 3)) & u32(1));
}
} m_DPCR;
static constexpr u32 DCIR_WRITE_MASK = 0b11111111'11111111'10000000'00111111;
u32 m_DCIR = 0;
};

443
src/pse/pse.vcxproj Normal file
View File

@ -0,0 +1,443 @@
<?xml version="1.0" encoding="utf-8"?>
<Project InitialTargets="UNDUPOBJ" DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugFast|Win32">
<Configuration>DebugFast</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="DebugFast|x64">
<Configuration>DebugFast</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseLTCG|Win32">
<Configuration>ReleaseLTCG</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseLTCG|x64">
<Configuration>ReleaseLTCG</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bus.cpp" />
<ClCompile Include="cpu_core.cpp" />
<ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="dma.cpp" />
<ClCompile Include="system.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="bus.h" />
<ClInclude Include="cpu_core.h" />
<ClInclude Include="cpu_disasm.h" />
<ClInclude Include="cpu_types.h" />
<ClInclude Include="dma.h" />
<ClInclude Include="save_state_version.h" />
<ClInclude Include="system.h" />
<ClInclude Include="types.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\dep\libsamplerate\libsamplerate.vcxproj">
<Project>{2f2a2b7b-60b3-478c-921e-3633b3c45c3f}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\YBaseLib\Source\YBaseLib.vcxproj">
<Project>{b56ce698-7300-4fa5-9609-942f1d05c5a2}</Project>
</ProjectReference>
<ProjectReference Include="..\common\common.vcxproj">
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="cpu_core.inl" />
<None Include="bus.inl" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{868B98C8-65A1-494B-8346-250A73A48C0A}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>pse</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>ENABLE_VOODOO=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>ENABLE_VOODOO=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WholeProgramOptimization>false</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<OmitFramePointers>true</OmitFramePointers>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WholeProgramOptimization>false</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>ENABLE_VOODOO=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\softfloat\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\inih\cpp;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<OmitFramePointers>true</OmitFramePointers>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
<!-- ================ UNDUPOBJ ================ -->
<!-- relevant topics -->
<!-- https://stackoverflow.com/questions/3729515/visual-studio-2010-2008-cant-handle-source-files-with-identical-names-in-diff/26935613 -->
<!-- https://stackoverflow.com/questions/7033855/msvc10-mp-builds-not-multicore-across-folders-in-a-project -->
<!-- https://stackoverflow.com/questions/18304911/how-can-one-modify-an-itemdefinitiongroup-from-an-msbuild-target -->
<!-- other maybe related info -->
<!-- https://stackoverflow.com/questions/841913/modify-msbuild-itemgroup-metadata -->
<UsingTask TaskName="UNDUPOBJ_TASK" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputDir ParameterType="System.String" Required="true" />
<ItemList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
<OutputItemList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
</ParameterGroup>
<Task>
<Code><![CDATA[
//general outline: for each item (in ClCompile) assign it to a subdirectory of $(IntDir) by allocating subdirectories 0,1,2, etc., as needed to prevent duplicate filenames from clobbering each other
//this minimizes the number of batches that need to be run, since each subdirectory will necessarily be in a distinct batch due to /Fo specifying that output subdirectory
var assignmentMap = new Dictionary<string,int>();
HashSet<string> neededDirectories = new HashSet<string>();
foreach( var item in ItemList )
{
//solve bug e.g. Checkbox.cpp vs CheckBox.cpp
var filename = item.GetMetadata("Filename").ToUpperInvariant();
//assign reused filenames to increasing numbers
//assign previously unused filenames to 0
int assignment = 0;
if(assignmentMap.TryGetValue(filename, out assignment))
assignmentMap[filename] = ++assignment;
else
assignmentMap[filename] = 0;
var thisFileOutdir = Path.Combine(OutputDir,assignment.ToString()) + "/"; //take care it ends in / so /Fo knows it's a directory and not a filename
item.SetMetadata( "ObjectFileName", thisFileOutdir );
}
foreach(var needed in neededDirectories)
System.IO.Directory.CreateDirectory(needed);
OutputItemList = ItemList;
ItemList = new Microsoft.Build.Framework.ITaskItem[0];
]]></Code>
</Task>
</UsingTask>
<Target Name="UNDUPOBJ">
<!-- see stackoverflow topics for discussion on why we need to do some loopy copying stuff here -->
<ItemGroup>
<ClCompileCopy Include="@(ClCompile)" />
<ClCompile Remove="@(ClCompile)" />
</ItemGroup>
<UNDUPOBJ_TASK OutputDir="$(IntDir)" ItemList="@(ClCompileCopy)" OutputItemList="@(ClCompile)">
<Output ItemName="ClCompile" TaskParameter="OutputItemList" />
</UNDUPOBJ_TASK>
</Target>
<!-- ================ UNDUPOBJ ================ -->
<PropertyGroup Label="Globals">
<IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="system.cpp" />
<ClCompile Include="cpu_core.cpp" />
<ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="bus.cpp" />
<ClCompile Include="dma.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
<ClInclude Include="save_state_version.h" />
<ClInclude Include="system.h" />
<ClInclude Include="cpu_core.h" />
<ClInclude Include="cpu_types.h" />
<ClInclude Include="cpu_disasm.h" />
<ClInclude Include="bus.h" />
<ClInclude Include="dma.h" />
</ItemGroup>
<ItemGroup>
<None Include="cpu_core.inl" />
<None Include="bus.inl" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,4 @@
#pragma once
#include "pse/types.h"
constexpr u32 SAVE_STATE_VERSION = 1;

30
src/pse/system.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "system.h"
System::System() = default;
System::~System() = default;
bool System::Initialize()
{
if (!m_cpu.Initialize(&m_bus))
return false;
if (!m_bus.Initialize(this, &m_dma, nullptr))
return false;
if (!m_dma.Initialize(&m_bus, nullptr))
return false;
return true;
}
void System::Reset()
{
m_cpu.Reset();
m_bus.Reset();
}
void System::RunFrame()
{
m_cpu.Execute();
}

22
src/pse/system.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "bus.h"
#include "dma.h"
#include "cpu_core.h"
#include "types.h"
class System
{
public:
System();
~System();
bool Initialize();
void Reset();
void RunFrame();
private:
CPU::Core m_cpu;
Bus m_bus;
DMA m_dma;
};

18
src/pse/types.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "common/types.h"
// Physical memory addresses are 32-bits wide
using PhysicalMemoryAddress = u32;
using VirtualMemoryAddress = u32;
enum class MemoryAccessType : u32
{
Read,
Write
};
enum class MemoryAccessSize : u32
{
Byte,
HalfWord,
Word
};