Merge remote-tracking branch 'upstream/master' into canary
This commit is contained in:
commit
b50feba296
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -401,4 +401,5 @@ int xenia_main(const std::vector<std::wstring>& args) {
|
|||
} // namespace app
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia", L"xenia some.xex", xe::app::xenia_main);
|
||||
DEFINE_ENTRY_POINT(L"xenia", xe::app::xenia_main, "[Path to .iso/.xex]",
|
||||
"target");
|
||||
|
|
|
@ -113,9 +113,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
|
|||
context_data_last_ptr_ =
|
||||
context_data_first_ptr_ + (sizeof(XMA_CONTEXT_DATA) * kContextCount - 1);
|
||||
register_file_[XE_XMA_REG_CONTEXT_ARRAY_ADDRESS].u32 =
|
||||
memory()
|
||||
->LookupHeap(context_data_first_ptr_)
|
||||
->GetPhysicalAddress(context_data_first_ptr_);
|
||||
memory()->GetPhysicalAddress(context_data_first_ptr_);
|
||||
|
||||
// Setup XMA contexts.
|
||||
for (int i = 0; i < kContextCount; ++i) {
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "cvar.h"
|
||||
|
||||
namespace cvar {
|
||||
|
@ -13,7 +22,9 @@ void PrintHelpAndExit() {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
void ParseLaunchArguments(int argc, char** argv) {
|
||||
void ParseLaunchArguments(int argc, char** argv,
|
||||
const std::string& positional_help,
|
||||
const std::vector<std::string>& positional_options) {
|
||||
options.add_options()("help", "Prints help and exit.");
|
||||
if (!CmdVars) CmdVars = new std::map<std::string, ICommandVar*>();
|
||||
if (!ConfigVars) ConfigVars = new std::map<std::string, IConfigVar*>();
|
||||
|
@ -29,8 +40,8 @@ void ParseLaunchArguments(int argc, char** argv) {
|
|||
configVar->AddToLaunchOptions(&options);
|
||||
}
|
||||
try {
|
||||
options.positional_help("[Path to .iso/.xex]");
|
||||
options.parse_positional({"target"});
|
||||
options.positional_help(positional_help);
|
||||
options.parse_positional(positional_options);
|
||||
|
||||
auto result = options.parse(argc, argv);
|
||||
if (result.count("help")) {
|
||||
|
|
|
@ -1,10 +1,23 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CVAR_H_
|
||||
#define XENIA_CVAR_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cpptoml/include/cpptoml.h"
|
||||
#include "cxxopts/include/cxxopts.hpp"
|
||||
#include "xenia/base/string_util.h"
|
||||
|
||||
namespace cvar {
|
||||
|
||||
namespace toml {
|
||||
|
@ -204,7 +217,9 @@ inline void AddCommandVar(ICommandVar* cv) {
|
|||
if (!CmdVars) CmdVars = new std::map<std::string, ICommandVar*>();
|
||||
CmdVars->insert(std::pair<std::string, ICommandVar*>(cv->name(), cv));
|
||||
}
|
||||
void ParseLaunchArguments(int argc, char** argv);
|
||||
void ParseLaunchArguments(int argc, char** argv,
|
||||
const std::string& positional_help,
|
||||
const std::vector<std::string>& positional_options);
|
||||
|
||||
template <typename T>
|
||||
T* define_configvar(const char* name, T* default_value, const char* description,
|
||||
|
@ -221,8 +236,6 @@ T* define_cmdvar(const char* name, T* default_value, const char* description) {
|
|||
AddCommandVar(cmdVar);
|
||||
return default_value;
|
||||
}
|
||||
#define DEFINE_double(name, default_value, description, category) \
|
||||
DEFINE_CVar(name, default_value, description, category, false, double)
|
||||
|
||||
#define DEFINE_int32(name, default_value, description, category) \
|
||||
DEFINE_CVar(name, default_value, description, category, false, int32_t)
|
||||
|
@ -230,6 +243,9 @@ T* define_cmdvar(const char* name, T* default_value, const char* description) {
|
|||
#define DEFINE_uint64(name, default_value, description, category) \
|
||||
DEFINE_CVar(name, default_value, description, category, false, uint64_t)
|
||||
|
||||
#define DEFINE_double(name, default_value, description, category) \
|
||||
DEFINE_CVar(name, default_value, description, category, false, double)
|
||||
|
||||
#define DEFINE_string(name, default_value, description, category) \
|
||||
DEFINE_CVar(name, default_value, description, category, false, std::string)
|
||||
|
||||
|
@ -275,4 +291,5 @@ T* define_cmdvar(const char* name, T* default_value, const char* description) {
|
|||
}
|
||||
|
||||
} // namespace cvar
|
||||
|
||||
#endif // XENIA_CVAR_H_
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -13,6 +13,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -24,14 +25,19 @@ bool has_console_attached();
|
|||
// launch.
|
||||
struct EntryInfo {
|
||||
std::wstring name;
|
||||
std::wstring usage;
|
||||
std::string positional_usage;
|
||||
std::vector<std::string> positional_options;
|
||||
int (*entry_point)(const std::vector<std::wstring>& args);
|
||||
};
|
||||
EntryInfo GetEntryInfo();
|
||||
|
||||
#define DEFINE_ENTRY_POINT(name, usage, entry_point) \
|
||||
xe::EntryInfo xe::GetEntryInfo() { \
|
||||
return xe::EntryInfo({name, usage, entry_point}); \
|
||||
#define DEFINE_ENTRY_POINT(name, entry_point, positional_usage, ...) \
|
||||
xe::EntryInfo xe::GetEntryInfo() { \
|
||||
std::initializer_list<std::string> positional_options = {__VA_ARGS__}; \
|
||||
return xe::EntryInfo( \
|
||||
{name, positional_usage, \
|
||||
std::vector<std::string>(std::move(positional_options)), \
|
||||
entry_point}); \
|
||||
}
|
||||
|
||||
} // namespace xe
|
||||
|
|
|
@ -22,7 +22,8 @@ bool has_console_attached() { return true; }
|
|||
extern "C" int main(int argc, char** argv) {
|
||||
auto entry_info = xe::GetEntryInfo();
|
||||
|
||||
cvar::ParseLaunchArguments(argc, argv);
|
||||
cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage,
|
||||
entry_info.positional_options);
|
||||
|
||||
std::vector<std::wstring> args;
|
||||
for (int n = 0; n < argc; n++) {
|
||||
|
|
|
@ -105,7 +105,8 @@ int Main() {
|
|||
std::wcstombs(argva[n], argv[n], len + 1);
|
||||
}
|
||||
|
||||
cvar::ParseLaunchArguments(argca, argva);
|
||||
cvar::ParseLaunchArguments(argca, argva, entry_info.positional_usage,
|
||||
entry_info.positional_options);
|
||||
|
||||
// Widen all remaining flags and convert to usable strings.
|
||||
std::vector<std::wstring> args;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -407,13 +407,27 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
|
|||
// rdx = arg0 (context)
|
||||
// r8 = arg1 (guest return address)
|
||||
|
||||
struct _code_offsets {
|
||||
size_t prolog;
|
||||
size_t prolog_stack_alloc;
|
||||
size_t body;
|
||||
size_t epilog;
|
||||
size_t tail;
|
||||
} code_offsets = {};
|
||||
|
||||
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
|
||||
|
||||
code_offsets.prolog = getSize();
|
||||
|
||||
// rsp + 0 = return address
|
||||
mov(qword[rsp + 8 * 3], r8);
|
||||
mov(qword[rsp + 8 * 2], rdx);
|
||||
mov(qword[rsp + 8 * 1], rcx);
|
||||
sub(rsp, stack_size);
|
||||
|
||||
code_offsets.prolog_stack_alloc = getSize();
|
||||
code_offsets.body = getSize();
|
||||
|
||||
// Save nonvolatile registers.
|
||||
EmitSaveNonvolatileRegs();
|
||||
|
||||
|
@ -424,13 +438,28 @@ HostToGuestThunk X64ThunkEmitter::EmitHostToGuestThunk() {
|
|||
|
||||
EmitLoadNonvolatileRegs();
|
||||
|
||||
code_offsets.epilog = getSize();
|
||||
|
||||
add(rsp, stack_size);
|
||||
mov(rcx, qword[rsp + 8 * 1]);
|
||||
mov(rdx, qword[rsp + 8 * 2]);
|
||||
mov(r8, qword[rsp + 8 * 3]);
|
||||
ret();
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
code_offsets.tail = getSize();
|
||||
|
||||
assert_zero(code_offsets.prolog);
|
||||
EmitFunctionInfo func_info = {};
|
||||
func_info.code_size.total = getSize();
|
||||
func_info.code_size.prolog = code_offsets.body - code_offsets.prolog;
|
||||
func_info.code_size.body = code_offsets.epilog - code_offsets.body;
|
||||
func_info.code_size.epilog = code_offsets.tail - code_offsets.epilog;
|
||||
func_info.code_size.tail = getSize() - code_offsets.tail;
|
||||
func_info.prolog_stack_alloc_offset =
|
||||
code_offsets.prolog_stack_alloc - code_offsets.prolog;
|
||||
func_info.stack_size = stack_size;
|
||||
|
||||
void* fn = Emplace(func_info);
|
||||
return (HostToGuestThunk)fn;
|
||||
}
|
||||
|
||||
|
@ -440,10 +469,24 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
|||
// r8 = arg1
|
||||
// r9 = arg2
|
||||
|
||||
struct _code_offsets {
|
||||
size_t prolog;
|
||||
size_t prolog_stack_alloc;
|
||||
size_t body;
|
||||
size_t epilog;
|
||||
size_t tail;
|
||||
} code_offsets = {};
|
||||
|
||||
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
|
||||
|
||||
code_offsets.prolog = getSize();
|
||||
|
||||
// rsp + 0 = return address
|
||||
sub(rsp, stack_size);
|
||||
|
||||
code_offsets.prolog_stack_alloc = getSize();
|
||||
code_offsets.body = getSize();
|
||||
|
||||
// Save off volatile registers.
|
||||
EmitSaveVolatileRegs();
|
||||
|
||||
|
@ -453,10 +496,25 @@ GuestToHostThunk X64ThunkEmitter::EmitGuestToHostThunk() {
|
|||
|
||||
EmitLoadVolatileRegs();
|
||||
|
||||
code_offsets.epilog = getSize();
|
||||
|
||||
add(rsp, stack_size);
|
||||
ret();
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
code_offsets.tail = getSize();
|
||||
|
||||
assert_zero(code_offsets.prolog);
|
||||
EmitFunctionInfo func_info = {};
|
||||
func_info.code_size.total = getSize();
|
||||
func_info.code_size.prolog = code_offsets.body - code_offsets.prolog;
|
||||
func_info.code_size.body = code_offsets.epilog - code_offsets.body;
|
||||
func_info.code_size.epilog = code_offsets.tail - code_offsets.epilog;
|
||||
func_info.code_size.tail = getSize() - code_offsets.tail;
|
||||
func_info.prolog_stack_alloc_offset =
|
||||
code_offsets.prolog_stack_alloc - code_offsets.prolog;
|
||||
func_info.stack_size = stack_size;
|
||||
|
||||
void* fn = Emplace(func_info);
|
||||
return (GuestToHostThunk)fn;
|
||||
}
|
||||
|
||||
|
@ -466,11 +524,25 @@ extern "C" uint64_t ResolveFunction(void* raw_context, uint32_t target_address);
|
|||
ResolveFunctionThunk X64ThunkEmitter::EmitResolveFunctionThunk() {
|
||||
// ebx = target PPC address
|
||||
// rcx = context
|
||||
|
||||
struct _code_offsets {
|
||||
size_t prolog;
|
||||
size_t prolog_stack_alloc;
|
||||
size_t body;
|
||||
size_t epilog;
|
||||
size_t tail;
|
||||
} code_offsets = {};
|
||||
|
||||
const size_t stack_size = StackLayout::THUNK_STACK_SIZE;
|
||||
|
||||
code_offsets.prolog = getSize();
|
||||
|
||||
// rsp + 0 = return address
|
||||
sub(rsp, stack_size);
|
||||
|
||||
code_offsets.prolog_stack_alloc = getSize();
|
||||
code_offsets.body = getSize();
|
||||
|
||||
// Save volatile registers
|
||||
EmitSaveVolatileRegs();
|
||||
|
||||
|
@ -481,10 +553,25 @@ ResolveFunctionThunk X64ThunkEmitter::EmitResolveFunctionThunk() {
|
|||
|
||||
EmitLoadVolatileRegs();
|
||||
|
||||
code_offsets.epilog = getSize();
|
||||
|
||||
add(rsp, stack_size);
|
||||
jmp(rax);
|
||||
|
||||
void* fn = Emplace(stack_size);
|
||||
code_offsets.tail = getSize();
|
||||
|
||||
assert_zero(code_offsets.prolog);
|
||||
EmitFunctionInfo func_info = {};
|
||||
func_info.code_size.total = getSize();
|
||||
func_info.code_size.prolog = code_offsets.body - code_offsets.prolog;
|
||||
func_info.code_size.body = code_offsets.epilog - code_offsets.body;
|
||||
func_info.code_size.epilog = code_offsets.tail - code_offsets.epilog;
|
||||
func_info.code_size.tail = getSize() - code_offsets.tail;
|
||||
func_info.prolog_stack_alloc_offset =
|
||||
code_offsets.prolog_stack_alloc - code_offsets.prolog;
|
||||
func_info.stack_size = stack_size;
|
||||
|
||||
void* fn = Emplace(func_info);
|
||||
return (ResolveFunctionThunk)fn;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -125,21 +125,21 @@ void X64CodeCache::CommitExecutableRange(uint32_t guest_low,
|
|||
}
|
||||
|
||||
void* X64CodeCache::PlaceHostCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size) {
|
||||
const EmitFunctionInfo& func_info) {
|
||||
// Same for now. We may use different pools or whatnot later on, like when
|
||||
// we only want to place guest code in a serialized cache on disk.
|
||||
return PlaceGuestCode(guest_address, machine_code, code_size, stack_size,
|
||||
nullptr);
|
||||
return PlaceGuestCode(guest_address, machine_code, func_info, nullptr);
|
||||
}
|
||||
|
||||
void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
const EmitFunctionInfo& func_info,
|
||||
GuestFunction* function_info) {
|
||||
// Hold a lock while we bump the pointers up. This is important as the
|
||||
// unwind table requires entries AND code to be sorted in order.
|
||||
size_t low_mark;
|
||||
size_t high_mark;
|
||||
uint8_t* code_address = nullptr;
|
||||
size_t code_offset;
|
||||
uint8_t* code_address;
|
||||
UnwindReservation unwind_reservation;
|
||||
{
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
@ -148,15 +148,16 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
|||
|
||||
// Reserve code.
|
||||
// Always move the code to land on 16b alignment.
|
||||
code_address = generated_code_base_ + generated_code_offset_;
|
||||
generated_code_offset_ += xe::round_up(code_size, 16);
|
||||
code_offset = generated_code_offset_;
|
||||
code_address = generated_code_base_ + code_offset;
|
||||
generated_code_offset_ += xe::round_up(func_info.code_size.total, 16);
|
||||
|
||||
// Reserve unwind info.
|
||||
// We go on the high size of the unwind info as we don't know how big we
|
||||
// need it, and a few extra bytes of padding isn't the worst thing.
|
||||
unwind_reservation =
|
||||
RequestUnwindReservation(generated_code_base_ + generated_code_offset_);
|
||||
generated_code_offset_ += unwind_reservation.data_size;
|
||||
generated_code_offset_ += xe::round_up(unwind_reservation.data_size, 16);
|
||||
|
||||
high_mark = generated_code_offset_;
|
||||
|
||||
|
@ -187,15 +188,14 @@ void* X64CodeCache::PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
|||
old_commit_mark, new_commit_mark));
|
||||
|
||||
// Copy code.
|
||||
std::memcpy(code_address, machine_code, code_size);
|
||||
std::memcpy(code_address, machine_code, func_info.code_size.total);
|
||||
|
||||
// Fill unused slots with 0xCC
|
||||
std::memset(
|
||||
code_address + code_size, 0xCC,
|
||||
xe::round_up(code_size + unwind_reservation.data_size, 16) - code_size);
|
||||
std::memset(code_address + func_info.code_size.total, 0xCC,
|
||||
generated_code_offset_ - code_offset);
|
||||
|
||||
// Notify subclasses of placed code.
|
||||
PlaceCode(guest_address, machine_code, code_size, stack_size, code_address,
|
||||
PlaceCode(guest_address, machine_code, func_info, code_address,
|
||||
unwind_reservation);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -25,6 +25,18 @@ namespace cpu {
|
|||
namespace backend {
|
||||
namespace x64 {
|
||||
|
||||
struct EmitFunctionInfo {
|
||||
struct _code_size {
|
||||
size_t prolog;
|
||||
size_t body;
|
||||
size_t epilog;
|
||||
size_t tail;
|
||||
size_t total;
|
||||
} code_size;
|
||||
size_t prolog_stack_alloc_offset; // offset of instruction after stack alloc
|
||||
size_t stack_size;
|
||||
};
|
||||
|
||||
class X64CodeCache : public CodeCache {
|
||||
public:
|
||||
~X64CodeCache() override;
|
||||
|
@ -48,9 +60,9 @@ class X64CodeCache : public CodeCache {
|
|||
void CommitExecutableRange(uint32_t guest_low, uint32_t guest_high);
|
||||
|
||||
void* PlaceHostCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size);
|
||||
const EmitFunctionInfo& func_info);
|
||||
void* PlaceGuestCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
const EmitFunctionInfo& func_info,
|
||||
GuestFunction* function_info);
|
||||
uint32_t PlaceData(const void* data, size_t length);
|
||||
|
||||
|
@ -84,8 +96,7 @@ class X64CodeCache : public CodeCache {
|
|||
return UnwindReservation();
|
||||
}
|
||||
virtual void PlaceCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
void* code_address,
|
||||
const EmitFunctionInfo& func_info, void* code_address,
|
||||
UnwindReservation unwind_reservation) {}
|
||||
|
||||
std::wstring file_name_;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -21,16 +21,10 @@
|
|||
#include "xenia/cpu/function.h"
|
||||
|
||||
// Function pointer definitions
|
||||
typedef DWORD(NTAPI* FnRtlAddGrowableFunctionTable)(
|
||||
_Out_ PVOID* DynamicTable,
|
||||
_In_reads_(MaximumEntryCount) PRUNTIME_FUNCTION FunctionTable,
|
||||
_In_ DWORD EntryCount, _In_ DWORD MaximumEntryCount,
|
||||
_In_ ULONG_PTR RangeBase, _In_ ULONG_PTR RangeEnd);
|
||||
|
||||
typedef VOID(NTAPI* FnRtlGrowFunctionTable)(_Inout_ PVOID DynamicTable,
|
||||
_In_ DWORD NewEntryCount);
|
||||
|
||||
typedef VOID(NTAPI* FnRtlDeleteGrowableFunctionTable)(_In_ PVOID DynamicTable);
|
||||
using FnRtlAddGrowableFunctionTable = decltype(&RtlAddGrowableFunctionTable);
|
||||
using FnRtlGrowFunctionTable = decltype(&RtlGrowFunctionTable);
|
||||
using FnRtlDeleteGrowableFunctionTable =
|
||||
decltype(&RtlDeleteGrowableFunctionTable);
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
@ -112,13 +106,13 @@ class Win32X64CodeCache : public X64CodeCache {
|
|||
|
||||
private:
|
||||
UnwindReservation RequestUnwindReservation(uint8_t* entry_address) override;
|
||||
void PlaceCode(uint32_t guest_address, void* machine_code, size_t code_size,
|
||||
size_t stack_size, void* code_address,
|
||||
void PlaceCode(uint32_t guest_address, void* machine_code,
|
||||
const EmitFunctionInfo& func_info, void* code_address,
|
||||
UnwindReservation unwind_reservation) override;
|
||||
|
||||
void InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot, void* code_address,
|
||||
size_t code_size, size_t stack_size);
|
||||
const EmitFunctionInfo& func_info);
|
||||
|
||||
// Growable function table system handle.
|
||||
void* unwind_table_handle_ = nullptr;
|
||||
|
@ -163,12 +157,19 @@ bool Win32X64CodeCache::Initialize() {
|
|||
unwind_table_.resize(kMaximumFunctionCount);
|
||||
|
||||
// Check if this version of Windows supports growable function tables.
|
||||
add_growable_table_ = (FnRtlAddGrowableFunctionTable)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlAddGrowableFunctionTable");
|
||||
delete_growable_table_ = (FnRtlDeleteGrowableFunctionTable)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlDeleteGrowableFunctionTable");
|
||||
grow_table_ = (FnRtlGrowFunctionTable)GetProcAddress(
|
||||
GetModuleHandleW(L"ntdll.dll"), "RtlGrowFunctionTable");
|
||||
auto ntdll_handle = GetModuleHandleW(L"ntdll.dll");
|
||||
if (!ntdll_handle) {
|
||||
add_growable_table_ = nullptr;
|
||||
delete_growable_table_ = nullptr;
|
||||
grow_table_ = nullptr;
|
||||
} else {
|
||||
add_growable_table_ = (FnRtlAddGrowableFunctionTable)GetProcAddress(
|
||||
ntdll_handle, "RtlAddGrowableFunctionTable");
|
||||
delete_growable_table_ = (FnRtlDeleteGrowableFunctionTable)GetProcAddress(
|
||||
ntdll_handle, "RtlDeleteGrowableFunctionTable");
|
||||
grow_table_ = (FnRtlGrowFunctionTable)GetProcAddress(
|
||||
ntdll_handle, "RtlGrowFunctionTable");
|
||||
}
|
||||
supports_growable_table_ =
|
||||
add_growable_table_ && delete_growable_table_ && grow_table_;
|
||||
|
||||
|
@ -205,23 +206,21 @@ bool Win32X64CodeCache::Initialize() {
|
|||
|
||||
Win32X64CodeCache::UnwindReservation
|
||||
Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) {
|
||||
assert_false(unwind_table_count_ >= kMaximumFunctionCount);
|
||||
UnwindReservation unwind_reservation;
|
||||
unwind_reservation.data_size = xe::round_up(kUnwindInfoSize, 16);
|
||||
unwind_reservation.table_slot = ++unwind_table_count_;
|
||||
unwind_reservation.table_slot = unwind_table_count_++;
|
||||
unwind_reservation.entry_address = entry_address;
|
||||
assert_false(unwind_table_count_ >= kMaximumFunctionCount);
|
||||
|
||||
return unwind_reservation;
|
||||
}
|
||||
|
||||
void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
||||
size_t code_size, size_t stack_size,
|
||||
const EmitFunctionInfo& func_info,
|
||||
void* code_address,
|
||||
UnwindReservation unwind_reservation) {
|
||||
// Add unwind info.
|
||||
InitializeUnwindEntry(unwind_reservation.entry_address,
|
||||
unwind_reservation.table_slot, code_address, code_size,
|
||||
stack_size);
|
||||
unwind_reservation.table_slot, code_address, func_info);
|
||||
|
||||
if (supports_growable_table_) {
|
||||
// Notify that the unwind table has grown.
|
||||
|
@ -230,29 +229,33 @@ void Win32X64CodeCache::PlaceCode(uint32_t guest_address, void* machine_code,
|
|||
}
|
||||
|
||||
// This isn't needed on x64 (probably), but is convention.
|
||||
FlushInstructionCache(GetCurrentProcess(), code_address, code_size);
|
||||
FlushInstructionCache(GetCurrentProcess(), code_address,
|
||||
func_info.code_size.total);
|
||||
}
|
||||
|
||||
void Win32X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
||||
size_t unwind_table_slot,
|
||||
void* code_address,
|
||||
size_t code_size,
|
||||
size_t stack_size) {
|
||||
void Win32X64CodeCache::InitializeUnwindEntry(
|
||||
uint8_t* unwind_entry_address, size_t unwind_table_slot, void* code_address,
|
||||
const EmitFunctionInfo& func_info) {
|
||||
auto unwind_info = reinterpret_cast<UNWIND_INFO*>(unwind_entry_address);
|
||||
UNWIND_CODE* unwind_code = nullptr;
|
||||
|
||||
if (!stack_size) {
|
||||
// https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
assert_true(func_info.code_size.prolog < 256); // needs to fit into a uint8_t
|
||||
auto prolog_size = static_cast<uint8_t>(func_info.code_size.prolog);
|
||||
assert_true(func_info.prolog_stack_alloc_offset <
|
||||
256); // needs to fit into a uint8_t
|
||||
auto prolog_stack_alloc_offset =
|
||||
static_cast<uint8_t>(func_info.prolog_stack_alloc_offset);
|
||||
|
||||
if (!func_info.stack_size) {
|
||||
// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
unwind_info->CountOfCodes = 0;
|
||||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
} else if (stack_size <= 128) {
|
||||
uint8_t prolog_size = 4;
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
} else if (func_info.stack_size <= 128) {
|
||||
// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
|
@ -260,17 +263,15 @@ void Win32X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
|||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_code
|
||||
unwind_code = &unwind_info->UnwindCode[unwind_info->CountOfCodes++];
|
||||
unwind_code->CodeOffset =
|
||||
14; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code->CodeOffset = prolog_stack_alloc_offset;
|
||||
unwind_code->UnwindOp = UWOP_ALLOC_SMALL;
|
||||
unwind_code->OpInfo = stack_size / 8 - 1;
|
||||
unwind_code->OpInfo = (func_info.stack_size / 8) - 1;
|
||||
} else {
|
||||
// TODO(benvanik): take as parameters?
|
||||
uint8_t prolog_size = 7;
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
|
||||
// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
|
||||
unwind_info->Version = 1;
|
||||
unwind_info->Flags = 0;
|
||||
unwind_info->SizeOfProlog = prolog_size;
|
||||
|
@ -278,16 +279,15 @@ void Win32X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
|||
unwind_info->FrameRegister = 0;
|
||||
unwind_info->FrameOffset = 0;
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
|
||||
// https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_code
|
||||
unwind_code = &unwind_info->UnwindCode[unwind_info->CountOfCodes++];
|
||||
unwind_code->CodeOffset =
|
||||
7; // end of instruction + 1 == offset of next instruction
|
||||
unwind_code->CodeOffset = prolog_stack_alloc_offset;
|
||||
unwind_code->UnwindOp = UWOP_ALLOC_LARGE;
|
||||
unwind_code->OpInfo = 0; // One slot for size
|
||||
|
||||
assert_true((stack_size / 8) < 65536u);
|
||||
assert_true((func_info.stack_size / 8) < 65536u);
|
||||
unwind_code = &unwind_info->UnwindCode[unwind_info->CountOfCodes++];
|
||||
unwind_code->FrameOffset = (USHORT)(stack_size) / 8;
|
||||
unwind_code->FrameOffset = (USHORT)(func_info.stack_size) / 8;
|
||||
}
|
||||
|
||||
if (unwind_info->CountOfCodes % 1) {
|
||||
|
@ -300,13 +300,14 @@ void Win32X64CodeCache::InitializeUnwindEntry(uint8_t* unwind_entry_address,
|
|||
auto& fn_entry = unwind_table_[unwind_table_slot];
|
||||
fn_entry.BeginAddress =
|
||||
(DWORD)(reinterpret_cast<uint8_t*>(code_address) - generated_code_base_);
|
||||
fn_entry.EndAddress = (DWORD)(fn_entry.BeginAddress + code_size);
|
||||
fn_entry.EndAddress =
|
||||
(DWORD)(fn_entry.BeginAddress + func_info.code_size.total);
|
||||
fn_entry.UnwindData = (DWORD)(unwind_entry_address - generated_code_base_);
|
||||
}
|
||||
|
||||
void* Win32X64CodeCache::LookupUnwindInfo(uint64_t host_pc) {
|
||||
return std::bsearch(
|
||||
&host_pc, unwind_table_.data(), unwind_table_count_ + 1,
|
||||
&host_pc, unwind_table_.data(), unwind_table_count_,
|
||||
sizeof(RUNTIME_FUNCTION),
|
||||
[](const void* key_ptr, const void* element_ptr) {
|
||||
auto key =
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -102,14 +102,14 @@ bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder,
|
|||
source_map_arena_.Reset();
|
||||
|
||||
// Fill the generator with code.
|
||||
size_t stack_size = 0;
|
||||
if (!Emit(builder, &stack_size)) {
|
||||
EmitFunctionInfo func_info = {};
|
||||
if (!Emit(builder, func_info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the final code to the cache and relocate it.
|
||||
*out_code_size = getSize();
|
||||
*out_code_address = Emplace(stack_size, function);
|
||||
*out_code_address = Emplace(func_info, function);
|
||||
|
||||
// Stash source map.
|
||||
source_map_arena_.CloneContents(out_source_map);
|
||||
|
@ -117,18 +117,20 @@ bool X64Emitter::Emit(GuestFunction* function, HIRBuilder* builder,
|
|||
return true;
|
||||
}
|
||||
|
||||
void* X64Emitter::Emplace(size_t stack_size, GuestFunction* function) {
|
||||
void* X64Emitter::Emplace(const EmitFunctionInfo& func_info,
|
||||
GuestFunction* function) {
|
||||
// To avoid changing xbyak, we do a switcharoo here.
|
||||
// top_ points to the Xbyak buffer, and since we are in AutoGrow mode
|
||||
// it has pending relocations. We copy the top_ to our buffer, swap the
|
||||
// pointer, relocate, then return the original scratch pointer for use.
|
||||
uint8_t* old_address = top_;
|
||||
void* new_address;
|
||||
assert_true(func_info.code_size.total == size_);
|
||||
if (function) {
|
||||
new_address = code_cache_->PlaceGuestCode(function->address(), top_, size_,
|
||||
stack_size, function);
|
||||
new_address = code_cache_->PlaceGuestCode(function->address(), top_,
|
||||
func_info, function);
|
||||
} else {
|
||||
new_address = code_cache_->PlaceHostCode(0, top_, size_, stack_size);
|
||||
new_address = code_cache_->PlaceHostCode(0, top_, func_info);
|
||||
}
|
||||
top_ = reinterpret_cast<uint8_t*>(new_address);
|
||||
ready();
|
||||
|
@ -137,7 +139,7 @@ void* X64Emitter::Emplace(size_t stack_size, GuestFunction* function) {
|
|||
return new_address;
|
||||
}
|
||||
|
||||
bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
||||
bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
||||
Xbyak::Label epilog_label;
|
||||
epilog_label_ = &epilog_label;
|
||||
|
||||
|
@ -159,6 +161,16 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
|||
stack_offset -= StackLayout::GUEST_STACK_SIZE;
|
||||
stack_offset = xe::align(stack_offset, static_cast<size_t>(16));
|
||||
|
||||
struct _code_offsets {
|
||||
size_t prolog;
|
||||
size_t prolog_stack_alloc;
|
||||
size_t body;
|
||||
size_t epilog;
|
||||
size_t tail;
|
||||
} code_offsets = {};
|
||||
|
||||
code_offsets.prolog = getSize();
|
||||
|
||||
// Function prolog.
|
||||
// Must be 16b aligned.
|
||||
// Windows is very strict about the form of this and the epilog:
|
||||
|
@ -168,10 +180,14 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
|||
// Adding or changing anything here must be matched!
|
||||
const size_t stack_size = StackLayout::GUEST_STACK_SIZE + stack_offset;
|
||||
assert_true((stack_size + 8) % 16 == 0);
|
||||
*out_stack_size = stack_size;
|
||||
func_info.stack_size = stack_size;
|
||||
stack_size_ = stack_size;
|
||||
|
||||
sub(rsp, (uint32_t)stack_size);
|
||||
|
||||
code_offsets.prolog_stack_alloc = getSize();
|
||||
code_offsets.body = getSize();
|
||||
|
||||
mov(qword[rsp + StackLayout::GUEST_CTX_HOME], GetContextReg());
|
||||
mov(qword[rsp + StackLayout::GUEST_RET_ADDR], rcx);
|
||||
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], 0);
|
||||
|
@ -241,9 +257,14 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
|||
epilog_label_ = nullptr;
|
||||
EmitTraceUserCallReturn();
|
||||
mov(GetContextReg(), qword[rsp + StackLayout::GUEST_CTX_HOME]);
|
||||
|
||||
code_offsets.epilog = getSize();
|
||||
|
||||
add(rsp, (uint32_t)stack_size);
|
||||
ret();
|
||||
|
||||
code_offsets.tail = getSize();
|
||||
|
||||
if (cvars::emit_source_annotations) {
|
||||
nop();
|
||||
nop();
|
||||
|
@ -252,6 +273,15 @@ bool X64Emitter::Emit(HIRBuilder* builder, size_t* out_stack_size) {
|
|||
nop();
|
||||
}
|
||||
|
||||
assert_zero(code_offsets.prolog);
|
||||
func_info.code_size.total = getSize();
|
||||
func_info.code_size.prolog = code_offsets.body - code_offsets.prolog;
|
||||
func_info.code_size.body = code_offsets.epilog - code_offsets.body;
|
||||
func_info.code_size.epilog = code_offsets.tail - code_offsets.epilog;
|
||||
func_info.code_size.tail = getSize() - code_offsets.tail;
|
||||
func_info.prolog_stack_alloc_offset =
|
||||
code_offsets.prolog_stack_alloc - code_offsets.prolog;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -39,6 +39,8 @@ namespace x64 {
|
|||
class X64Backend;
|
||||
class X64CodeCache;
|
||||
|
||||
struct EmitFunctionInfo;
|
||||
|
||||
enum RegisterFlags {
|
||||
REG_DEST = (1 << 0),
|
||||
REG_ABCD = (1 << 1),
|
||||
|
@ -203,8 +205,6 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
|||
|
||||
void nop(size_t length = 1);
|
||||
|
||||
// TODO(benvanik): Label for epilog (don't use strings).
|
||||
|
||||
// Moves a 64bit immediate into memory.
|
||||
bool ConstantFitsIn32Reg(uint64_t v);
|
||||
void MovMem64(const Xbyak::RegExp& addr, uint64_t v);
|
||||
|
@ -224,8 +224,9 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
|||
size_t stack_size() const { return stack_size_; }
|
||||
|
||||
protected:
|
||||
void* Emplace(size_t stack_size, GuestFunction* function = nullptr);
|
||||
bool Emit(hir::HIRBuilder* builder, size_t* out_stack_size);
|
||||
void* Emplace(const EmitFunctionInfo& func_info,
|
||||
GuestFunction* function = nullptr);
|
||||
bool Emit(hir::HIRBuilder* builder, EmitFunctionInfo& func_info);
|
||||
void EmitGetCurrentThreadId();
|
||||
void EmitTraceUserCallReturn();
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -27,6 +27,7 @@ DEFINE_string(test_path, "src/xenia/cpu/ppc/testing/",
|
|||
"Directory scanned for test files.", "Other");
|
||||
DEFINE_string(test_bin_path, "src/xenia/cpu/ppc/testing/bin/",
|
||||
"Directory with binary outputs of the test files.", "Other");
|
||||
DEFINE_transient_string(test_name, "", "Specifies test name.", "General");
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
@ -472,5 +473,5 @@ int main(const std::vector<std::wstring>& args) {
|
|||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-cpu-ppc-test", L"xenia-cpu-ppc-test [test name]",
|
||||
xe::cpu::test::main);
|
||||
DEFINE_ENTRY_POINT(L"xenia-cpu-ppc-test", xe::cpu::test::main, "[test name]",
|
||||
"test_name");
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -179,6 +179,5 @@ int shader_compiler_main(const std::vector<std::wstring>& args) {
|
|||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-gpu-shader-compiler",
|
||||
L"xenia-gpu-shader-compiler shader.bin",
|
||||
xe::gpu::shader_compiler_main);
|
||||
DEFINE_ENTRY_POINT(L"xenia-gpu-shader-compiler", xe::gpu::shader_compiler_main,
|
||||
"shader.bin", "shader_input");
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2016 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -36,5 +36,5 @@ int trace_dump_main(const std::vector<std::wstring>& args) {
|
|||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-gpu-vulkan-trace-dump",
|
||||
L"xenia-gpu-vulkan-trace-dump some.trace",
|
||||
xe::gpu::vulkan::trace_dump_main);
|
||||
xe::gpu::vulkan::trace_dump_main, "some.trace",
|
||||
"target_trace_file");
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2016 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -72,5 +72,5 @@ int trace_viewer_main(const std::vector<std::wstring>& args) {
|
|||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-gpu-vulkan-trace-viewer",
|
||||
L"xenia-gpu-vulkan-trace-viewer some.trace",
|
||||
xe::gpu::vulkan::trace_viewer_main);
|
||||
xe::gpu::vulkan::trace_viewer_main, "some.trace",
|
||||
"target_trace_file");
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2017 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -215,5 +215,4 @@ void DrawInputStatus() {
|
|||
} // namespace hid
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-hid-demo", L"xenia-hid-demo",
|
||||
xe::hid::hid_demo_main);
|
||||
DEFINE_ENTRY_POINT(L"xenia-hid-demo", xe::hid::hid_demo_main, "");
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "xenia/apu/audio_system.h"
|
||||
#include "xenia/apu/xma_decoder.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/emulator.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
|
@ -184,9 +185,19 @@ DECLARE_XBOXKRNL_EXPORT2(XMASetInputBufferReadOffset, kAudio, kImplemented,
|
|||
|
||||
dword_result_t XMASetInputBuffer0(lpvoid_t context_ptr, lpvoid_t buffer,
|
||||
dword_t packet_count) {
|
||||
uint32_t buffer_physical_address =
|
||||
kernel_memory()->GetPhysicalAddress(buffer.guest_address());
|
||||
assert_true(buffer_physical_address != UINT32_MAX);
|
||||
if (buffer_physical_address == UINT32_MAX) {
|
||||
// Xenia-specific safety check.
|
||||
XELOGE("XMASetInputBuffer0: Invalid buffer virtual address %.8X",
|
||||
buffer.guest_address());
|
||||
return X_E_FALSE;
|
||||
}
|
||||
|
||||
XMA_CONTEXT_DATA context(context_ptr);
|
||||
|
||||
context.input_buffer_0_ptr = buffer.guest_address();
|
||||
context.input_buffer_0_ptr = buffer_physical_address;
|
||||
context.input_buffer_0_packet_count = packet_count;
|
||||
|
||||
context.Store(context_ptr);
|
||||
|
@ -215,9 +226,19 @@ DECLARE_XBOXKRNL_EXPORT2(XMASetInputBuffer0Valid, kAudio, kImplemented,
|
|||
|
||||
dword_result_t XMASetInputBuffer1(lpvoid_t context_ptr, lpvoid_t buffer,
|
||||
dword_t packet_count) {
|
||||
uint32_t buffer_physical_address =
|
||||
kernel_memory()->GetPhysicalAddress(buffer.guest_address());
|
||||
assert_true(buffer_physical_address != UINT32_MAX);
|
||||
if (buffer_physical_address == UINT32_MAX) {
|
||||
// Xenia-specific safety check.
|
||||
XELOGE("XMASetInputBuffer1: Invalid buffer virtual address %.8X",
|
||||
buffer.guest_address());
|
||||
return X_E_FALSE;
|
||||
}
|
||||
|
||||
XMA_CONTEXT_DATA context(context_ptr);
|
||||
|
||||
context.input_buffer_1_ptr = buffer.guest_address();
|
||||
context.input_buffer_1_ptr = buffer_physical_address;
|
||||
context.input_buffer_1_packet_count = packet_count;
|
||||
|
||||
context.Store(context_ptr);
|
||||
|
|
|
@ -330,9 +330,20 @@ dword_result_t MmAllocatePhysicalMemoryEx(dword_t flags, dword_t region_size,
|
|||
uint32_t allocation_type = kMemoryAllocationReserve | kMemoryAllocationCommit;
|
||||
uint32_t protect = FromXdkProtectFlags(protect_bits);
|
||||
bool top_down = true;
|
||||
auto heap = kernel_memory()->LookupHeapByType(true, page_size);
|
||||
auto heap = static_cast<PhysicalHeap*>(
|
||||
kernel_memory()->LookupHeapByType(true, page_size));
|
||||
// min_addr_range/max_addr_range are bounds in physical memory, not virtual.
|
||||
uint32_t heap_base = heap->heap_base();
|
||||
uint32_t heap_physical_address_offset = heap->GetPhysicalAddress(heap_base);
|
||||
uint32_t heap_min_addr =
|
||||
xe::sat_sub(min_addr_range.value(), heap_physical_address_offset);
|
||||
uint32_t heap_max_addr =
|
||||
xe::sat_sub(max_addr_range.value(), heap_physical_address_offset);
|
||||
uint32_t heap_size = heap->heap_size();
|
||||
heap_min_addr = heap_base + std::min(heap_min_addr, heap_size);
|
||||
heap_max_addr = heap_base + std::min(heap_max_addr, heap_size);
|
||||
uint32_t base_address;
|
||||
if (!heap->AllocRange(min_addr_range, max_addr_range, adjusted_size,
|
||||
if (!heap->AllocRange(heap_min_addr, heap_max_addr, adjusted_size,
|
||||
adjusted_alignment, allocation_type, protect, top_down,
|
||||
&base_address)) {
|
||||
// Failed - assume no memory available.
|
||||
|
@ -502,13 +513,11 @@ dword_result_t MmGetPhysicalAddress(dword_t base_address) {
|
|||
// _In_ PVOID BaseAddress
|
||||
// );
|
||||
// base_address = result of MmAllocatePhysicalMemory.
|
||||
assert_true(base_address >= 0xA0000000);
|
||||
|
||||
uint32_t physical_address = base_address & 0x1FFFFFFF;
|
||||
if (base_address >= 0xE0000000) {
|
||||
physical_address += 0x1000;
|
||||
uint32_t physical_address = kernel_memory()->GetPhysicalAddress(base_address);
|
||||
assert_true(physical_address != UINT32_MAX);
|
||||
if (physical_address == UINT32_MAX) {
|
||||
physical_address = 0;
|
||||
}
|
||||
|
||||
return physical_address;
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT1(MmGetPhysicalAddress, kMemory, kImplemented);
|
||||
|
|
|
@ -362,13 +362,26 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer
|
|||
xe::copy_and_swap_32_unaligned(
|
||||
&fetch, reinterpret_cast<uint32_t*>(fetch_ptr.host_address()), 6);
|
||||
|
||||
// Kernel virtual -> GPU physical.
|
||||
uint32_t frontbuffer_address = fetch.base_address << 12;
|
||||
assert_true(*frontbuffer_ptr == frontbuffer_address);
|
||||
frontbuffer_address =
|
||||
kernel_memory()->GetPhysicalAddress(frontbuffer_address);
|
||||
assert_true(frontbuffer_address != UINT32_MAX);
|
||||
if (frontbuffer_address == UINT32_MAX) {
|
||||
// Xenia-specific safety check.
|
||||
XELOGE("VdSwap: Invalid front buffer virtual address 0x%.8X",
|
||||
fetch.base_address << 12);
|
||||
return;
|
||||
}
|
||||
fetch.base_address = frontbuffer_address >> 12;
|
||||
|
||||
auto texture_format = gpu::TextureFormat(texture_format_ptr.value());
|
||||
auto color_space = *color_space_ptr;
|
||||
assert_true(texture_format == gpu::TextureFormat::k_8_8_8_8 ||
|
||||
texture_format ==
|
||||
gpu::TextureFormat::k_2_10_10_10_AS_16_16_16_16);
|
||||
assert_true(color_space == 0); // RGB(0)
|
||||
assert_true(*frontbuffer_ptr == fetch.base_address << 12);
|
||||
assert_true(*width == 1 + fetch.size_2d.width);
|
||||
assert_true(*height == 1 + fetch.size_2d.height);
|
||||
|
||||
|
@ -379,14 +392,6 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer
|
|||
// use this method.
|
||||
buffer_ptr.Zero(64 * 4);
|
||||
|
||||
// virtual -> physical
|
||||
// Doom 3: BFG Edition uses front buffers from the 0xE0000000 range with 4 KB
|
||||
// offset, so & 0x1FFFF is not enough for this.
|
||||
fetch.base_address = kernel_memory()
|
||||
->LookupHeap(fetch.base_address << 12)
|
||||
->GetPhysicalAddress(fetch.base_address << 12) >>
|
||||
12;
|
||||
|
||||
uint32_t offset = 0;
|
||||
auto dwords = buffer_ptr.as_array<uint32_t>();
|
||||
|
||||
|
@ -402,9 +407,7 @@ void VdSwap(lpvoid_t buffer_ptr, // ptr into primary ringbuffer
|
|||
|
||||
dwords[offset++] = xenos::MakePacketType3(xenos::PM4_XE_SWAP, 4);
|
||||
dwords[offset++] = 'SWAP';
|
||||
dwords[offset++] = kernel_memory()
|
||||
->LookupHeap(*frontbuffer_ptr)
|
||||
->GetPhysicalAddress(*frontbuffer_ptr);
|
||||
dwords[offset++] = frontbuffer_address;
|
||||
|
||||
dwords[offset++] = *width;
|
||||
dwords[offset++] = *height;
|
||||
|
|
|
@ -369,6 +369,14 @@ uint32_t Memory::HostToGuestVirtualThunk(const void* context,
|
|||
host_address);
|
||||
}
|
||||
|
||||
uint32_t Memory::GetPhysicalAddress(uint32_t address) const {
|
||||
const BaseHeap* heap = LookupHeap(address);
|
||||
if (!heap || !heap->IsGuestPhysicalHeap()) {
|
||||
return UINT32_MAX;
|
||||
}
|
||||
return static_cast<const PhysicalHeap*>(heap)->GetPhysicalAddress(address);
|
||||
}
|
||||
|
||||
void Memory::Zero(uint32_t address, uint32_t size) {
|
||||
std::memset(TranslateVirtual(address), 0, size);
|
||||
}
|
||||
|
@ -440,8 +448,7 @@ bool Memory::AccessViolationCallback(void* host_address, bool is_write) {
|
|||
}
|
||||
uint32_t virtual_address = HostToGuestVirtual(host_address);
|
||||
BaseHeap* heap = LookupHeap(virtual_address);
|
||||
if (heap == &heaps_.vA0000000 || heap == &heaps_.vC0000000 ||
|
||||
heap == &heaps_.vE0000000) {
|
||||
if (heap->IsGuestPhysicalHeap()) {
|
||||
// Will be rounded to physical page boundaries internally, so just pass 1 as
|
||||
// the length - guranteed not to cross page boundaries also.
|
||||
return static_cast<PhysicalHeap*>(heap)->TriggerWatches(virtual_address, 1,
|
||||
|
@ -461,8 +468,7 @@ bool Memory::TriggerWatches(uint32_t virtual_address, uint32_t length,
|
|||
bool is_write, bool unwatch_exact_range,
|
||||
bool unprotect) {
|
||||
BaseHeap* heap = LookupHeap(virtual_address);
|
||||
if (heap == &heaps_.vA0000000 || heap == &heaps_.vC0000000 ||
|
||||
heap == &heaps_.vE0000000) {
|
||||
if (heap->IsGuestPhysicalHeap()) {
|
||||
return static_cast<PhysicalHeap*>(heap)->TriggerWatches(
|
||||
virtual_address, length, is_write, unwatch_exact_range, unprotect);
|
||||
}
|
||||
|
@ -1274,16 +1280,6 @@ bool BaseHeap::QueryProtect(uint32_t address, uint32_t* out_protect) {
|
|||
return true;
|
||||
}
|
||||
|
||||
uint32_t BaseHeap::GetPhysicalAddress(uint32_t address) {
|
||||
// Only valid for memory in this range - will be bogus if the origin was
|
||||
// outside of it.
|
||||
uint32_t physical_address = address & 0x1FFFFFFF;
|
||||
if (address >= 0xE0000000) {
|
||||
physical_address += 0x1000;
|
||||
}
|
||||
return physical_address;
|
||||
}
|
||||
|
||||
VirtualHeap::VirtualHeap() = default;
|
||||
|
||||
VirtualHeap::~VirtualHeap() = default;
|
||||
|
@ -1724,4 +1720,14 @@ bool PhysicalHeap::TriggerWatches(uint32_t virtual_address, uint32_t length,
|
|||
return true;
|
||||
}
|
||||
|
||||
uint32_t PhysicalHeap::GetPhysicalAddress(uint32_t address) const {
|
||||
assert_true(address >= heap_base_);
|
||||
address -= heap_base_;
|
||||
assert_true(address <= heap_size_);
|
||||
if (heap_base_ >= 0xE0000000) {
|
||||
address += 0x1000;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
} // namespace xe
|
||||
|
|
|
@ -171,9 +171,8 @@ class BaseHeap {
|
|||
// 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);
|
||||
// Whether the heap is a guest virtual memory mapping of the physical memory.
|
||||
virtual bool IsGuestPhysicalHeap() const { return false; }
|
||||
|
||||
bool Save(ByteStream* stream);
|
||||
bool Restore(ByteStream* stream);
|
||||
|
@ -244,6 +243,9 @@ class PhysicalHeap : public BaseHeap {
|
|||
bool TriggerWatches(uint32_t virtual_address, uint32_t length, bool is_write,
|
||||
bool unwatch_exact_range, bool unprotect = true);
|
||||
|
||||
bool IsGuestPhysicalHeap() const override { return true; }
|
||||
uint32_t GetPhysicalAddress(uint32_t address) const;
|
||||
|
||||
protected:
|
||||
VirtualHeap* parent_heap_;
|
||||
|
||||
|
@ -317,6 +319,10 @@ class Memory {
|
|||
// Note that the contents at the returned host address are big-endian.
|
||||
uint32_t HostToGuestVirtual(const void* host_address) const;
|
||||
|
||||
// Returns the guest physical address for the guest virtual address, or
|
||||
// UINT32_MAX if it can't be obtained.
|
||||
uint32_t GetPhysicalAddress(uint32_t address) const;
|
||||
|
||||
// Zeros out a range of memory at the given guest address.
|
||||
void Zero(uint32_t address, uint32_t size);
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2016 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -26,5 +26,5 @@ std::unique_ptr<GraphicsProvider> CreateDemoGraphicsProvider(Window* window) {
|
|||
} // namespace ui
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-ui-window-vulkan-demo",
|
||||
L"xenia-ui-window-vulkan-demo", xe::ui::window_demo_main);
|
||||
DEFINE_ENTRY_POINT(L"xenia-ui-window-vulkan-demo", xe::ui::window_demo_main,
|
||||
"");
|
||||
|
|
|
@ -21,9 +21,15 @@
|
|||
namespace xe {
|
||||
namespace vfs {
|
||||
|
||||
DEFINE_transient_string(source, "", "Specifies the file to dump from.",
|
||||
"General");
|
||||
|
||||
DEFINE_transient_string(dump_path, "",
|
||||
"Specifies the directory to dump files to.", "General");
|
||||
|
||||
int vfs_dump_main(const std::vector<std::wstring>& args) {
|
||||
if (args.size() <= 2) {
|
||||
XELOGE("Usage: %s [source] [dump_path]", args[0].c_str());
|
||||
XELOGE("Usage: %S [source] [dump_path]", args[0].c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -107,5 +113,5 @@ int vfs_dump_main(const std::vector<std::wstring>& args) {
|
|||
} // namespace vfs
|
||||
} // namespace xe
|
||||
|
||||
DEFINE_ENTRY_POINT(L"xenia-vfs-dump", L"xenia-vfs-dump",
|
||||
xe::vfs::vfs_dump_main);
|
||||
DEFINE_ENTRY_POINT(L"xenia-vfs-dump", xe::vfs::vfs_dump_main,
|
||||
"[source] [dump_path]", "source", "dump_path");
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -23,7 +23,7 @@ bool has_console_attached() { return true; }
|
|||
|
||||
// Used in console mode apps; automatically picked based on subsystem.
|
||||
int Main(int argc, char* argv[]) {
|
||||
cvar::ParseLaunchArguments(argc, argv);
|
||||
cvar::ParseLaunchArguments(argc, argv, "", std::vector<std::string>());
|
||||
|
||||
// Run Catch.
|
||||
int result = Catch::Session().run(argc, argv);
|
||||
|
|
52
xb.bat
52
xb.bat
|
@ -1,7 +1,7 @@
|
|||
@ECHO OFF
|
||||
REM Copyright 2015 Ben Vanik. All Rights Reserved.
|
||||
|
||||
SET DIR=%~dp0
|
||||
SET "DIR=%~dp0"
|
||||
|
||||
REM ============================================================================
|
||||
REM Environment Validation
|
||||
|
@ -20,7 +20,7 @@ REM ============================================================================
|
|||
REM Trampoline into xenia-build
|
||||
REM ============================================================================
|
||||
|
||||
"%PYTHON_EXE%" xenia-build %*
|
||||
"%PYTHON_EXE%" "%DIR%\xenia-build" %*
|
||||
EXIT /b %ERRORLEVEL%
|
||||
|
||||
|
||||
|
@ -33,19 +33,23 @@ SETLOCAL ENABLEDELAYEDEXPANSION
|
|||
|
||||
SET FOUND_PATH=""
|
||||
|
||||
SET CANDIDATE_PATHS[0]=C:\python37\python.exe
|
||||
SET CANDIDATE_PATHS[1]=C:\python36\python.exe
|
||||
SET CANDIDATE_PATHS[2]=C:\python35\python.exe
|
||||
SET CANDIDATE_PATHS[3]=C:\python34\python.exe
|
||||
SET "CANDIDATE_PATHS[0]=C:\python37\python.exe"
|
||||
SET "CANDIDATE_PATHS[1]=C:\python36\python.exe"
|
||||
SET "CANDIDATE_PATHS[2]=C:\python35\python.exe"
|
||||
SET "CANDIDATE_PATHS[3]=C:\python34\python.exe"
|
||||
SET OUTPUT_INDEX=4
|
||||
|
||||
FOR /F "usebackq" %%l IN (`2^>NUL where python3`) DO (
|
||||
SET CANDIDATE_PATHS[!OUTPUT_INDEX!]=%%l
|
||||
SET /A OUTPUT_INDEX+=1
|
||||
FOR /F "usebackq delims=" %%L IN (`2^>NUL where python3`) DO (
|
||||
IF %%~zL NEQ 0 (
|
||||
SET "CANDIDATE_PATHS[!OUTPUT_INDEX!]=%%L"
|
||||
SET /A OUTPUT_INDEX+=1
|
||||
)
|
||||
)
|
||||
FOR /F "usebackq" %%l IN (`2^>NUL where python`) DO (
|
||||
SET CANDIDATE_PATHS[!OUTPUT_INDEX!]=%%l
|
||||
SET /A OUTPUT_INDEX+=1
|
||||
FOR /F "usebackq delims=" %%L IN (`2^>NUL where python`) DO (
|
||||
IF %%~zL NEQ 0 (
|
||||
SET "CANDIDATE_PATHS[!OUTPUT_INDEX!]=%%L"
|
||||
SET /A OUTPUT_INDEX+=1
|
||||
)
|
||||
)
|
||||
|
||||
SET CANDIDATE_INDEX=0
|
||||
|
@ -54,40 +58,30 @@ IF NOT DEFINED CANDIDATE_PATHS[%CANDIDATE_INDEX%] (
|
|||
GOTO :found_python
|
||||
)
|
||||
CALL SET CANDIDATE_PATH=%%CANDIDATE_PATHS[%CANDIDATE_INDEX%]%%
|
||||
IF NOT EXIST %CANDIDATE_PATH% (
|
||||
IF NOT EXIST "%CANDIDATE_PATH%" (
|
||||
SET /A CANDIDATE_INDEX+=1
|
||||
GOTO :check_candidate_loop
|
||||
)
|
||||
CALL :get_size %CANDIDATE_PATH%
|
||||
IF %_SIZE% NEQ 0 (
|
||||
SET FOUND_PATH=%CANDIDATE_PATH%
|
||||
GOTO :found_python
|
||||
)
|
||||
SET /A CANDIDATE_INDEX+=1
|
||||
GOTO :check_candidate_loop
|
||||
SET "FOUND_PATH=%CANDIDATE_PATH%"
|
||||
|
||||
:found_python
|
||||
IF %FOUND_PATH%=="" (
|
||||
IF "%FOUND_PATH%"=="" (
|
||||
ECHO ERROR: no Python executable found on PATH.
|
||||
ECHO Make sure you can run 'python' or 'python3' in a Command Prompt.
|
||||
ENDLOCAL & SET _RESULT=1
|
||||
GOTO :eof
|
||||
)
|
||||
|
||||
CMD /C %FOUND_PATH% -c "import sys; sys.exit(1 if not sys.version_info[:2] >= (3, 4) else 0)"
|
||||
CMD /C ""%FOUND_PATH%" -c "import sys; sys.exit(1 if not sys.version_info[:2] ^>= (3, 4) else 0)"
|
||||
IF %ERRORLEVEL% NEQ 0 (
|
||||
ECHO ERROR: Python version mismatch - not 3.4+
|
||||
ECHO Found Python executable was '%FOUND_PATH%'.
|
||||
ECHO ERROR: Python version mismatch, not at least 3.4.
|
||||
ECHO Found Python executable was "%FOUND_PATH%".
|
||||
ENDLOCAL & SET _RESULT=1
|
||||
GOTO :eof
|
||||
)
|
||||
|
||||
ENDLOCAL & (
|
||||
SET _RESULT=0
|
||||
SET PYTHON_EXE=%FOUND_PATH%
|
||||
SET "PYTHON_EXE=%FOUND_PATH%"
|
||||
)
|
||||
GOTO :eof
|
||||
|
||||
:get_size
|
||||
SET _SIZE=%~z1
|
||||
goto :eof
|
||||
|
|
Loading…
Reference in New Issue