Initial commit
This commit is contained in:
16
src/pse/CMakeLists.txt
Normal file
16
src/pse/CMakeLists.txt
Normal 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
220
src/pse/bus.cpp
Normal 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
72
src/pse/bus.h
Normal 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
130
src/pse/bus.inl
Normal 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
192
src/pse/cpu_bus.cpp
Normal 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
783
src/pse/cpu_core.cpp
Normal 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
84
src/pse/cpu_core.h
Normal 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
78
src/pse/cpu_core.inl
Normal 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
310
src/pse/cpu_disasm.cpp
Normal 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
7
src/pse/cpu_disasm.h
Normal 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
330
src/pse/cpu_types.h
Normal 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
56
src/pse/dma.cpp
Normal 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
95
src/pse/dma.h
Normal 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
443
src/pse/pse.vcxproj
Normal 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>
|
||||
24
src/pse/pse.vcxproj.filters
Normal file
24
src/pse/pse.vcxproj.filters
Normal 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>
|
||||
4
src/pse/save_state_version.h
Normal file
4
src/pse/save_state_version.h
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "pse/types.h"
|
||||
|
||||
constexpr u32 SAVE_STATE_VERSION = 1;
|
||||
30
src/pse/system.cpp
Normal file
30
src/pse/system.cpp
Normal 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
22
src/pse/system.h
Normal 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
18
src/pse/types.h
Normal 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
|
||||
};
|
||||
Reference in New Issue
Block a user