Partial implementation of DMA controller and GPU stubs

This commit is contained in:
Connor McLaughlin 2019-09-11 14:01:19 +10:00
parent 2149ab4d69
commit 27913cd20a
13 changed files with 425 additions and 30 deletions

View file

@ -3,6 +3,7 @@
#include "YBaseLib/Log.h"
#include "YBaseLib/String.h"
#include "dma.h"
#include "gpu.h"
#include <cstdio>
Log_SetChannel(Bus);
@ -38,7 +39,7 @@ bool Bus::ReadByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_
return result;
}
bool Bus::ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value)
bool Bus::ReadHalfWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value)
{
u32 temp = 0;
const bool result =
@ -47,7 +48,7 @@ bool Bus::ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_
return result;
}
bool Bus::ReadDWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value)
bool Bus::ReadWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32* value)
{
return DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(cpu_address, bus_address, *value);
}
@ -58,13 +59,13 @@ bool Bus::WriteByte(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus
return DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Byte>(cpu_address, bus_address, temp);
}
bool Bus::WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value)
bool Bus::WriteHalfWord(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)
bool Bus::WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value)
{
return DispatchAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(cpu_address, bus_address, value);
}
@ -178,6 +179,20 @@ bool Bus::WriteExpansionRegion2(MemoryAccessSize size, u32 offset, u32 value)
return DoInvalidAccess(MemoryAccessType::Write, size, EXP2_BASE | offset, EXP2_BASE | offset, value);
}
bool Bus::DoReadGPU(MemoryAccessSize size, u32 offset, u32& value)
{
Assert(size == MemoryAccessSize::Word);
value = m_gpu->ReadRegister(offset);
return true;
}
bool Bus::DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value)
{
Assert(size == MemoryAccessSize::Word);
m_gpu->WriteRegister(offset, value);
return true;
}
bool Bus::ReadSPU(MemoryAccessSize size, u32 offset, u32& value)
{
if (offset == 0x1AE)
@ -186,7 +201,7 @@ bool Bus::ReadSPU(MemoryAccessSize size, u32 offset, u32& value)
return true;
}
//return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value);
// return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value);
value = 0;
return true;
}
@ -201,7 +216,7 @@ bool Bus::WriteSPU(MemoryAccessSize size, u32 offset, u32 value)
if (offset == 0x1AA)
return true;
//return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value);
// return DoInvalidAccess(MemoryAccessType::Write, size, SPU_BASE | offset, SPU_BASE | offset, value);
return true;
}
@ -212,7 +227,7 @@ bool Bus::DoReadDMA(MemoryAccessSize size, u32 offset, u32& value)
return true;
}
bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32& value)
bool Bus::DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value)
{
Assert(size == MemoryAccessSize::Word);
m_dma->WriteRegister(offset, value);

View file

@ -20,11 +20,11 @@ public:
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 ReadHalfWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16* value);
bool ReadWord(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);
bool WriteHalfWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u16 value);
bool WriteWord(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32 value);
template<MemoryAccessType type, MemoryAccessSize size>
bool DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddress bus_address, u32& value);
@ -33,6 +33,9 @@ private:
static constexpr u32 DMA_BASE = 0x1F801080;
static constexpr u32 DMA_SIZE = 0x80;
static constexpr u32 DMA_MASK = DMA_SIZE - 1;
static constexpr u32 GPU_BASE = 0x1F801810;
static constexpr u32 GPU_SIZE = 0x10;
static constexpr u32 GPU_MASK = GPU_SIZE - 1;
static constexpr u32 SPU_BASE = 0x1F801C00;
static constexpr u32 SPU_SIZE = 0x300;
static constexpr u32 SPU_MASK = 0x3FF;
@ -54,11 +57,14 @@ private:
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 DoReadGPU(MemoryAccessSize size, u32 offset, u32& value);
bool DoWriteGPU(MemoryAccessSize size, u32 offset, u32 value);
bool DoReadDMA(MemoryAccessSize size, u32 offset, u32& value);
bool DoWriteDMA(MemoryAccessSize size, u32 offset, u32& value);
bool DoWriteDMA(MemoryAccessSize size, u32 offset, u32 value);
bool ReadSPU(MemoryAccessSize size, u32 offset, u32& value);
bool WriteSPU(MemoryAccessSize size, u32 offset, u32 value);
DMA* m_dma = nullptr;
GPU* m_gpu = nullptr;

View file

@ -89,6 +89,15 @@ bool Bus::DispatchAccess(PhysicalMemoryAddress cpu_address, PhysicalMemoryAddres
return (type == MemoryAccessType::Read) ? DoReadDMA(size, bus_address & DMA_MASK, value) :
DoWriteDMA(size, bus_address & DMA_MASK, value);
}
else if (bus_address < GPU_BASE)
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);
}
else if (bus_address < (GPU_BASE + GPU_SIZE))
{
return (type == MemoryAccessType::Read) ? DoReadGPU(size, bus_address & GPU_MASK, value) :
DoWriteGPU(size, bus_address & GPU_MASK, value);
}
else if (bus_address < SPU_BASE)
{
return DoInvalidAccess(type, size, cpu_address, bus_address, value);

View file

@ -526,6 +526,32 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
}
break;
case InstructionOp::lwl:
case InstructionOp::lwr:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
const u32 aligned_value = ReadMemoryWord(aligned_addr);
// note: bypasses load delay on the read
const u32 existing_value = m_regs.r[static_cast<u8>(inst.i.rt.GetValue())];
const u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
u32 new_value;
if (inst.op == InstructionOp::lwl)
{
const u32 mask = UINT32_C(0x00FFFFFF) >> shift;
new_value = (existing_value & mask) | (aligned_value << (24 - shift));
}
else
{
const u32 mask = UINT32_C(0xFFFFFF00) << (24 - shift);
new_value = (existing_value & mask) | (aligned_value >> shift);
}
WriteRegDelayed(inst.i.rt, new_value);
}
break;
case InstructionOp::sb:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
@ -550,6 +576,31 @@ void Core::ExecuteInstruction(Instruction inst, u32 inst_pc)
}
break;
case InstructionOp::swl:
case InstructionOp::swr:
{
const VirtualMemoryAddress addr = ReadReg(inst.i.rs) + inst.i.imm_sext32();
const VirtualMemoryAddress aligned_addr = addr & ~UINT32_C(3);
const u32 mem_value = ReadMemoryWord(aligned_addr);
const u32 reg_value = ReadReg(inst.i.rt);
const u8 shift = (Truncate8(addr) & u8(3)) * u8(8);
u32 new_value;
if (inst.op == InstructionOp::swl)
{
const u32 mem_mask = UINT32_C(0xFFFFFF00) << shift;
new_value = (mem_value & mem_mask) | (reg_value >> (24 - shift));
}
else
{
const u32 mem_mask = UINT32_C(0x00FFFFFF) >> (24 - shift);
new_value = (mem_value & mem_mask) | (reg_value << shift);
}
WriteMemoryWord(aligned_addr, new_value);
}
break;
case InstructionOp::j:
{
Branch((m_regs.pc & UINT32_C(0xF0000000)) | (inst.j.target << 2));

View file

@ -61,19 +61,19 @@ static const std::array<const char*, 64> s_base_table = {{
"UNKNOWN", // 31
"lb $rt, $offsetrs", // 32
"lh $rt, $offsetrs", // 33
"UNKNOWN", // 34
"lwl $rt, $offsetrs", // 34
"lw $rt, $offsetrs", // 35
"lbu $rt, $offsetrs", // 36
"lhu $rt, $offsetrs", // 37
"UNKNOWN", // 38
"lwr $rt, $offsetrs", // 38
"UNKNOWN", // 39
"sb $rt, $offsetrs", // 40
"sh $rt, $offsetrs", // 41
"UNKNOWN", // 42
"swl $rt, $offsetrs", // 42
"sw $rt, $offsetrs", // 43
"UNKNOWN", // 44
"UNKNOWN", // 45
"UNKNOWN", // 46
"swr $rt, $offsetrs", // 46
"UNKNOWN", // 47
"UNKNOWN", // 48
"UNKNOWN", // 49

View file

@ -17,7 +17,7 @@ bool DMA::Initialize(Bus* bus, GPU* gpu)
void DMA::Reset()
{
m_state = {};
m_DPCR.bits = 0;
m_DPCR.bits = 0x07654321;
m_DCIR = 0;
}
@ -52,5 +52,160 @@ u32 DMA::ReadRegister(u32 offset)
void DMA::WriteRegister(u32 offset, u32 value)
{
const u32 channel_index = offset >> 4;
if (channel_index < 7)
{
ChannelState& state = m_state[channel_index];
switch (offset & UINT32_C(0x0F))
{
case 0x00:
{
state.base_address = value & BASE_ADDRESS_MASK;
Log_DebugPrintf("DMA channel %u base address <- 0x%08X", channel_index, state.base_address);
return;
}
case 0x04:
{
Log_DebugPrintf("DMA channel %u block control <- 0x%08X", channel_index, value);
state.block_control.bits = value;
return;
}
case 0x08:
{
state.channel_control.bits = (state.channel_control.bits & ~ChannelState::ChannelControl::WRITE_MASK) |
(value & ChannelState::ChannelControl::WRITE_MASK);
Log_DebugPrintf("DMA channel %u channel control <- 0x%08X", channel_index, state.channel_control.bits);
if (CanRunChannel(static_cast<Channel>(channel_index)))
RunDMA(static_cast<Channel>(channel_index));
return;
}
default:
break;
}
}
else
{
switch (offset)
{
case 0x70:
{
Log_DebugPrintf("DPCR <- 0x%08X", value);
m_DPCR.bits = value;
return;
}
case 0x74:
{
m_DCIR = (m_DCIR & ~DCIR_WRITE_MASK) | (value & DCIR_WRITE_MASK);
Log_DebugPrintf("DCIR <- 0x%08X", m_DCIR);
return;
}
default:
break;
}
}
Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value);
}
void DMA::SetRequest(Channel channel, bool request)
{
ChannelState& cs = m_state[static_cast<u32>(channel)];
cs.request = request;
if (CanRunChannel(channel))
RunDMA(channel);
}
bool DMA::CanRunChannel(Channel channel) const
{
if (!m_DPCR.GetMasterEnable(channel))
return false;
const ChannelState& cs = m_state[static_cast<u32>(channel)];
if (cs.channel_control.start_trigger)
return true;
return (cs.channel_control.enable_busy && cs.request);
}
void DMA::RunDMA(Channel channel)
{
ChannelState& cs = m_state[static_cast<u32>(channel)];
const PhysicalMemoryAddress memory_address = cs.base_address;
const bool copy_to_device = cs.channel_control.copy_to_device;
Log_DebugPrintf("Running DMA for channel %u", static_cast<u32>(channel));
Assert(Common::IsAlignedPow2(memory_address, 4));
// start/trigger bit is cleared on beginning of transfer
cs.channel_control.start_trigger = false;
switch (cs.channel_control.sync_mode)
{
case SyncMode::Manual:
{
const u32 word_count = cs.block_control.manual.GetWordCount();
Log_DebugPrintf(" ... copying %u words %s 0x%08X", word_count, copy_to_device ? "from" : "to", memory_address);
if (copy_to_device)
{
for (u32 i = 0; i < word_count; i++)
{
u32 memory_value = 0;
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(memory_address, memory_address,
memory_value);
DMAWrite(channel, memory_value);
}
}
else
{
for (u32 i = 0; i < word_count; i++)
{
u32 memory_value = DMARead(channel);
m_bus->DispatchAccess<MemoryAccessType::Write, MemoryAccessSize::Word>(memory_address, memory_address,
memory_value);
}
}
}
break;
case SyncMode::Request:
case SyncMode::LinkedList:
default:
Panic("Unimplemented sync mode");
break;
}
// start/busy bit is cleared on end of transfer
cs.channel_control.enable_busy = false;
}
u32 DMA::DMARead(Channel channel)
{
switch (channel)
{
case Channel::OTC:
{
// we just return zeros here.. guessing it's pulled low?
return 0;
}
case Channel::MDECin:
case Channel::MDECout:
case Channel::GPU:
case Channel::CDROM:
case Channel::SPU:
case Channel::PIO:
default:
Panic("Unhandled DMA channel write");
return UINT32_C(0xFFFFFFFF);
}
}
void DMA::DMAWrite(Channel channel, u32 value)
{
Panic("Unhandled DMA channel write");
}

View file

@ -34,18 +34,33 @@ public:
u32 ReadRegister(u32 offset);
void WriteRegister(u32 offset, u32 value);
void SetRequest(Channel channel, bool request);
private:
Bus* m_bus = nullptr;
GPU* m_gpu = nullptr;
static constexpr PhysicalMemoryAddress BASE_ADDRESS_MASK = UINT32_C(0x00FFFFFF);
enum class SyncMode : u32
{
Word = 0,
Block = 1,
Manual = 0,
Request = 1,
LinkedList = 2,
Reserved = 3
};
// is everything enabled for a channel to operate?
bool CanRunChannel(Channel channel) const;
void RunDMA(Channel channel);
// from device -> memory
u32 DMARead(Channel channel);
// from memory -> device
void DMAWrite(Channel channel, u32 value);
Bus* m_bus = nullptr;
GPU* m_gpu = nullptr;
struct ChannelState
{
u32 base_address;
@ -56,25 +71,35 @@ private:
struct
{
BitField<u32, u32, 0, 16> word_count;
} word_mode;
u32 GetWordCount() const { return (word_count == 0) ? 0x10000 : word_count; }
} manual;
struct
{
BitField<u32, u32, 0, 16> block_size;
BitField<u32, u32, 16, 16> block_count;
} block_mode;
u32 GetBlockSize() const { return (block_size == 0) ? 0x10000 : block_size; }
u32 GetBlockCount() const { return (block_count == 0) ? 0x10000 : block_count; }
} request;
} block_control;
union ChannelControl
{
u32 bits;
BitField<u32, bool, 0, 1> direction_to_ram;
BitField<u32, bool, 0, 1> copy_to_device;
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, 24, 1> enable_busy;
BitField<u32, bool, 28, 1> start_trigger;
static constexpr u32 WRITE_MASK = 0b01110001'01110111'00000111'00000011;
} channel_control;
bool request = false;
};
std::array<ChannelState, NUM_CHANNELS> m_state = {};
@ -83,8 +108,8 @@ private:
{
u32 bits;
u8 GetPriority(Channel channel) { return ((bits >> (static_cast<u8>(channel) * 4)) & u32(3)); }
bool GetMasterEnable(Channel channel)
u8 GetPriority(Channel channel) const { return ((bits >> (static_cast<u8>(channel) * 4)) & u32(3)); }
bool GetMasterEnable(Channel channel) const
{
return ConvertToBoolUnchecked((bits >> (static_cast<u8>(channel) * 4 + 3)) & u32(1));
}

65
src/pse/gpu.cpp Normal file
View file

@ -0,0 +1,65 @@
#include "gpu.h"
#include "YBaseLib/Log.h"
#include "bus.h"
Log_SetChannel(GPU);
GPU::GPU() = default;
GPU::~GPU() = default;
bool GPU::Initialize(Bus* bus, DMA* dma)
{
m_bus = bus;
m_dma = dma;
return true;
}
void GPU::Reset()
{
SoftReset();
}
void GPU::SoftReset()
{
m_GPUSTAT.bits = 0x14802000;
}
u32 GPU::ReadRegister(u32 offset)
{
if (offset == 0x00)
{
// GPUREAD
Log_ErrorPrintf("GPUREAD");
return 0;
}
else if (offset == 0x04)
{
// GPUSTAT
return m_GPUSTAT.bits;
}
Log_ErrorPrintf("Unhandled register read: %02X", offset);
return UINT32_C(0xFFFFFFFF);
}
void GPU::WriteRegister(u32 offset, u32 value)
{
if (offset == 0x00)
WriteGP0(value);
else if (offset == 0x04)
WriteGP1(value);
else
Log_ErrorPrintf("Unhandled register write: %02X <- %08X", offset, value);
}
void GPU::WriteGP0(u32 value)
{
const u8 command = Truncate8(value >> 24);
Log_ErrorPrintf("Unimplemented GP0 command 0x%02X", command);
}
void GPU::WriteGP1(u32 value)
{
const u8 command = Truncate8(value >> 24);
Log_ErrorPrintf("Unimplemented GP1 command 0x%02X", command);
}

58
src/pse/gpu.h Normal file
View file

@ -0,0 +1,58 @@
#pragma once
#include "common/bitfield.h"
#include "types.h"
#include <array>
class Bus;
class DMA;
class GPU
{
public:
GPU();
~GPU();
bool Initialize(Bus* bus, DMA* dma);
void Reset();
u32 ReadRegister(u32 offset);
void WriteRegister(u32 offset, u32 value);
private:
void SoftReset();
void WriteGP0(u32 value);
void WriteGP1(u32 value);
Bus* m_bus = nullptr;
DMA* m_dma = nullptr;
union GPUSTAT
{
u32 bits;
BitField<u32, u8, 0, 4> texture_page_x_base;
BitField<u32, u8, 4, 1> texture_page_y_base;
BitField<u32, u8, 5, 2> semi_transparency;
BitField<u32, u8, 7, 2> texture_page_colors;
BitField<u32, bool, 9, 1> dither_enable;
BitField<u32, bool, 10, 1> draw_to_display_area;
BitField<u32, bool, 11, 1> draw_set_mask_bit;
BitField<u32, bool, 12, 1> draw_to_masked_pixels;
BitField<u32, bool, 13, 1> interlaced_field;
BitField<u32, bool, 14, 1> reverse_flag;
BitField<u32, bool, 15, 1> texture_disable;
BitField<u32, u8, 16, 1> horizontal_resolution_2;
BitField<u32, u8, 17, 2> horizontal_resolution_1;
BitField<u32, u8, 19, 1> vetical_resolution;
BitField<u32, bool, 20, 1> pal_mode;
BitField<u32, bool, 21, 1> display_area_color_depth_24;
BitField<u32, bool, 22, 1> vertical_interlace;
BitField<u32, bool, 23, 1> display_enable;
BitField<u32, bool, 24, 1> interrupt_request;
BitField<u32, bool, 25, 1> dma_data_request;
BitField<u32, bool, 26, 1> ready_to_recieve_cmd;
BitField<u32, bool, 27, 1> ready_to_send_vram;
BitField<u32, bool, 28, 1> ready_to_recieve_dma;
BitField<u32, u8, 29, 2> dma_direction;
BitField<u32, bool, 31, 1> drawing_even_line;
} m_GPUSTAT = {};
};

View file

@ -39,6 +39,7 @@
<ClCompile Include="cpu_core.cpp" />
<ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="dma.cpp" />
<ClCompile Include="gpu.cpp" />
<ClCompile Include="system.cpp" />
</ItemGroup>
<ItemGroup>
@ -47,6 +48,7 @@
<ClInclude Include="cpu_disasm.h" />
<ClInclude Include="cpu_types.h" />
<ClInclude Include="dma.h" />
<ClInclude Include="gpu.h" />
<ClInclude Include="save_state_version.h" />
<ClInclude Include="system.h" />
<ClInclude Include="types.h" />

View file

@ -6,6 +6,7 @@
<ClCompile Include="cpu_disasm.cpp" />
<ClCompile Include="bus.cpp" />
<ClCompile Include="dma.cpp" />
<ClCompile Include="gpu.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@ -16,6 +17,7 @@
<ClInclude Include="cpu_disasm.h" />
<ClInclude Include="bus.h" />
<ClInclude Include="dma.h" />
<ClInclude Include="gpu.h" />
</ItemGroup>
<ItemGroup>
<None Include="cpu_core.inl" />

View file

@ -9,10 +9,13 @@ bool System::Initialize()
if (!m_cpu.Initialize(&m_bus))
return false;
if (!m_bus.Initialize(this, &m_dma, nullptr))
if (!m_bus.Initialize(this, &m_dma, &m_gpu))
return false;
if (!m_dma.Initialize(&m_bus, nullptr))
if (!m_dma.Initialize(&m_bus, &m_gpu))
return false;
if (!m_gpu.Initialize(&m_bus, &m_dma))
return false;
return true;
@ -22,6 +25,8 @@ void System::Reset()
{
m_cpu.Reset();
m_bus.Reset();
m_dma.Reset();
m_gpu.Reset();
}
void System::RunFrame()

View file

@ -1,6 +1,7 @@
#pragma once
#include "bus.h"
#include "dma.h"
#include "gpu.h"
#include "cpu_core.h"
#include "types.h"
@ -19,4 +20,5 @@ private:
CPU::Core m_cpu;
Bus m_bus;
DMA m_dma;
GPU m_gpu;
};