GTE: Stub and register read/write function

This commit is contained in:
Connor McLaughlin 2019-09-17 23:35:17 +10:00
parent 6df8d42480
commit 4025d6e4a6
11 changed files with 258 additions and 200 deletions

View file

@ -1,192 +0,0 @@
#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);
}

View file

@ -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<u32>(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

View file

@ -1,6 +1,7 @@
#pragma once
#include "common/bitfield.h"
#include "cpu_types.h"
#include "gte.h"
#include "types.h"
#include <array>
@ -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<u8, DCACHE_SIZE> m_dcache = {};
GTE::Core m_cop2;
};
extern bool TRACE_EXECUTION;

View file

@ -168,6 +168,13 @@ static const std::array<std::pair<Cop0Instruction, const char*>, 6> s_cop0_table
{Cop0Instruction::bc0c, "bc0$copcc $rel"},
{Cop0Instruction::rfe, "rfe"}}};
static const std::array<std::pair<Cop2Instruction, const char*>, 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("<cop%u 0x%07X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
dest->Format("<cop%u 0x%08X>", 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("<cop%u 0x%08X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
}
}
break;
case InstructionOp::cop1:
case InstructionOp::cop3:
dest->Format("<cop%u 0x%07X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
dest->Format("<cop%u 0x%08X>", ZeroExtend32(inst.cop.cop_n.GetValue()), inst.cop.imm25.GetValue());
break;
// special case for bltz/bgez{al}

View file

@ -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<u32, u16, 0, 16> imm16;
BitField<u32, u32, 0, 25> imm25;
bool IsCommonInstruction() const { return (bits & (UINT32_C(1) << 25)) == 0; }
Cop0Instruction cop0_op() const
{
return static_cast<Cop0Instruction>(((bits >> 15) & UINT32_C(0b11111000000)) | (bits & UINT32_C(0b111111)));
}
Cop2Instruction cop2_op() const
{
return static_cast<Cop2Instruction>((bits >> 21) & UINT32_C(0b1111));
}
} cop;
};

View file

@ -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;

24
src/pse/gte.cpp Normal file
View file

@ -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

32
src/pse/gte.h Normal file
View file

@ -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

103
src/pse/gte_types.h Normal file
View file

@ -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<u32, u8, 20, 5> fake_command;
BitField<u32, u8, 19, 1> sf; // shift fraction in IR registers, 0=no fraction, 1=12bit fraction
BitField<u32, u8, 17, 2> mvmva_multiply_matrix;
BitField<u32, u8, 15, 2> mvmva_multiply_vector;
BitField<u32, u8, 13, 2> mvmva_translation_vector;
BitField<u32, bool, 10, 1> lm; // saturate IR1, IR2, IR3 result
BitField<u32, u8, 0, 6> command;
};
} // namespace GTE

View file

@ -39,6 +39,7 @@
<ClCompile Include="cdrom.cpp" />
<ClCompile Include="cpu_core.cpp" />
<ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="gte.cpp" />
<ClCompile Include="dma.cpp" />
<ClCompile Include="gpu.cpp" />
<ClCompile Include="gpu_hw.cpp" />
@ -52,11 +53,13 @@
<ClInclude Include="cdrom.h" />
<ClInclude Include="cpu_core.h" />
<ClInclude Include="cpu_disasm.h" />
<ClInclude Include="gte.h" />
<ClInclude Include="cpu_types.h" />
<ClInclude Include="dma.h" />
<ClInclude Include="gpu.h" />
<ClInclude Include="gpu_hw.h" />
<ClInclude Include="gpu_hw_opengl.h" />
<ClInclude Include="gte_types.h" />
<ClInclude Include="host_interface.h" />
<ClInclude Include="interrupt_controller.h" />
<ClInclude Include="save_state_version.h" />

View file

@ -12,6 +12,7 @@
<ClCompile Include="host_interface.cpp" />
<ClCompile Include="interrupt_controller.cpp" />
<ClCompile Include="cdrom.cpp" />
<ClCompile Include="gte.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@ -28,6 +29,8 @@
<ClInclude Include="host_interface.h" />
<ClInclude Include="interrupt_controller.h" />
<ClInclude Include="cdrom.h" />
<ClInclude Include="gte.h" />
<ClInclude Include="gte_types.h" />
</ItemGroup>
<ItemGroup>
<None Include="cpu_core.inl" />