Adding some comments.
This commit is contained in:
parent
66cab7b76e
commit
249b952de9
|
@ -25,6 +25,9 @@ bool IsDebuggerAttached();
|
|||
// If no debugger is present, a signal will be raised.
|
||||
void Break();
|
||||
|
||||
// Prints a message to the attached debugger.
|
||||
// This bypasses the normal logging mechanism. If no debugger is attached it's
|
||||
// likely to no-op.
|
||||
void DebugPrint(const char* fmt, ...);
|
||||
|
||||
} // namespace debugging
|
||||
|
|
|
@ -20,16 +20,34 @@
|
|||
namespace xe {
|
||||
namespace filesystem {
|
||||
|
||||
// Canonicalizes a path, removing ..'s.
|
||||
std::string CanonicalizePath(const std::string& original_path);
|
||||
|
||||
// Returns true of the specified path exists as either a directory or file.
|
||||
bool PathExists(const std::wstring& path);
|
||||
|
||||
// Creates the parent folder of the specified path if needed.
|
||||
// This can be used to ensure the destination path for a new file exists before
|
||||
// attempting to create it.
|
||||
bool CreateParentFolder(const std::wstring& path);
|
||||
|
||||
// Creates a folder at the specified path.
|
||||
// Returns true if the path was created.
|
||||
bool CreateFolder(const std::wstring& path);
|
||||
|
||||
// Recursively deletes the files and folders at the specified path.
|
||||
// Returns true if the path was found and removed.
|
||||
bool DeleteFolder(const std::wstring& path);
|
||||
|
||||
// Returns true if the given path exists and is a folder.
|
||||
bool IsFolder(const std::wstring& path);
|
||||
|
||||
// Opens the file at the given path with the specified mode.
|
||||
// This behaves like fopen and the returned handle can be used with stdio.
|
||||
FILE* OpenFile(const std::wstring& path, const char* mode);
|
||||
|
||||
// Deletes the file at the given path.
|
||||
// Returns true if the file was found and removed.
|
||||
bool DeleteFile(const std::wstring& path);
|
||||
|
||||
struct FileAccess {
|
||||
|
|
|
@ -95,9 +95,8 @@ X_STATUS Emulator::Setup(
|
|||
|
||||
// Create memory system first, as it is required for other systems.
|
||||
memory_ = std::make_unique<Memory>();
|
||||
result = memory_->Initialize();
|
||||
if (result) {
|
||||
return result;
|
||||
if (!memory_->Initialize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shared export resolver used to attach and query for HLE exports.
|
||||
|
|
|
@ -43,33 +43,57 @@ class Window;
|
|||
|
||||
namespace xe {
|
||||
|
||||
// The main type that runs the whole emulator.
|
||||
// This is responsible for initializing and managing all the various subsystems.
|
||||
class Emulator {
|
||||
public:
|
||||
explicit Emulator(const std::wstring& command_line);
|
||||
~Emulator();
|
||||
|
||||
// Full command line used when launching the process.
|
||||
const std::wstring& command_line() const { return command_line_; }
|
||||
|
||||
// Window used for displaying graphical output.
|
||||
ui::Window* display_window() const { return display_window_; }
|
||||
|
||||
// Guest memory system modelling the RAM (both virtual and physical) of the
|
||||
// system.
|
||||
Memory* memory() const { return memory_.get(); }
|
||||
|
||||
// Active debugger, which may or may not be attached.
|
||||
debug::Debugger* debugger() const { return debugger_.get(); }
|
||||
|
||||
// Virtualized processor that can execute PPC code.
|
||||
cpu::Processor* processor() const { return processor_.get(); }
|
||||
|
||||
// Audio hardware emulation for decoding and playback.
|
||||
apu::AudioSystem* audio_system() const { return audio_system_.get(); }
|
||||
|
||||
// GPU emulation for command list processing.
|
||||
gpu::GraphicsSystem* graphics_system() const {
|
||||
return graphics_system_.get();
|
||||
}
|
||||
|
||||
// Human-interface Device (HID) adapters for controllers.
|
||||
hid::InputSystem* input_system() const { return input_system_.get(); }
|
||||
|
||||
// Kernel function export table used to resolve exports when JITing code.
|
||||
cpu::ExportResolver* export_resolver() const {
|
||||
return export_resolver_.get();
|
||||
}
|
||||
|
||||
// File systems mapped to disc images, folders, etc for games and save data.
|
||||
vfs::VirtualFileSystem* file_system() const { return file_system_.get(); }
|
||||
|
||||
// The 'kernel', tracking all kernel objects and other state.
|
||||
// This is effectively the guest operating system.
|
||||
kernel::KernelState* kernel_state() const { return kernel_state_.get(); }
|
||||
|
||||
// Initializes the emulator and configures all components.
|
||||
// The given window is used for display and the provided functions are used
|
||||
// to create subsystems as required.
|
||||
// Once this function returns a game can be launched using one of the Launch
|
||||
// functions.
|
||||
X_STATUS Setup(
|
||||
ui::Window* display_window,
|
||||
std::function<std::unique_ptr<apu::AudioSystem>(cpu::Processor*)>
|
||||
|
@ -79,9 +103,19 @@ class Emulator {
|
|||
std::function<std::vector<std::unique_ptr<hid::InputDriver>>(ui::Window*)>
|
||||
input_driver_factory);
|
||||
|
||||
// Launches a game from the given file path.
|
||||
// This will attempt to infer the type of the given file (such as an iso, etc)
|
||||
// using heuristics.
|
||||
X_STATUS LaunchPath(std::wstring path);
|
||||
|
||||
// Launches a game from a .xex file by mounting the containing folder as if it
|
||||
// was an extracted STFS container.
|
||||
X_STATUS LaunchXexFile(std::wstring path);
|
||||
|
||||
// Launches a game from a disc image file (.iso, etc).
|
||||
X_STATUS LaunchDiscImage(std::wstring path);
|
||||
|
||||
// Launches a game from an STFS container file.
|
||||
X_STATUS LaunchStfsContainer(std::wstring path);
|
||||
|
||||
private:
|
||||
|
|
|
@ -115,7 +115,7 @@ Memory::~Memory() {
|
|||
physical_membase_ = nullptr;
|
||||
}
|
||||
|
||||
int Memory::Initialize() {
|
||||
bool Memory::Initialize() {
|
||||
file_name_ = std::wstring(L"Local\\xenia_memory_") +
|
||||
std::to_wstring(Clock::QueryHostTickCount());
|
||||
|
||||
|
@ -128,7 +128,7 @@ int Memory::Initialize() {
|
|||
if (!mapping_) {
|
||||
XELOGE("Unable to reserve the 4gb guest address space.");
|
||||
assert_not_null(mapping_);
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to create our views. This may fail at the first address
|
||||
|
@ -144,7 +144,7 @@ int Memory::Initialize() {
|
|||
if (!mapping_base_) {
|
||||
XELOGE("Unable to find a continuous block in the 64bit address space.");
|
||||
assert_always();
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
virtual_membase_ = mapping_base_;
|
||||
physical_membase_ = mapping_base_ + 0x100000000ull;
|
||||
|
@ -189,7 +189,7 @@ int Memory::Initialize() {
|
|||
if (!mmio_handler_) {
|
||||
XELOGE("Unable to install MMIO handlers");
|
||||
assert_always();
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ?
|
||||
|
@ -197,7 +197,7 @@ int Memory::Initialize() {
|
|||
heaps_.vA0000000.Alloc(0x340000, 64 * 1024, kMemoryAllocationReserve,
|
||||
kMemoryProtectNoAccess, true, &unk_phys_alloc);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
|
|
|
@ -63,45 +63,85 @@ struct HeapAllocationInfo {
|
|||
uint32_t type;
|
||||
};
|
||||
|
||||
// Describes a single page in the page table.
|
||||
union PageEntry {
|
||||
struct {
|
||||
uint32_t base_address : 20; // in 4k pages
|
||||
uint32_t region_page_count : 20; // in 4k pages
|
||||
// Base address of the allocated region in 4k pages.
|
||||
uint32_t base_address : 20;
|
||||
// Total number of pages in the allocated region in 4k pages.
|
||||
uint32_t region_page_count : 20;
|
||||
// Protection bits specified during region allocation.
|
||||
// Composed of bits from MemoryProtectFlag.
|
||||
uint32_t allocation_protect : 4;
|
||||
// Current protection bits as of the last Protect.
|
||||
// Composed of bits from MemoryProtectFlag.
|
||||
uint32_t current_protect : 4;
|
||||
// Allocation state of the page as a MemoryAllocationFlag bit mask.
|
||||
uint32_t state : 2;
|
||||
uint32_t reserved : 14;
|
||||
};
|
||||
uint64_t qword;
|
||||
};
|
||||
|
||||
// Heap abstraction for page-based allocation.
|
||||
class BaseHeap {
|
||||
public:
|
||||
virtual ~BaseHeap();
|
||||
|
||||
// Size of each page within the heap range in bytes.
|
||||
uint32_t page_size() const { return page_size_; }
|
||||
|
||||
// Disposes and decommits all memory and clears the page table.
|
||||
virtual void Dispose();
|
||||
|
||||
// Dumps information about all allocations within the heap to the log.
|
||||
void DumpMap();
|
||||
|
||||
// Allocates pages with the given properties and allocation strategy.
|
||||
// This can reserve and commit the pages as well as set protection modes.
|
||||
// This will fail if not enough contiguous pages can be found.
|
||||
virtual bool Alloc(uint32_t size, uint32_t alignment,
|
||||
uint32_t allocation_type, uint32_t protect, bool top_down,
|
||||
uint32_t* out_address);
|
||||
|
||||
// Allocates pages at the given address.
|
||||
// This can reserve and commit the pages as well as set protection modes.
|
||||
// This will fail if the pages are already allocated.
|
||||
virtual bool AllocFixed(uint32_t base_address, uint32_t size,
|
||||
uint32_t alignment, uint32_t allocation_type,
|
||||
uint32_t protect);
|
||||
|
||||
// Allocates pages at an address within the given address range.
|
||||
// This can reserve and commit the pages as well as set protection modes.
|
||||
// This will fail if not enough contiguous pages can be found.
|
||||
virtual bool AllocRange(uint32_t low_address, uint32_t high_address,
|
||||
uint32_t size, uint32_t alignment,
|
||||
uint32_t allocation_type, uint32_t protect,
|
||||
bool top_down, uint32_t* out_address);
|
||||
|
||||
// Decommits pages in the given range.
|
||||
// Partial overlapping pages will also be decommitted.
|
||||
virtual bool Decommit(uint32_t address, uint32_t size);
|
||||
|
||||
// Decommits and releases pages in the given range.
|
||||
// Partial overlapping pages will also be released.
|
||||
virtual bool Release(uint32_t address, uint32_t* out_region_size = nullptr);
|
||||
|
||||
// Modifies the protection mode of pages within the given range.
|
||||
virtual bool Protect(uint32_t address, uint32_t size, uint32_t protect);
|
||||
|
||||
// Queries information about the given region of pages.
|
||||
bool QueryRegionInfo(uint32_t base_address, HeapAllocationInfo* out_info);
|
||||
|
||||
// Queries the size of the region containing the given address.
|
||||
bool QuerySize(uint32_t address, uint32_t* out_size);
|
||||
|
||||
// Queries the current protection mode of the region containing the given
|
||||
// address.
|
||||
bool QueryProtect(uint32_t address, uint32_t* out_protect);
|
||||
|
||||
// Gets the physical address of a virtual address.
|
||||
// This is only valid if the page is backed by a physical allocation.
|
||||
uint32_t GetPhysicalAddress(uint32_t address);
|
||||
|
||||
protected:
|
||||
|
@ -118,20 +158,30 @@ class BaseHeap {
|
|||
std::vector<PageEntry> page_table_;
|
||||
};
|
||||
|
||||
// Normal heap allowing allocations from guest virtual address ranges.
|
||||
class VirtualHeap : public BaseHeap {
|
||||
public:
|
||||
VirtualHeap();
|
||||
~VirtualHeap() override;
|
||||
|
||||
// Initializes the heap properties and allocates the page table.
|
||||
void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size,
|
||||
uint32_t page_size);
|
||||
};
|
||||
|
||||
// A heap for ranges of memory that are mapped to physical ranges.
|
||||
// Physical ranges are used by the audio and graphics subsystems representing
|
||||
// hardware wired directly to memory in the console.
|
||||
//
|
||||
// The physical heap and the behavior of sharing pages with virtual pages is
|
||||
// implemented by having a 'parent' heap that is used to perform allocation in
|
||||
// the guest virtual address space 1:1 with the physical address space.
|
||||
class PhysicalHeap : public BaseHeap {
|
||||
public:
|
||||
PhysicalHeap();
|
||||
~PhysicalHeap() override;
|
||||
|
||||
|
||||
// Initializes the heap properties and allocates the page table.
|
||||
void Initialize(uint8_t* membase, uint32_t heap_base, uint32_t heap_size,
|
||||
uint32_t page_size, VirtualHeap* parent_heap);
|
||||
|
||||
|
@ -152,16 +202,40 @@ class PhysicalHeap : public BaseHeap {
|
|||
VirtualHeap* parent_heap_;
|
||||
};
|
||||
|
||||
// Models the entire guest memory system on the console.
|
||||
// This exposes interfaces to both virtual and physical memory and a TLB and
|
||||
// page table for allocation, mapping, and protection.
|
||||
//
|
||||
// The memory is backed by a memory mapped file and is placed at a stable
|
||||
// fixed address in the host address space (like 0x100000000). This allows
|
||||
// efficient guest<->host address translations as well as easy sharing of the
|
||||
// memory across various subsystems.
|
||||
//
|
||||
// The guest memory address space is split into several ranges that have varying
|
||||
// properties such as page sizes, caching strategies, protections, and
|
||||
// overlap with other ranges. Each range is represented by a BaseHeap of either
|
||||
// VirtualHeap or PhysicalHeap depending on type. Heaps model the page tables
|
||||
// and can handle reservation and committing of requested pages.
|
||||
class Memory {
|
||||
public:
|
||||
Memory();
|
||||
~Memory();
|
||||
|
||||
int Initialize();
|
||||
// Initializes the memory system.
|
||||
// This may fail if the host address space could not be reserved or the
|
||||
// mapping to the file system fails.
|
||||
bool Initialize();
|
||||
|
||||
// Full file name and path of the memory-mapped file backing all memory.
|
||||
const std::wstring& file_name() const { return file_name_; }
|
||||
|
||||
// Base address of virtual memory in the host address space.
|
||||
// This is often something like 0x100000000.
|
||||
inline uint8_t* virtual_membase() const { return virtual_membase_; }
|
||||
|
||||
// Translates a guest virtual address to a host address that can be accessed
|
||||
// as a normal pointer.
|
||||
// Note that the contents at the specified host address are big-endian.
|
||||
inline uint8_t* TranslateVirtual(uint32_t guest_address) const {
|
||||
return virtual_membase_ + guest_address;
|
||||
}
|
||||
|
@ -170,7 +244,13 @@ class Memory {
|
|||
return reinterpret_cast<T>(virtual_membase_ + guest_address);
|
||||
}
|
||||
|
||||
// Base address of physical memory in the host address space.
|
||||
// This is often something like 0x200000000.
|
||||
inline uint8_t* physical_membase() const { return physical_membase_; }
|
||||
|
||||
// Translates a guest physical address to a host address that can be accessed
|
||||
// as a normal pointer.
|
||||
// Note that the contents at the specified host address are big-endian.
|
||||
inline uint8_t* TranslatePhysical(uint32_t guest_address) const {
|
||||
return physical_membase_ + (guest_address & 0x1FFFFFFF);
|
||||
}
|
||||
|
@ -179,32 +259,62 @@ class Memory {
|
|||
return reinterpret_cast<T>(physical_membase_ +
|
||||
(guest_address & 0x1FFFFFFF));
|
||||
}
|
||||
|
||||
// TODO(benvanik): make poly memory utils for these.
|
||||
|
||||
// Zeros out a range of memory at the given guest address.
|
||||
void Zero(uint32_t address, uint32_t size);
|
||||
|
||||
// Fills a range of guest memory with the given byte value.
|
||||
void Fill(uint32_t address, uint32_t size, uint8_t value);
|
||||
|
||||
// Copies a non-overlapping range of guest memory (like a memcpy).
|
||||
void Copy(uint32_t dest, uint32_t src, uint32_t size);
|
||||
|
||||
// Searches the given range of guest memory for a run of dword values in
|
||||
// big-endian order.
|
||||
uint32_t SearchAligned(uint32_t start, uint32_t end, const uint32_t* values,
|
||||
size_t value_count);
|
||||
|
||||
// Defines a memory-mapped IO (MMIO) virtual address range that when accessed
|
||||
// will trigger the specified read and write callbacks for dword read/writes.
|
||||
bool AddVirtualMappedRange(uint32_t virtual_address, uint32_t mask,
|
||||
uint32_t size, void* context,
|
||||
cpu::MMIOReadCallback read_callback,
|
||||
cpu::MMIOWriteCallback write_callback);
|
||||
|
||||
// Gets the defined MMIO range for the given virtual address, if any.
|
||||
cpu::MMIORange* LookupVirtualMappedRange(uint32_t virtual_address);
|
||||
|
||||
// Adds a write watch for the given physical address range that will trigger
|
||||
// the specified callback whenever any bytes are written in that range.
|
||||
// The returned handle can be used with CancelWriteWatch to remove the watch
|
||||
// if it is no longer required.
|
||||
//
|
||||
// This has a significant performance penalty for writes in in the range or
|
||||
// nearby (sharing 64KiB pages).
|
||||
uintptr_t AddPhysicalWriteWatch(uint32_t physical_address, uint32_t length,
|
||||
cpu::WriteWatchCallback callback,
|
||||
void* callback_context, void* callback_data);
|
||||
|
||||
// Cancels a write watch requested with AddPhysicalWriteWatch.
|
||||
void CancelWriteWatch(uintptr_t watch_handle);
|
||||
|
||||
// Allocates virtual memory from the 'system' heap.
|
||||
// System memory is kept separate from game memory but is still accessible
|
||||
// using normal guest virtual addresses. Kernel structures and other internal
|
||||
// 'system' allocations should come from this heap when possible.
|
||||
uint32_t SystemHeapAlloc(uint32_t size, uint32_t alignment = 0x20,
|
||||
uint32_t system_heap_flags = kSystemHeapDefault);
|
||||
|
||||
// Frees memory allocated with SystemHeapAlloc.
|
||||
void SystemHeapFree(uint32_t address);
|
||||
|
||||
// Gets the heap for the address space containing the given address.
|
||||
BaseHeap* LookupHeap(uint32_t address);
|
||||
|
||||
// Gets the heap with the given properties.
|
||||
BaseHeap* LookupHeapByType(bool physical, uint32_t page_size);
|
||||
|
||||
// Dumps a map of all allocated memory to the log.
|
||||
void DumpMap();
|
||||
|
||||
private:
|
||||
|
|
Loading…
Reference in New Issue