From 4025d6e4a6d49f6479fa7a43c967b735a2048874 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Tue, 17 Sep 2019 23:35:17 +1000 Subject: [PATCH] GTE: Stub and register read/write function --- src/pse/cpu_bus.cpp | 192 ------------------------------------ src/pse/cpu_core.cpp | 53 +++++++++- src/pse/cpu_core.h | 4 + src/pse/cpu_disasm.cpp | 26 ++++- src/pse/cpu_types.h | 16 +++ src/pse/gpu.cpp | 2 +- src/pse/gte.cpp | 24 +++++ src/pse/gte.h | 32 ++++++ src/pse/gte_types.h | 103 +++++++++++++++++++ src/pse/pse.vcxproj | 3 + src/pse/pse.vcxproj.filters | 3 + 11 files changed, 258 insertions(+), 200 deletions(-) delete mode 100644 src/pse/cpu_bus.cpp create mode 100644 src/pse/gte.cpp create mode 100644 src/pse/gte.h create mode 100644 src/pse/gte_types.h diff --git a/src/pse/cpu_bus.cpp b/src/pse/cpu_bus.cpp deleted file mode 100644 index 9dbf774bf..000000000 --- a/src/pse/cpu_bus.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "cpu_bus.h" -#include "YBaseLib/ByteStream.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" -#include -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(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(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(cpu_address, bus_address, temp); - *value = Truncate16(temp); - return result; -} - -bool Bus::ReadDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value) -{ - return DispatchAccess(cpu_address, bus_address, *value); -} - -bool Bus::WriteByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u8 value) -{ - u32 temp = ZeroExtend32(value); - return DispatchAccess(cpu_address, bus_address, temp); -} - -bool Bus::WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value) -{ - u32 temp = ZeroExtend32(value); - return DispatchAccess(cpu_address, bus_address, temp); -} - -bool Bus::WriteDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value) -{ - return DispatchAccess(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(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(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); -} diff --git a/src/pse/cpu_core.cpp b/src/pse/cpu_core.cpp index bc049e6f3..693052720 100644 --- a/src/pse/cpu_core.cpp +++ b/src/pse/cpu_core.cpp @@ -19,6 +19,8 @@ bool Core::Initialize(Bus* bus) // From nocash spec. m_cop0_regs.PRID = UINT32_C(0x00000002); + m_cop2.Initialize(); + return true; } @@ -38,6 +40,8 @@ void Core::Reset() m_cop0_regs.sr.bits = 0; m_cop0_regs.cause.bits = 0; + m_cop2.Reset(); + SetPC(RESET_VECTOR); } @@ -70,6 +74,10 @@ bool Core::DoState(StateWrapper& sw) sw.Do(&m_branched); sw.Do(&m_cache_control); sw.DoBytes(m_dcache.data(), m_dcache.size()); + + if (!m_cop2.DoState(sw)) + return false; + return !sw.HasError(); } @@ -893,17 +901,24 @@ void Core::ExecuteInstruction(Instruction inst) } break; - // COP1/COP3 are not present - case InstructionOp::cop1: case InstructionOp::cop2: { - RaiseException(Exception::CpU, inst.cop.cop_n); + if (!m_cop0_regs.sr.CU0 && InUserMode()) + { + Log_WarningPrintf("Coprocessor 2 not present in user mode"); + RaiseException(Exception::CpU, 2); + return; + } + + ExecuteCop2Instruction(inst); } break; + // COP1/COP3 are not present + case InstructionOp::cop1: case InstructionOp::cop3: { - Panic("GTE not implemented"); + RaiseException(Exception::CpU, inst.cop.cop_n); } break; @@ -1059,4 +1074,34 @@ void Core::ExecuteCop0Instruction(Instruction inst) } } +void Core::ExecuteCop2Instruction(Instruction inst) +{ + if (inst.cop.IsCommonInstruction()) + { + // TODO: Combine with cop0. + switch (inst.cop.cop2_op()) + { + case Cop2Instruction::mfc2: + case Cop2Instruction::cfc2: + case Cop2Instruction::mtc2: + case Cop2Instruction::ctc2: + { + const u32 index = static_cast(inst.r.rd.GetValue()); + const u32 value = ReadReg(inst.r.rt); + m_cop2.WriteControlRegister(index, value); + } + break; + + case Cop2Instruction::bc2c: + default: + Panic("Missing implementation"); + break; + } + } + else + { + m_cop2.ExecuteInstruction(GTE::Instruction{inst.bits}); + } +} + } // namespace CPU \ No newline at end of file diff --git a/src/pse/cpu_core.h b/src/pse/cpu_core.h index d0f47ea05..c1f7666f5 100644 --- a/src/pse/cpu_core.h +++ b/src/pse/cpu_core.h @@ -1,6 +1,7 @@ #pragma once #include "common/bitfield.h" #include "cpu_types.h" +#include "gte.h" #include "types.h" #include @@ -74,6 +75,7 @@ private: bool FetchInstruction(); void ExecuteInstruction(Instruction inst); void ExecuteCop0Instruction(Instruction inst); + void ExecuteCop2Instruction(Instruction inst); void Branch(u32 target); // exceptions @@ -121,6 +123,8 @@ private: // data cache (used as scratchpad) std::array m_dcache = {}; + + GTE::Core m_cop2; }; extern bool TRACE_EXECUTION; diff --git a/src/pse/cpu_disasm.cpp b/src/pse/cpu_disasm.cpp index 051a11afb..8b2948d58 100644 --- a/src/pse/cpu_disasm.cpp +++ b/src/pse/cpu_disasm.cpp @@ -168,6 +168,13 @@ static const std::array, 6> s_cop0_table {Cop0Instruction::bc0c, "bc0$copcc $rel"}, {Cop0Instruction::rfe, "rfe"}}}; +static const std::array, 6> s_cop2_common_table = { + {{Cop2Instruction::mfc2, "mfc2 $rt, $coprd"}, + {Cop2Instruction::cfc2, "cfc2 $rt, $copcr"}, + {Cop2Instruction::mtc2, "mtc2 $rt, $coprd"}, + {Cop2Instruction::ctc2, "ctc2 $rt, $copcr"}, + {Cop2Instruction::bc2c, "bc2$copcc $rel"}}}; + static void FormatInstruction(String* dest, const Instruction inst, u32 pc, const char* format) { dest->Clear(); @@ -266,7 +273,7 @@ void FormatCopInstruction(String* dest, u32 pc, const Instruction inst, const st } } - dest->Format("", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue()); + dest->Format("", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue()); } void DisassembleInstruction(String* dest, u32 pc, u32 bits) @@ -282,10 +289,23 @@ void DisassembleInstruction(String* dest, u32 pc, u32 bits) FormatCopInstruction(dest, pc, inst, s_cop0_table.data(), s_cop0_table.size(), inst.cop.cop0_op()); return; - case InstructionOp::cop1: case InstructionOp::cop2: + { + if (inst.cop.IsCommonInstruction()) + { + FormatCopInstruction(dest, pc, inst, s_cop2_common_table.data(), s_cop2_common_table.size(), + inst.cop.cop2_op()); + } + else + { + dest->Format("", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue()); + } + } + break; + + case InstructionOp::cop1: case InstructionOp::cop3: - dest->Format("", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue()); + dest->Format("", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue()); break; // special case for bltz/bgez{al} diff --git a/src/pse/cpu_types.h b/src/pse/cpu_types.h index 1eaca0bf0..5380563d4 100644 --- a/src/pse/cpu_types.h +++ b/src/pse/cpu_types.h @@ -127,6 +127,15 @@ enum class Cop0Instruction : u32 // 25:21 | 0:5 rfe = 0b10000'010000, }; +enum class Cop2Instruction : u32 // 25:21 +{ + mfc2 = 0b0000, + cfc2 = 0b0010, + mtc2 = 0b0100, + ctc2 = 0b0110, + bc2c = 0b1000, +}; + union Instruction { u32 bits; @@ -164,10 +173,17 @@ union Instruction BitField imm16; BitField imm25; + bool IsCommonInstruction() const { return (bits & (UINT32_C(1) << 25)) == 0; } + Cop0Instruction cop0_op() const { return static_cast(((bits >> 15) & UINT32_C(0b11111000000)) | (bits & UINT32_C(0b111111))); } + + Cop2Instruction cop2_op() const + { + return static_cast((bits >> 21) & UINT32_C(0b1111)); + } } cop; }; diff --git a/src/pse/gpu.cpp b/src/pse/gpu.cpp index 688ba2586..e1afdb42d 100644 --- a/src/pse/gpu.cpp +++ b/src/pse/gpu.cpp @@ -653,7 +653,7 @@ bool GPU::HandleCopyRectangleVRAMToCPUCommand() Log_DebugPrintf("Copy rectangle from VRAM to CPU offset=(%u,%u), size=(%u,%u)", src_x, src_y, width, height); - if ((src_x + width) > VRAM_WIDTH || (src_x + height) > VRAM_HEIGHT) + if ((src_x + width) > VRAM_WIDTH || (src_y + height) > VRAM_HEIGHT) { Panic("Out of bounds VRAM copy"); return true; diff --git a/src/pse/gte.cpp b/src/pse/gte.cpp new file mode 100644 index 000000000..9d6146bc7 --- /dev/null +++ b/src/pse/gte.cpp @@ -0,0 +1,24 @@ +#include "gte.h" + +namespace GTE { + +Core::Core() = default; + +Core::~Core() = default; + +void Core::Initialize() {} + +void Core::Reset() +{ + m_regs = {}; +} + +bool Core::DoState(StateWrapper& sw) +{ + sw.DoPOD(&m_regs); + return !sw.HasError(); +} + +void Core::ExecuteInstruction(Instruction inst) {} + +} // namespace GTE \ No newline at end of file diff --git a/src/pse/gte.h b/src/pse/gte.h new file mode 100644 index 000000000..715cc56d0 --- /dev/null +++ b/src/pse/gte.h @@ -0,0 +1,32 @@ +#pragma once +#include "common/state_wrapper.h" +#include "gte_types.h" + +namespace GTE { + +class Core +{ +public: + Core(); + ~Core(); + + void Initialize(); + void Reset(); + bool DoState(StateWrapper& sw); + + u32 ReadRegister(u32 index) const { return m_regs.r32[index]; } + void WriteRegister(u32 index, u32 value) { m_regs.r32[index] = value; } + + u32 ReadDataRegister(u32 index) const { return m_regs.r32[index]; } + void WriteDataRegister(u32 index, u32 value) { m_regs.r32[index] = value; } + + u32 ReadControlRegister(u32 index) const { return m_regs.r32[index + 32]; } + void WriteControlRegister(u32 index, u32 value) { m_regs.r32[index + 32] = value; } + + void ExecuteInstruction(Instruction inst); + +private: + Regs m_regs = {}; +}; + +} // namespace GTE \ No newline at end of file diff --git a/src/pse/gte_types.h b/src/pse/gte_types.h new file mode 100644 index 000000000..ca125f662 --- /dev/null +++ b/src/pse/gte_types.h @@ -0,0 +1,103 @@ +#pragma once +#include "common/bitfield.h" +#include "types.h" + +namespace GTE { +static constexpr u32 NUM_REGS = 64; + +union Regs +{ + u32 r32[NUM_REGS]; + +#pragma pack(push, 1) + struct + { + s16 V0[3]; // 0-1 + u16 pad1; // 1 + s16 V1[3]; // 2-3 + u16 pad2; // 3 + s16 V2[3]; // 4-5 + u16 pad3; // 5 + u8 RGBC[4]; // 6 + u16 OTZ; // 7 + u16 pad4; // 7 + s16 IR0; // 8 + u16 pad5; // 8 + s16 IR1; // 9 + u16 pad6; // 9 + s16 IR2; // 10 + u16 pad7; // 10 + s16 IR3; // 11 + u16 pad8; // 11 + s16 SXY0; // 12 + u16 pad9; // 12 + s16 SXY1; // 13 + u16 pad10; // 13 + s16 SXY2; // 14 + u16 pad11; // 14 + s16 SXYP; // 15 + u16 pad12; // 15 + u16 SZ0; // 16 + u16 pad13; // 16 + u16 SZ1; // 17 + u16 pad14; // 17 + u16 SZ2; // 18 + u16 pad15; // 18 + u16 SZ3; // 19 + u16 pad16; // 19 + u32 RGB0; // 20 + u32 RGB1; // 21 + u32 RGB2; // 22 + u32 RES1; // 23 + s32 MAC0; // 24 + s32 MAC1; // 25 + s32 MAC2; // 26 + s32 MAC4; // 27 + u16 IRGB; // 28 + u16 ORGB; // 29 + s32 LZCS; // 30 + s32 LZCR; // 31 + u16 RT[3][3]; // 32-36 + u16 pad17; // 36 + s32 TR[3]; // 37-39 + u16 L[3][3]; // 40-44 + u16 pad18; // 44 + u32 RBK; // 45 + u32 GBK; // 46 + u32 BBK; // 47 + u16 LR[3][3]; // 48-52 + u16 pad19; // 52 + u32 RFC; // 53 + u32 GFC; // 54 + u32 BFC; // 55 + u32 OFX; // 56 + u32 OFY; // 57 + u16 H; // 58 + u16 pad20; // 58 + u16 DQA; // 59 + u16 pad21; // 59 + u32 DQB; // 60 + u16 ZSF3; // 61 + u16 pad22; // 61 + u16 ZSF4; // 62 + u16 pad23; // 62 + u32 FLAG; // 63 + }; +#pragma pack(pop) +}; +static_assert(sizeof(Regs) == (sizeof(u32) * NUM_REGS)); + +union Instruction +{ + u32 bits; + + BitField fake_command; + BitField sf; // shift fraction in IR registers, 0=no fraction, 1=12bit fraction + BitField mvmva_multiply_matrix; + BitField mvmva_multiply_vector; + BitField mvmva_translation_vector; + BitField lm; // saturate IR1, IR2, IR3 result + BitField command; +}; + +} // namespace GTE \ No newline at end of file diff --git a/src/pse/pse.vcxproj b/src/pse/pse.vcxproj index e553f727e..2de5bf836 100644 --- a/src/pse/pse.vcxproj +++ b/src/pse/pse.vcxproj @@ -39,6 +39,7 @@ + @@ -52,11 +53,13 @@ + + diff --git a/src/pse/pse.vcxproj.filters b/src/pse/pse.vcxproj.filters index c72d24162..4b66926de 100644 --- a/src/pse/pse.vcxproj.filters +++ b/src/pse/pse.vcxproj.filters @@ -12,6 +12,7 @@ + @@ -28,6 +29,8 @@ + +