Compare commits

..

3 Commits

Author SHA1 Message Date
kdlz4161
fe038816ef Update xci.h 2018-04-01 14:13:23 -03:00
kdlz4161
f28de1c3c4 Update xci.h 2018-04-01 14:12:41 -03:00
kdlz4161
1ab81d3bd8 Create xci.h
Add support of xci files
2018-04-01 14:09:45 -03:00
50 changed files with 303 additions and 788 deletions

View File

@@ -278,7 +278,7 @@ endif()
if (ENABLE_QT)
if (YUZU_USE_BUNDLED_QT)
if (MSVC14 AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.10.0-msvc2015_64)
set(QT_VER qt-5.7-msvc2015_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()

View File

@@ -17,7 +17,6 @@ endif()
# libfmt
add_subdirectory(fmt)
add_library(fmt::fmt ALIAS fmt)
# getopt
if (MSVC)

2
externals/fmt vendored

View File

@@ -89,7 +89,7 @@ endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
target_link_libraries(common PUBLIC Boost::boost microprofile)
if (ARCHITECTURE_x86_64)
target_link_libraries(common PRIVATE xbyak)
endif()

View File

@@ -11,7 +11,6 @@
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
namespace Log {
@@ -107,20 +106,25 @@ const char* GetLevelName(Level log_level) {
}
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string message) {
const char* function, const char* format, va_list args) {
using std::chrono::duration_cast;
using std::chrono::steady_clock;
static steady_clock::time_point time_origin = steady_clock::now();
std::array<char, 4 * 1024> formatting_buffer;
Entry entry;
entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
entry.log_class = log_class;
entry.log_level = log_level;
entry.filename = Common::TrimSourcePath(filename);
entry.line_num = line_nr;
entry.function = function;
entry.message = std::move(message);
snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function,
line_nr);
entry.location = std::string(formatting_buffer.data());
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
entry.message = std::string(formatting_buffer.data());
return entry;
}
@@ -131,27 +135,15 @@ void SetFilter(Filter* new_filter) {
filter = new_filter;
}
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, const char* format, ...) {
if (filter && !filter->CheckMessage(log_class, log_level))
if (filter != nullptr && !filter->CheckMessage(log_class, log_level))
return;
std::array<char, 4 * 1024> formatting_buffer;
va_list args;
va_start(args, format);
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
Entry entry = CreateEntry(log_class, log_level, filename, line_nr, function, format, args);
va_end(args);
Entry entry = CreateEntry(log_class, log_level, filename, line_num, function,
std::string(formatting_buffer.data()));
PrintColoredMessage(entry);
}
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, const fmt::format_args& args) {
if (filter && !filter->CheckMessage(log_class, log_level))
return;
Entry entry =
CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args));
PrintColoredMessage(entry);
}

View File

@@ -22,16 +22,13 @@ struct Entry {
std::chrono::microseconds timestamp;
Class log_class;
Level log_level;
std::string filename;
unsigned int line_num;
std::string function;
std::string location;
std::string message;
Entry() = default;
Entry(Entry&& o) = default;
Entry& operator=(Entry&& o) = default;
Entry& operator=(const Entry& o) = default;
};
/**
@@ -47,7 +44,7 @@ const char* GetLevelName(Level log_level);
/// Creates a log entry by formatting the given source location, and message.
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, std::string message);
const char* function, const char* format, va_list args);
void SetFilter(Filter* filter);
} // namespace Log

View File

@@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const std::string::const_iterator end) {
auto level_separator = std::find(begin, end, ':');
if (level_separator == end) {
NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
std::string(begin, end).c_str());
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
std::string(begin, end).c_str());
return false;
}
const Level level = GetLevelByName(level_separator + 1, end);
if (level == Level::Count) {
NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
return false;
}
@@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const Class log_class = GetClassByName(begin, level_separator);
if (log_class == Class::Count) {
NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
return false;
}

View File

@@ -19,7 +19,7 @@ namespace Log {
class Filter {
public:
/// Initializes the filter with all classes having `default_level` as the minimum level.
Filter(Level default_level = Level::Info);
Filter(Level default_level);
/// Resets the filter so that all classes have `level` as the minimum displayed level.
void ResetAll(Level level);

View File

@@ -4,7 +4,6 @@
#pragma once
#include <fmt/format.h>
#include "common/common_types.h"
namespace Log {
@@ -92,7 +91,7 @@ enum class Class : ClassType {
};
/// Logs a message to the global logger.
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function,
#ifdef _MSC_VER
_Printf_format_string_
@@ -104,16 +103,6 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
#endif
;
/// Logs a message to the global logger, using fmt
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, const fmt::format_args& args);
template <typename... Args>
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, const Args&... args) {
LogMessage(log_class, log_level, filename, line_num, function, format, fmt::make_args(args...));
}
} // namespace Log
#define LOG_GENERIC(log_class, log_level, ...) \
@@ -136,28 +125,3 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
#define LOG_CRITICAL(log_class, ...) \
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)
// Define the fmt lib macros
#ifdef _DEBUG
#define NGLOG_TRACE(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#else
#define NGLOG_TRACE(log_class, fmt, ...) (void(0))
#endif
#define NGLOG_DEBUG(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_INFO(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_WARNING(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_ERROR(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_CRITICAL(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \
__func__, __VA_ARGS__)

View File

@@ -18,29 +18,50 @@
namespace Log {
std::string FormatLogMessage(const Entry& entry) {
// TODO(bunnei): This should be moved to a generic path manipulation library
const char* TrimSourcePath(const char* path, const char* root) {
const char* p = path;
while (*p != '\0') {
const char* next_slash = p;
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
++next_slash;
}
bool is_src = Common::ComparePartialString(p, next_slash, root);
p = next_slash;
if (*p != '\0') {
++p;
}
if (is_src) {
path = p;
}
}
return path;
}
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
const char* class_name = GetLogClassName(entry.log_class);
const char* level_name = GetLevelName(entry.log_level);
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional,
class_name, level_name, entry.filename, entry.function, entry.line_num,
entry.message);
snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s", time_seconds, time_fractional,
class_name, level_name, TrimSourcePath(entry.location.c_str()), entry.message.c_str());
}
void PrintMessage(const Entry& entry) {
auto str = FormatLogMessage(entry) + '\n';
fputs(str.c_str(), stderr);
std::array<char, 4 * 1024> format_buffer;
FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
fputs(format_buffer.data(), stderr);
fputc('\n', stderr);
}
void PrintColoredMessage(const Entry& entry) {
#ifdef _WIN32
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
if (console_handle == INVALID_HANDLE_VALUE) {
return;
}
static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
GetConsoleScreenBufferInfo(console_handle, &original_info);

View File

@@ -10,8 +10,20 @@ namespace Log {
struct Entry;
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
* leaving only the path relative to the sources root.
*
* @param path The input file path as a null-terminated string
* @param root The name of the root source directory as a null-terminated string. Path up to and
* including the last occurrence of this name will be stripped
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
*/
const char* TrimSourcePath(const char* path, const char* root = "src");
/// Formats a log entry into the provided text buffer.
std::string FormatLogMessage(const Entry& entry);
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
/// Formats and prints a log entry to stderr.
void PrintMessage(const Entry& entry);
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.

View File

@@ -462,27 +462,4 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_l
return std::string(buffer, len);
}
const char* TrimSourcePath(const char* path, const char* root) {
const char* p = path;
while (*p != '\0') {
const char* next_slash = p;
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
++next_slash;
}
bool is_src = Common::ComparePartialString(p, next_slash, root);
p = next_slash;
if (*p != '\0') {
++p;
}
if (is_src) {
path = p;
}
}
return path;
}
} // namespace Common

View File

@@ -134,17 +134,4 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
* NUL-terminated then the string ends at max_len characters.
*/
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len);
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
* leaving only the path relative to the sources root.
*
* @param path The input file path as a null-terminated string
* @param root The name of the root source directory as a null-terminated string. Path up to and
* including the last occurrence of this name will be stripped
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
*/
const char* TrimSourcePath(const char* path, const char* root = "src");
} // namespace Common

View File

@@ -103,19 +103,7 @@ inline __attribute__((always_inline)) u64 swap64(u64 _data) {
return __builtin_bswap64(_data);
}
#elif defined(__Bitrig__) || defined(__OpenBSD__)
// redefine swap16, swap32, swap64 as inline functions
#undef swap16
#undef swap32
#undef swap64
inline u16 swap16(u16 _data) {
return __swap16(_data);
}
inline u32 swap32(u32 _data) {
return __swap32(_data);
}
inline u64 swap64(u64 _data) {
return __swap64(_data);
}
// swap16, swap32, swap64 are left as is
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
inline u16 swap16(u16 _data) {
return bswap16(_data);

View File

@@ -15,7 +15,7 @@ namespace Telemetry {
/// Field type, used for grouping fields together in the final submitted telemetry log
enum class FieldType : u8 {
None = 0, ///< No specified field group
App, ///< yuzu application fields (e.g. version, branch, etc.)
App, ///< Citra application fields (e.g. version, branch, etc.)
Session, ///< Emulated session fields (e.g. title ID, log, etc.)
Performance, ///< Emulated performance (e.g. fps, emulated CPU speed, etc.)
UserFeedback, ///< User submitted feedback (e.g. star rating, user notes, etc.)

View File

@@ -130,8 +130,6 @@ add_library(core STATIC
hle/service/friend/friend.h
hle/service/friend/friend_a.cpp
hle/service/friend/friend_a.h
hle/service/friend/friend_u.cpp
hle/service/friend/friend_u.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/lm/lm.cpp

View File

@@ -174,9 +174,8 @@ u64 Disk_Storage::GetSize() const {
}
bool Disk_Storage::SetSize(const u64 size) const {
file->Resize(size);
file->Flush();
return true;
LOG_WARNING(Service_FS, "(STUBBED) called");
return false;
}
Disk_Directory::Disk_Directory(const std::string& path) : directory() {

View File

@@ -268,11 +268,8 @@ std::vector<u8> HLERequestContext::ReadBuffer() const {
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
const size_t buffer_size{GetWriteBufferSize()};
if (size > buffer_size) {
LOG_CRITICAL(Core, "size (%016zx) is greater than buffer_size (%016zx)", size, buffer_size);
size = buffer_size; // TODO(bunnei): This needs to be HW tested
}
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %lx is too big", size);
if (is_buffer_b) {
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);

View File

@@ -121,9 +121,8 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
vm_manager
.MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Mapped)
.MapMemoryBlock(Memory::STACK_VADDR, std::make_shared<std::vector<u8>>(stack_size, 0), 0,
stack_size, MemoryState::Mapped)
.Unwrap();
misc_memory_used += stack_size;
memory_region->used += stack_size;

View File

@@ -120,6 +120,18 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return ERR_WRONG_PERMISSION;
}
// TODO(Subv): The same process that created a SharedMemory object
// can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
if (address != 0) {
// TODO(shinyquagsire23): Check for virtual/mappable memory here too?
if (address >= Memory::HEAP_VADDR && address < Memory::HEAP_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, invalid address",
GetObjectId(), address, name.c_str());
return ERR_INVALID_ADDRESS;
}
}
VAddr target_address = address;
if (base_address == 0 && target_address == 0) {

View File

@@ -371,18 +371,6 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
return RESULT_SUCCESS;
}
/// Sets the thread activity
static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x%08X, unknown=0x%08X", handle, unknown);
return RESULT_SUCCESS;
}
/// Gets the thread context
static ResultCode GetThreadContext(Handle handle, VAddr addr) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x%08X, addr=0x%" PRIx64, handle, addr);
return RESULT_SUCCESS;
}
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle);
@@ -865,8 +853,8 @@ static const FunctionDef SVC_Table[] = {
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, nullptr, "GetResourceLimitLimitValue"},
{0x31, nullptr, "GetResourceLimitCurrentValue"},
{0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
{0x32, nullptr, "SetThreadActivity"},
{0x33, nullptr, "GetThreadContext"},
{0x34, nullptr, "Unknown"},
{0x35, nullptr, "Unknown"},
{0x36, nullptr, "Unknown"},

View File

@@ -70,11 +70,6 @@ void SvcWrap() {
FuncReturn(retval);
}
template <ResultCode func(u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), PARAM(1)).raw);
}
template <ResultCode func(u32, u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw);

View File

@@ -342,7 +342,7 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_AREA_VADDR_END, owner_process);
Memory::STACK_VADDR_END, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();

View File

@@ -25,7 +25,7 @@ class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) {
static const FunctionInfo functions[] = {
{0x0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
{0x0, nullptr, "GetAudioOutState"},
{0x1, &IAudioOut::StartAudioOut, "StartAudioOut"},
{0x2, &IAudioOut::StopAudioOut, "StopAudioOut"},
{0x3, &IAudioOut::AppendAudioOutBuffer_1, "AppendAudioOutBuffer_1"},
@@ -57,13 +57,6 @@ public:
}
private:
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(audio_out_state));
}
void StartAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");

View File

@@ -158,7 +158,7 @@ public:
{0x0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
{0x1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
{0x2, nullptr, "GetAudioDeviceOutputVolume"},
{0x3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
{0x3, nullptr, "GetActiveAudioDeviceName"},
{0x4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
{0x5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
{0x6, nullptr, "ListAudioDeviceNameAuto"},
@@ -199,18 +199,6 @@ private:
rb.Push(RESULT_SUCCESS);
}
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const std::string audio_interface = "AudioDevice";
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
}
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");

View File

@@ -72,8 +72,8 @@ public:
explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend)
: ServiceFramework("IFile"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"},
{3, &IFile::SetSize, "SetSize"}, {4, &IFile::GetSize, "GetSize"},
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"},
{3, nullptr, "SetSize"}, {4, nullptr, "GetSize"},
};
RegisterHandlers(functions);
}
@@ -150,25 +150,6 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
backend->SetSize(size);
LOG_DEBUG(Service_FS, "called, size=%" PRIu64, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetSize(Kernel::HLERequestContext& ctx) {
const u64 size = backend->GetSize();
LOG_DEBUG(Service_FS, "called, size=%" PRIu64, size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(size);
}
};
class IDirectory final : public ServiceFramework<IDirectory> {

View File

@@ -6,7 +6,6 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/friend/friend_a.h"
#include "core/hle/service/friend/friend_u.h"
namespace Service {
namespace Friend {
@@ -23,7 +22,6 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<Friend_A>(module)->InstallAsService(service_manager);
std::make_shared<Friend_U>(module)->InstallAsService(service_manager);
}
} // namespace Friend

View File

@@ -1,19 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/friend/friend_u.h"
namespace Service {
namespace Friend {
Friend_U::Friend_U(std::shared_ptr<Module> module)
: Module::Interface(std::move(module), "friend:u") {
static const FunctionInfo functions[] = {
{0, &Friend_U::Unknown, "Unknown"},
};
RegisterHandlers(functions);
}
} // namespace Friend
} // namespace Service

View File

@@ -1,18 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/friend/friend.h"
namespace Service {
namespace Friend {
class Friend_U final : public Module::Interface {
public:
explicit Friend_U(std::shared_ptr<Module> module);
};
} // namespace Friend
} // namespace Service

View File

@@ -65,14 +65,13 @@ private:
}
void UpdatePadCallback(u64 userdata, int cycles_late) {
SharedMemory mem{};
std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
SharedMemory* mem = reinterpret_cast<SharedMemory*>(shared_mem->GetPointer());
if (is_device_reload_pending.exchange(false))
LoadInputDevices();
// Set up controllers as neon red+blue Joy-Con attached to console
ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
ControllerHeader& controller_header = mem->controllers[Controller_Handheld].header;
controller_header.type = ControllerType_Handheld | ControllerType_JoyconPair;
controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
controller_header.right_color_body = JOYCON_BODY_NEON_RED;
@@ -80,8 +79,8 @@ private:
controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
for (int index = 0; index < HID_NUM_LAYOUTS; index++) {
ControllerLayout& layout = mem.controllers[Controller_Handheld].layouts[index];
for (int layoutIdx = 0; layoutIdx < HID_NUM_LAYOUTS; layoutIdx++) {
ControllerLayout& layout = mem->controllers[Controller_Handheld].layouts[layoutIdx];
layout.header.num_entries = HID_NUM_ENTRIES;
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
@@ -137,25 +136,10 @@ private:
// layouts)
}
// TODO(bunnei): Properly implement the touch screen, the below will just write empty data
TouchScreen& touchscreen = mem.touchscreen;
const u64 last_entry = touchscreen.header.latest_entry;
const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
const u64 timestamp = CoreTiming::GetTicks();
const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1;
touchscreen.header.timestamp_ticks = timestamp;
touchscreen.header.num_entries = touchscreen.entries.size();
touchscreen.header.latest_entry = curr_entry;
touchscreen.header.max_entry_index = touchscreen.entries.size();
touchscreen.header.timestamp = timestamp;
touchscreen.entries[curr_entry].header.timestamp = sample_counter;
touchscreen.entries[curr_entry].header.num_touches = 0;
// TODO(shinyquagsire23): Update touch info
// TODO(shinyquagsire23): Signal events
std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory));
// Reschedule recurrent event
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
}
@@ -201,7 +185,6 @@ public:
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
{103, &Hid::ActivateNpad, "ActivateNpad"},
{106, &Hid::AcquireNpadStyleSetUpdateEventHandle,
@@ -282,13 +265,6 @@ private:
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -70,8 +70,9 @@ private:
}
void GetResult(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");

View File

@@ -112,15 +112,15 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
auto cmd_buf = ctx.CommandBuffer();
std::string function_name = info == nullptr ? fmt::format("{}", ctx.GetCommand()) : info->name;
fmt::memory_buffer buf;
fmt::format_to(buf, "function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name,
cmd_buf[0]);
fmt::MemoryWriter w;
w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name,
cmd_buf[0]);
for (int i = 1; i <= 8; ++i) {
fmt::format_to(buf, ", [{}]={:#x}", i, cmd_buf[i]);
w.write(", [{}]={:#x}", i, cmd_buf[i]);
}
buf.push_back('}');
w << '}';
LOG_ERROR(Service, "unknown / unimplemented %s", fmt::to_string(buf).c_str());
LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str());
UNIMPLEMENTED();
}

View File

@@ -17,7 +17,6 @@
#include "core/hle/service/vi/vi_m.h"
#include "core/hle/service/vi/vi_s.h"
#include "core/hle/service/vi/vi_u.h"
#include "core/settings.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -712,23 +711,6 @@ private:
rb.Push(RESULT_SUCCESS);
}
void GetDisplayResolution(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
rb.Push(static_cast<u32>(DisplayResolution::DockedWidth));
rb.Push(static_cast<u32>(DisplayResolution::DockedHeight));
} else {
rb.Push(static_cast<u32>(DisplayResolution::UndockedWidth));
rb.Push(static_cast<u32>(DisplayResolution::UndockedHeight));
}
}
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
@@ -826,7 +808,6 @@ IApplicationDisplayService::IApplicationDisplayService(
{1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
{1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
{1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
{1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
{2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
{2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
{2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},

View File

@@ -14,13 +14,6 @@ struct EventType;
namespace Service {
namespace VI {
enum class DisplayResolution : u32 {
DockedWidth = 1920,
DockedHeight = 1080,
UndockedWidth = 1280,
UndockedHeight = 720,
};
class Module final {
public:
class Interface : public ServiceFramework<Interface> {

View File

@@ -76,7 +76,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& fil
} else if (Common::ToLower(virtual_name) == "sdk") {
is_sdk_found = true;
} else {
// Continue searching
// Contrinue searching
return true;
}

View File

@@ -414,7 +414,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE);
process->Run(codeset->entrypoint, 48, Memory::STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -137,7 +137,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(base_addr, 48, Memory::DEFAULT_STACK_SIZE);
process->Run(base_addr, 48, Memory::STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -165,7 +165,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::DEFAULT_STACK_SIZE);
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

40
src/core/loader/xci.h Normal file
View File

@@ -0,0 +1,40 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace Loader {
/// Loads an XCI file
class AppLoader_XCI final : public AppLoader, Linker {
public:
AppLoader_XCI(FileUtil::IOFile&& file, std::string filepath);
/**
* Returns the type of the file
* @param file FileUtil::IOFile open file
* @param filepath Path of the file that we are opening.
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
FileType GetFileType() override {
return IdentifyType(file, filepath);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
private:
bool LoadXci(const std::string& path, VAddr load_base);
std::string filepath;
};
} // namespace Loader

View File

@@ -162,13 +162,12 @@ enum : VAddr {
TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END,
TLS_ENTRY_SIZE = 0x200,
TLS_AREA_SIZE = 0x10000000,
TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
TLS_ADREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
/// Application stack
STACK_AREA_VADDR = TLS_AREA_VADDR_END,
STACK_AREA_SIZE = 0x10000000,
STACK_AREA_VADDR_END = STACK_AREA_VADDR + STACK_AREA_SIZE,
DEFAULT_STACK_SIZE = 0x100000,
STACK_VADDR = TLS_ADREA_VADDR_END,
STACK_SIZE = 0x10000,
STACK_VADDR_END = STACK_VADDR + STACK_SIZE,
/// Application heap
/// Size is confirmed to be a static value on fw 3.0.0

View File

@@ -87,8 +87,8 @@ TelemetrySession::TelemetrySession() {
#ifdef ENABLE_WEB_SERVICE
if (Settings::values.enable_telemetry) {
backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.telemetry_endpoint_url, Settings::values.yuzu_username,
Settings::values.yuzu_token);
Settings::values.telemetry_endpoint_url, Settings::values.citra_username,
Settings::values.citra_token);
} else {
backend = std::make_unique<Telemetry::NullVisitor>();
}

View File

@@ -50,8 +50,8 @@ u64 RegenerateTelemetryId();
/**
* Verifies the username and token.
* @param username yuzu username to use for authentication.
* @param token yuzu token to use for authentication.
* @param username Citra username to use for authentication.
* @param token Citra token to use for authentication.
* @param func A function that gets exectued when the verification is finished
* @returns Future with bool indicating whether the verification succeeded
*/

View File

@@ -11,8 +11,6 @@ add_library(video_core STATIC
engines/maxwell_compute.h
gpu.cpp
gpu.h
macro_interpreter.cpp
macro_interpreter.h
memory_manager.cpp
memory_manager.h
rasterizer_interface.h

View File

@@ -19,21 +19,35 @@ namespace Engines {
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
Maxwell3D::Maxwell3D(MemoryManager& memory_manager)
: memory_manager(memory_manager), macro_interpreter(*this) {}
const std::unordered_map<u32, Maxwell3D::MethodInfo> Maxwell3D::method_handlers = {
{0xE1A, {"BindTextureInfoBuffer", 1, &Maxwell3D::BindTextureInfoBuffer}},
{0xE24, {"SetShader", 5, &Maxwell3D::SetShader}},
{0xE2A, {"BindStorageBuffer", 1, &Maxwell3D::BindStorageBuffer}},
};
Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
void Maxwell3D::SubmitMacroCode(u32 entry, std::vector<u32> code) {
uploaded_macros[entry * 2 + MacroRegistersStart] = std::move(code);
}
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
auto macro_code = uploaded_macros.find(method);
// The requested macro must have been uploaded already.
ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method);
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
// TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47
// Reset the current macro and execute it.
// The requested macro must have been uploaded already.
ASSERT_MSG(uploaded_macros.find(method) != uploaded_macros.end(), "Macro %08X was not uploaded",
method);
auto itr = method_handlers.find(method);
ASSERT_MSG(itr != method_handlers.end(), "Unhandled method call %08X", method);
ASSERT(itr->second.arguments == parameters.size());
(this->*itr->second.handler)(parameters);
// Reset the current macro and its parameters.
executing_macro = 0;
macro_interpreter.Execute(macro_code->second, std::move(parameters));
macro_params.clear();
}
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
@@ -63,7 +77,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
// Call the macro when there are no more parameters in the command buffer
if (remaining_params == 0) {
CallMacroMethod(executing_macro, std::move(macro_params));
CallMacroMethod(executing_macro, macro_params);
}
return;
}
@@ -179,6 +193,84 @@ void Maxwell3D::DrawArrays() {
VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(false /*is_indexed*/);
}
void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Shader stage, usually 4 for FragmentShader
*/
u32 stage = parameters[0];
// Perform the same operations as the real macro code.
GPUVAddr address = static_cast<GPUVAddr>(regs.tex_info_buffers.address[stage]) << 8;
u32 size = regs.tex_info_buffers.size[stage];
regs.const_buffer.cb_size = size;
regs.const_buffer.cb_address_high = address >> 32;
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
}
void Maxwell3D::SetShader(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Shader Program.
* [1] = Unknown, presumably the shader id.
* [2] = Offset to the start of the shader, after the 0x30 bytes header.
* [3] = Shader Stage.
* [4] = Const Buffer Address >> 8.
*/
auto shader_program = static_cast<Regs::ShaderProgram>(parameters[0]);
// TODO(Subv): This address is probably an offset from the CODE_ADDRESS register.
GPUVAddr address = parameters[2];
auto shader_stage = static_cast<Regs::ShaderStage>(parameters[3]);
GPUVAddr cb_address = parameters[4] << 8;
auto& shader = state.shader_programs[static_cast<size_t>(shader_program)];
shader.program = shader_program;
shader.stage = shader_stage;
shader.address = address;
// Perform the same operations as the real macro code.
// TODO(Subv): Early exit if register 0xD1C + shader_program contains the same as params[1].
auto& shader_regs = regs.shader_config[static_cast<size_t>(shader_program)];
shader_regs.start_id = address;
// TODO(Subv): Write params[1] to register 0xD1C + shader_program.
// TODO(Subv): Write params[2] to register 0xD22 + shader_program.
// Note: This value is hardcoded in the macro's code.
static constexpr u32 DefaultCBSize = 0x10000;
regs.const_buffer.cb_size = DefaultCBSize;
regs.const_buffer.cb_address_high = cb_address >> 32;
regs.const_buffer.cb_address_low = cb_address & 0xFFFFFFFF;
// Write a hardcoded 0x11 to CB_BIND, this binds the current const buffer to buffer c1[] in the
// shader. It's likely that these are the constants for the shader.
regs.cb_bind[static_cast<size_t>(shader_stage)].valid.Assign(1);
regs.cb_bind[static_cast<size_t>(shader_stage)].index.Assign(1);
ProcessCBBind(shader_stage);
}
void Maxwell3D::BindStorageBuffer(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Buffer offset >> 2
*/
u32 buffer_offset = parameters[0] << 2;
// Perform the same operations as the real macro code.
// Note: This value is hardcoded in the macro's code.
static constexpr u32 DefaultCBSize = 0x5F00;
regs.const_buffer.cb_size = DefaultCBSize;
GPUVAddr address = regs.ssbo_info.BufferAddress();
regs.const_buffer.cb_address_high = address >> 32;
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
regs.const_buffer.cb_pos = buffer_offset;
}
void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
auto& shader = state.shader_stages[static_cast<size_t>(stage)];
@@ -294,10 +386,5 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
return textures;
}
u32 Maxwell3D::GetRegisterValue(u32 method) const {
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register");
return regs.reg_array[method];
}
} // namespace Engines
} // namespace Tegra

View File

@@ -13,7 +13,6 @@
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/gpu.h"
#include "video_core/macro_interpreter.h"
#include "video_core/memory_manager.h"
#include "video_core/textures/texture.h"
@@ -499,18 +498,22 @@ public:
bool enabled;
};
struct ShaderProgramInfo {
Regs::ShaderStage stage;
Regs::ShaderProgram program;
GPUVAddr address;
};
struct ShaderStageInfo {
std::array<ConstBufferInfo, Regs::MaxConstBuffers> const_buffers;
};
std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
std::array<ShaderProgramInfo, Regs::MaxShaderProgram> shader_programs;
};
State state{};
/// Reads a register value located at the input method address
u32 GetRegisterValue(u32 method) const;
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value, u32 remaining_params);
@@ -530,9 +533,6 @@ private:
/// Parameters that have been submitted to the macro call so far.
std::vector<u32> macro_params;
/// Interpreter for the macro codes uploaded to the GPU.
MacroInterpreter macro_interpreter;
/// Retrieves information about a specific TIC entry from the TIC buffer.
Texture::TICEntry GetTICEntry(u32 tic_index) const;
@@ -544,7 +544,7 @@ private:
* @param method Method to call
* @param parameters Arguments to the method call
*/
void CallMacroMethod(u32 method, std::vector<u32> parameters);
void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
/// Handles a write to the QUERY_GET register.
void ProcessQueryGet();
@@ -557,6 +557,19 @@ private:
/// Handles a write to the VERTEX_END_GL register, triggering a draw.
void DrawArrays();
/// Method call handlers
void BindTextureInfoBuffer(const std::vector<u32>& parameters);
void SetShader(const std::vector<u32>& parameters);
void BindStorageBuffer(const std::vector<u32>& parameters);
struct MethodInfo {
const char* name;
u32 arguments;
void (Maxwell3D::*handler)(const std::vector<u32>& parameters);
};
static const std::unordered_map<u32, MethodInfo> method_handlers;
};
#define ASSERT_REG_POSITION(field_name, position) \

View File

@@ -1,257 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/macro_interpreter.h"
namespace Tegra {
MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> parameters) {
Reset();
registers[1] = parameters[0];
this->parameters = std::move(parameters);
// Execute the code until we hit an exit condition.
bool keep_executing = true;
while (keep_executing) {
keep_executing = Step(code, false);
}
// Assert the the macro used all the input parameters
ASSERT(next_parameter_index == this->parameters.size());
}
void MacroInterpreter::Reset() {
registers = {};
pc = 0;
delayed_pc = boost::none;
method_address.raw = 0;
parameters.clear();
// The next parameter index starts at 1, because $r1 already has the value of the first
// parameter.
next_parameter_index = 1;
}
bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
u32 base_address = pc;
Opcode opcode = GetOpcode(code);
pc += 4;
// Update the program counter if we were delayed
if (delayed_pc != boost::none) {
ASSERT(is_delay_slot);
pc = *delayed_pc;
delayed_pc = boost::none;
}
switch (opcode.operation) {
case Operation::ALU: {
u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a),
GetRegister(opcode.src_b));
ProcessResult(opcode.result_operation, opcode.dst, result);
break;
}
case Operation::AddImmediate: {
ProcessResult(opcode.result_operation, opcode.dst,
GetRegister(opcode.src_a) + opcode.immediate);
break;
}
case Operation::ExtractInsert: {
u32 dst = GetRegister(opcode.src_a);
u32 src = GetRegister(opcode.src_b);
src = (src >> opcode.bf_src_bit) & opcode.GetBitfieldMask();
dst &= ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
dst |= src << opcode.bf_dst_bit;
ProcessResult(opcode.result_operation, opcode.dst, dst);
break;
}
case Operation::ExtractShiftLeftImmediate: {
u32 dst = GetRegister(opcode.src_a);
u32 src = GetRegister(opcode.src_b);
u32 result = ((src >> dst) & opcode.GetBitfieldMask()) << opcode.bf_dst_bit;
ProcessResult(opcode.result_operation, opcode.dst, result);
break;
}
case Operation::ExtractShiftLeftRegister: {
u32 dst = GetRegister(opcode.src_a);
u32 src = GetRegister(opcode.src_b);
u32 result = ((src >> opcode.bf_src_bit) & opcode.GetBitfieldMask()) << dst;
ProcessResult(opcode.result_operation, opcode.dst, result);
break;
}
case Operation::Read: {
u32 result = Read(GetRegister(opcode.src_a) + opcode.immediate);
ProcessResult(opcode.result_operation, opcode.dst, result);
break;
}
case Operation::Branch: {
ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
u32 value = GetRegister(opcode.src_a);
bool taken = EvaluateBranchCondition(opcode.branch_condition, value);
if (taken) {
// Ignore the delay slot if the branch has the annul bit.
if (opcode.branch_annul) {
pc = base_address + (opcode.immediate << 2);
return true;
}
delayed_pc = base_address + (opcode.immediate << 2);
// Execute one more instruction due to the delay slot.
return Step(code, true);
}
break;
}
default:
UNIMPLEMENTED_MSG("Unimplemented macro operation %u",
static_cast<u32>(opcode.operation.Value()));
}
if (opcode.is_exit) {
// Exit has a delay slot, execute the next instruction
// Note: Executing an exit during a branch delay slot will cause the instruction at the
// branch target to be executed before exiting.
Step(code, true);
return false;
}
return true;
}
MacroInterpreter::Opcode MacroInterpreter::GetOpcode(const std::vector<u32>& code) const {
ASSERT((pc % sizeof(u32)) == 0);
ASSERT(pc < code.size() * sizeof(u32));
return {code[pc / sizeof(u32)]};
}
u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {
switch (operation) {
case ALUOperation::Add:
return src_a + src_b;
// TODO(Subv): Implement AddWithCarry
case ALUOperation::Subtract:
return src_a - src_b;
// TODO(Subv): Implement SubtractWithBorrow
case ALUOperation::Xor:
return src_a ^ src_b;
case ALUOperation::Or:
return src_a | src_b;
case ALUOperation::And:
return src_a & src_b;
case ALUOperation::AndNot:
return src_a & ~src_b;
case ALUOperation::Nand:
return ~(src_a & src_b);
default:
UNIMPLEMENTED_MSG("Unimplemented ALU operation %u", static_cast<u32>(operation));
}
}
void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 result) {
switch (operation) {
case ResultOperation::IgnoreAndFetch:
// Fetch parameter and ignore result.
SetRegister(reg, FetchParameter());
break;
case ResultOperation::Move:
// Move result.
SetRegister(reg, result);
break;
case ResultOperation::MoveAndSetMethod:
// Move result and use as Method Address.
SetRegister(reg, result);
SetMethodAddress(result);
break;
case ResultOperation::FetchAndSend:
// Fetch parameter and send result.
SetRegister(reg, FetchParameter());
Send(result);
break;
case ResultOperation::MoveAndSend:
// Move and send result.
SetRegister(reg, result);
Send(result);
break;
case ResultOperation::FetchAndSetMethod:
// Fetch parameter and use result as Method Address.
SetRegister(reg, FetchParameter());
SetMethodAddress(result);
break;
case ResultOperation::MoveAndSetMethodFetchAndSend:
// Move result and use as Method Address, then fetch and send parameter.
SetRegister(reg, result);
SetMethodAddress(result);
Send(FetchParameter());
break;
case ResultOperation::MoveAndSetMethodSend:
// Move result and use as Method Address, then send bits 12:17 of result.
SetRegister(reg, result);
SetMethodAddress(result);
Send((result >> 12) & 0b111111);
break;
default:
UNIMPLEMENTED_MSG("Unimplemented result operation %u", static_cast<u32>(operation));
}
}
u32 MacroInterpreter::FetchParameter() {
ASSERT(next_parameter_index < parameters.size());
return parameters[next_parameter_index++];
}
u32 MacroInterpreter::GetRegister(u32 register_id) const {
// Register 0 is supposed to always return 0.
if (register_id == 0)
return 0;
ASSERT(register_id < registers.size());
return registers[register_id];
}
void MacroInterpreter::SetRegister(u32 register_id, u32 value) {
// Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
// register.
if (register_id == 0)
return;
ASSERT(register_id < registers.size());
registers[register_id] = value;
}
void MacroInterpreter::SetMethodAddress(u32 address) {
method_address.raw = address;
}
void MacroInterpreter::Send(u32 value) {
maxwell3d.WriteReg(method_address.address, value, 0);
// Increment the method address by the method increment.
method_address.address.Assign(method_address.address.Value() +
method_address.increment.Value());
}
u32 MacroInterpreter::Read(u32 method) const {
return maxwell3d.GetRegisterValue(method);
}
bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value) const {
switch (cond) {
case BranchCondition::Zero:
return value == 0;
case BranchCondition::NotZero:
return value != 0;
}
UNREACHABLE();
}
} // namespace Tegra

View File

@@ -1,164 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include <boost/optional.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Tegra {
namespace Engines {
class Maxwell3D;
}
class MacroInterpreter final {
public:
explicit MacroInterpreter(Engines::Maxwell3D& maxwell3d);
/**
* Executes the macro code with the specified input parameters.
* @param code The macro byte code to execute
* @param parameters The parameters of the macro
*/
void Execute(const std::vector<u32>& code, std::vector<u32> parameters);
private:
enum class Operation : u32 {
ALU = 0,
AddImmediate = 1,
ExtractInsert = 2,
ExtractShiftLeftImmediate = 3,
ExtractShiftLeftRegister = 4,
Read = 5,
Unused = 6, // This operation doesn't seem to be a valid encoding.
Branch = 7,
};
enum class ALUOperation : u32 {
Add = 0,
AddWithCarry = 1,
Subtract = 2,
SubtractWithBorrow = 3,
// Operations 4-7 don't seem to be valid encodings.
Xor = 8,
Or = 9,
And = 10,
AndNot = 11,
Nand = 12
};
enum class ResultOperation : u32 {
IgnoreAndFetch = 0,
Move = 1,
MoveAndSetMethod = 2,
FetchAndSend = 3,
MoveAndSend = 4,
FetchAndSetMethod = 5,
MoveAndSetMethodFetchAndSend = 6,
MoveAndSetMethodSend = 7
};
enum class BranchCondition : u32 {
Zero = 0,
NotZero = 1,
};
union Opcode {
u32 raw;
BitField<0, 3, Operation> operation;
BitField<4, 3, ResultOperation> result_operation;
BitField<4, 1, BranchCondition> branch_condition;
BitField<5, 1, u32>
branch_annul; // If set on a branch, then the branch doesn't have a delay slot.
BitField<7, 1, u32> is_exit;
BitField<8, 3, u32> dst;
BitField<11, 3, u32> src_a;
BitField<14, 3, u32> src_b;
// The signed immediate overlaps the second source operand and the alu operation.
BitField<14, 18, s32> immediate;
BitField<17, 5, ALUOperation> alu_operation;
// Bitfield instructions data
BitField<17, 5, u32> bf_src_bit;
BitField<22, 5, u32> bf_size;
BitField<27, 5, u32> bf_dst_bit;
u32 GetBitfieldMask() const {
return (1 << bf_size) - 1;
}
};
union MethodAddress {
u32 raw;
BitField<0, 12, u32> address;
BitField<12, 6, u32> increment;
};
/// Resets the execution engine state, zeroing registers, etc.
void Reset();
/**
* Executes a single macro instruction located at the current program counter. Returns whether
* the interpreter should keep running.
* @param code The macro code to execute.
* @param is_delay_slot Whether the current step is being executed due to a delay slot in a
* previous instruction.
*/
bool Step(const std::vector<u32>& code, bool is_delay_slot);
/// Calculates the result of an ALU operation. src_a OP src_b;
u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
/// Performs the result operation on the input result and stores it in the specified register
/// (if necessary).
void ProcessResult(ResultOperation operation, u32 reg, u32 result);
/// Evaluates the branch condition and returns whether the branch should be taken or not.
bool EvaluateBranchCondition(BranchCondition cond, u32 value) const;
/// Reads an opcode at the current program counter location.
Opcode GetOpcode(const std::vector<u32>& code) const;
/// Returns the specified register's value. Register 0 is hardcoded to always return 0.
u32 GetRegister(u32 register_id) const;
/// Sets the register to the input value.
void SetRegister(u32 register_id, u32 value);
/// Sets the method address to use for the next Send instruction.
void SetMethodAddress(u32 address);
/// Calls a GPU Engine method with the input parameter.
void Send(u32 value);
/// Reads a GPU register located at the method address.
u32 Read(u32 method) const;
/// Returns the next parameter in the parameter queue.
u32 FetchParameter();
Engines::Maxwell3D& maxwell3d;
u32 pc; ///< Current program counter
boost::optional<u32>
delayed_pc; ///< Program counter to execute at after the delay slot is executed.
static constexpr size_t NumMacroRegisters = 8;
/// General purpose macro registers.
std::array<u32, NumMacroRegisters> registers = {};
/// Method address to use for the next Send instruction.
MethodAddress method_address = {};
/// Input parameters of the current macro.
std::vector<u32> parameters;
/// Index of the next parameter that will be fetched by the 'parm' instruction.
u32 next_parameter_index = 0;
};
} // namespace Tegra

View File

@@ -371,9 +371,9 @@ bool GMainWindow::LoadROM(const QString& filename) {
"yuzu. A real Switch is required.<br/><br/>"
"For more information on dumping and decrypting games, please see the following "
"wiki pages: <ul>"
"<li><a href='https://yuzu-emu.org/wiki/dumping-game-cartridges/'>Dumping Game "
"<li><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Dumping Game "
"Cartridges</a></li>"
"<li><a href='https://yuzu-emu.org/wiki/dumping-installed-titles/'>Dumping "
"<li><a href='https://citra-emu.org/wiki/dumping-installed-titles/'>Dumping "
"Installed Titles</a></li>"
"</ul>"));
break;
@@ -408,7 +408,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
}
void GMainWindow::BootGame(const QString& filename) {
NGLOG_INFO(Frontend, "yuzu starting...");
LOG_INFO(Frontend, "yuzu starting...");
StoreRecentFile(filename); // Put the filename on top of the list
if (!LoadROM(filename))
@@ -695,18 +695,18 @@ void GMainWindow::UpdateStatusBar() {
void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
QMessageBox::StandardButton answer;
QString status_message;
const QString common_message = tr(
"The game you are trying to load requires additional files from your Switch to be dumped "
"before playing.<br/><br/>For more information on dumping these files, please see the "
"following wiki page: <a "
"href='https://yuzu-emu.org/wiki/"
"dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
"Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit "
"back to the game list? Continuing emulation may result in crashes, corrupted save "
"data, or other bugs.");
const QString common_message =
tr("The game you are trying to load requires additional files from your 3DS to be dumped "
"before playing.<br/><br/>For more information on dumping these files, please see the "
"following wiki page: <a "
"href='https://citra-emu.org/wiki/"
"dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>Dumping System "
"Archives and the Shared Fonts from a 3DS Console</a>.<br/><br/>Would you like to quit "
"back to the game list? Continuing emulation may result in crashes, corrupted save "
"data, or other bugs.");
switch (result) {
case Core::System::ResultStatus::ErrorSystemFiles: {
QString message = "yuzu was unable to locate a Switch system archive";
QString message = "Citra was unable to locate a 3DS system archive";
if (!details.empty()) {
message.append(tr(": %1. ").arg(details.c_str()));
} else {
@@ -721,7 +721,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
}
case Core::System::ResultStatus::ErrorSharedFont: {
QString message = tr("yuzu was unable to locate the Switch shared fonts. ");
QString message = tr("Citra was unable to locate the 3DS shared fonts. ");
message.append(common_message);
answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message,
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

View File

@@ -177,12 +177,12 @@ gdbstub_port=24689
# 0: No, 1 (default): Yes
enable_telemetry =
# Endpoint URL for submitting telemetry data
telemetry_endpoint_url =
telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry
# Endpoint URL to verify the username and token
verify_endpoint_url =
# Username and token for yuzu Web Service
verify_endpoint_url = https://services.citra-emu.org/api/profile
# Username and token for Citra Web Service
# See https://services.citra-emu.org/ for more info
yuzu_username =
yuzu_token =
citra_username =
citra_token =
)";
}

View File

@@ -147,7 +147,7 @@ int main(int argc, char** argv) {
LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
"being used with yuzu. \n\n For more information on dumping and "
"decrypting games, please refer to: "
"https://yuzu-emu.org/wiki/dumping-game-cartridges/");
"https://citra-emu.org/wiki/dumping-game-cartridges/");
return -1;
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");