summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.travis-deps.sh4
-rw-r--r--CMakeLists.txt3
-rw-r--r--appveyor.yml4
-rw-r--r--dist/citra.icnsbin0 -> 1027012 bytes
-rw-r--r--src/citra/citra.cpp3
-rw-r--r--src/citra/config.cpp4
-rw-r--r--src/citra/default_ini.h5
-rw-r--r--src/citra_qt/CMakeLists.txt4
-rw-r--r--src/citra_qt/config.cpp10
-rw-r--r--src/citra_qt/debugger/callstack.cpp12
-rw-r--r--src/citra_qt/debugger/registers.cpp8
-rw-r--r--src/citra_qt/game_list.cpp18
-rw-r--r--src/citra_qt/main.cpp31
-rw-r--r--src/citra_qt/main.h1
-rw-r--r--src/citra_qt/main.ui9
-rw-r--r--src/common/file_util.cpp80
-rw-r--r--src/common/file_util.h31
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/logging/log.h2
-rw-r--r--src/common/vector_math.h4
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/arm/arm_interface.h11
-rw-r--r--src/core/arm/dyncom/arm_dyncom_dec.cpp8
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp587
-rw-r--r--src/core/arm/dyncom/arm_dyncom_run.h4
-rw-r--r--src/core/arm/dyncom/arm_dyncom_thumb.h2
-rw-r--r--src/core/arm/skyeye_common/armstate.cpp35
-rw-r--r--src/core/arm/skyeye_common/armstate.h24
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp.cpp12
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp.h2
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp_helper.h32
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpdouble.cpp76
-rw-r--r--src/core/core.cpp32
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/file_sys/archive_backend.h6
-rw-r--r--src/core/file_sys/disk_archive.cpp5
-rw-r--r--src/core/file_sys/disk_archive.h1
-rw-r--r--src/core/file_sys/ivfc_archive.cpp5
-rw-r--r--src/core/file_sys/ivfc_archive.h1
-rw-r--r--src/core/gdbstub/gdbstub.cpp960
-rw-r--r--src/core/gdbstub/gdbstub.h94
-rw-r--r--src/core/hle/function_wrappers.h8
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp25
-rw-r--r--src/core/hle/kernel/memory.cpp2
-rw-r--r--src/core/hle/kernel/memory.h1
-rw-r--r--src/core/hle/kernel/process.cpp6
-rw-r--r--src/core/hle/kernel/thread.cpp5
-rw-r--r--src/core/hle/kernel/timer.cpp6
-rw-r--r--src/core/hle/service/act_u.cpp7
-rw-r--r--src/core/hle/service/am/am_net.cpp30
-rw-r--r--src/core/hle/service/am/am_sys.cpp15
-rw-r--r--src/core/hle/service/am/am_u.cpp28
-rw-r--r--src/core/hle/service/apt/apt_s.cpp6
-rw-r--r--src/core/hle/service/apt/apt_u.cpp6
-rw-r--r--src/core/hle/service/boss/boss_u.cpp3
-rw-r--r--src/core/hle/service/cam/cam.h156
-rw-r--r--src/core/hle/service/cam/cam_u.cpp5
-rw-r--r--src/core/hle/service/csnd_snd.cpp65
-rw-r--r--src/core/hle/service/csnd_snd.h13
-rw-r--r--src/core/hle/service/dsp_dsp.cpp14
-rw-r--r--src/core/hle/service/frd/frd_u.cpp43
-rw-r--r--src/core/hle/service/fs/archive.cpp7
-rw-r--r--src/core/hle/service/fs/archive.h7
-rw-r--r--src/core/hle/service/fs/fs_user.cpp223
-rw-r--r--src/core/hle/service/gsp_gpu.cpp8
-rw-r--r--src/core/hle/service/gsp_lcd.cpp10
-rw-r--r--src/core/hle/service/hid/hid_user.cpp2
-rw-r--r--src/core/hle/service/http_c.cpp10
-rw-r--r--src/core/hle/service/mic_u.cpp10
-rw-r--r--src/core/hle/service/ndm_u.cpp16
-rw-r--r--src/core/hle/service/news/news_s.cpp12
-rw-r--r--src/core/hle/service/nim/nim_s.cpp3
-rw-r--r--src/core/hle/service/ns_s.cpp9
-rw-r--r--src/core/hle/service/nwm_uds.cpp18
-rw-r--r--src/core/hle/service/pm_app.cpp4
-rw-r--r--src/core/hle/service/ptm/ptm.cpp12
-rw-r--r--src/core/hle/service/ptm/ptm.h8
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.cpp3
-rw-r--r--src/core/hle/service/ptm/ptm_u.cpp2
-rw-r--r--src/core/hle/service/soc_u.cpp16
-rw-r--r--src/core/hle/service/ssl_c.cpp55
-rw-r--r--src/core/hle/service/y2r_u.cpp23
-rw-r--r--src/core/hle/svc.cpp47
-rw-r--r--src/core/hle/svc.h29
-rw-r--r--src/core/hw/gpu.cpp12
-rw-r--r--src/core/loader/3dsx.cpp4
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/ncch.cpp8
-rw-r--r--src/core/loader/ncch.h36
-rw-r--r--src/core/memory.cpp4
-rw-r--r--src/core/settings.h5
-rw-r--r--src/core/system.cpp4
-rw-r--r--src/video_core/CMakeLists.txt8
-rw-r--r--src/video_core/clipper.cpp2
-rw-r--r--src/video_core/clipper.h2
-rw-r--r--src/video_core/command_processor.cpp27
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp12
-rw-r--r--src/video_core/pica.h4
-rw-r--r--src/video_core/primitive_assembly.cpp9
-rw-r--r--src/video_core/rasterizer.cpp21
-rw-r--r--src/video_core/rasterizer_interface.h (renamed from src/video_core/hwrasterizer_base.h)19
-rw-r--r--src/video_core/renderer_base.cpp29
-rw-r--r--src/video_core/renderer_base.h8
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp383
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h188
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h8
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp392
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h27
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h20
-rw-r--r--src/video_core/renderer_opengl/gl_shaders.h337
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_state.h3
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp55
-rw-r--r--src/video_core/swrasterizer.cpp16
-rw-r--r--src/video_core/swrasterizer.h26
-rw-r--r--src/video_core/video_core.cpp11
-rw-r--r--src/video_core/video_core.h5
120 files changed, 3427 insertions, 1407 deletions
diff --git a/.travis-deps.sh b/.travis-deps.sh
index 5c530dcb9..4b907abb3 100755
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -9,7 +9,7 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
export CXX=g++-4.9
mkdir -p $HOME/.local
- curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
+ curl -L http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
| tar -xz -C $HOME/.local --strip-components=1
(
@@ -25,6 +25,6 @@ if [ "$TRAVIS_OS_NAME" = "linux" -o -z "$TRAVIS_OS_NAME" ]; then
elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
brew update > /dev/null # silence the very verbose output
- brew install qt5 glfw3 pkgconfig
+ brew install qt5 glfw3
gem install xcpretty
fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 604362e57..b8a981711 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -92,9 +92,8 @@ else()
# /Zi - Output debugging information
# /Zo - enahnced debug info for optimized builds
set(CMAKE_C_FLAGS "/W3 /MP /Zi /Zo" CACHE STRING "" FORCE)
- # /GR- - Disable RTTI
# /EHsc - C++-only exception handling semantics
- set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /GR- /EHsc" CACHE STRING "" FORCE)
+ set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} /EHsc" CACHE STRING "" FORCE)
# /MDd - Multi-threaded Debug Runtime DLL
set(CMAKE_C_FLAGS_DEBUG "/Od /MDd" CACHE STRING "" FORCE)
diff --git a/appveyor.yml b/appveyor.yml
index d05cc2213..a5ed35392 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -5,6 +5,8 @@ environment:
BUILD_PASSWORD:
secure: EXGNlWKJsCtbeImEJ5EP9qrxZ+EqUFfNy+CP61nDOMA=
+os: Visual Studio 2015
+
platform:
- x64
@@ -17,7 +19,7 @@ install:
before_build:
- mkdir build
- cd build
- - cmake -G "Visual Studio 14 Win64" -DCITRA_USE_BUNDLED_GLFW=1 -DCITRA_USE_BUNDLED_QT=1 ..
+ - cmake -G "Visual Studio 14 2015 Win64" -DCITRA_USE_BUNDLED_GLFW=1 -DCITRA_USE_BUNDLED_QT=1 ..
- cd ..
after_build:
diff --git a/dist/citra.icns b/dist/citra.icns
new file mode 100644
index 000000000..9d3dcca83
--- /dev/null
+++ b/dist/citra.icns
Binary files differ
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index 46f4a07c9..c96fc1374 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -23,6 +23,7 @@
#include "core/settings.h"
#include "core/system.h"
#include "core/core.h"
+#include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h"
#include "citra/config.h"
@@ -72,6 +73,8 @@ int main(int argc, char **argv) {
Config config;
log_filter.ParseFilterString(Settings::values.log_filter);
+ GDBStub::ToggleServer(Settings::values.use_gdbstub);
+ GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 8a98bda87..2f13c29a2 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -75,6 +75,10 @@ void Config::ReadValues() {
// Miscellaneous
Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info");
+
+ // Debugging
+ Settings::values.use_gdbstub = glfw_config->GetBoolean("Debugging", "use_gdbstub", false);
+ Settings::values.gdbstub_port = glfw_config->GetInteger("Debugging", "gdbstub_port", 24689);
}
void Config::Reload() {
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 7e5d49729..5ba40a8ed 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -66,6 +66,11 @@ region_value =
# A filter which removes logs below a certain logging level.
# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
log_filter = *:Info
+
+[Debugging]
+# Port for listening to GDB connections.
+use_gdbstub=false
+gdbstub_port=24689
)";
}
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 747ad5519..bbf6ae001 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -72,7 +72,9 @@ else()
endif()
if (APPLE)
- add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS})
+ set(MACOSX_ICON "../../dist/citra.icns")
+ set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
+ add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${MACOSX_ICON})
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
else()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 1f4981ce1..8e247ff5c 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -62,6 +62,11 @@ void Config::ReadValues() {
qt_config->beginGroup("Miscellaneous");
Settings::values.log_filter = qt_config->value("log_filter", "*:Info").toString().toStdString();
qt_config->endGroup();
+
+ qt_config->beginGroup("Debugging");
+ Settings::values.use_gdbstub = qt_config->value("use_gdbstub", false).toBool();
+ Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
+ qt_config->endGroup();
}
void Config::SaveValues() {
@@ -97,6 +102,11 @@ void Config::SaveValues() {
qt_config->beginGroup("Miscellaneous");
qt_config->setValue("log_filter", QString::fromStdString(Settings::values.log_filter));
qt_config->endGroup();
+
+ qt_config->beginGroup("Debugging");
+ qt_config->setValue("use_gdbstub", Settings::values.use_gdbstub);
+ qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
+ qt_config->endGroup();
}
void Config::Reload() {
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index d45eed179..793944639 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -29,18 +29,16 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent)
void CallstackWidget::OnDebugModeEntered()
{
- ARM_Interface* app_core = Core::g_app_core;
-
- u32 sp = app_core->GetReg(13); //stack pointer
- u32 ret_addr, call_addr, func_addr;
+ // Stack pointer
+ const u32 sp = Core::g_app_core->GetReg(13);
Clear();
int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
{
- ret_addr = Memory::Read32(addr);
- call_addr = ret_addr - 4; //get call address???
+ const u32 ret_addr = Memory::Read32(addr);
+ const u32 call_addr = ret_addr - 4; //get call address???
if (Memory::GetPointer(call_addr) == nullptr)
break;
@@ -60,7 +58,7 @@ void CallstackWidget::OnDebugModeEntered()
// Pre-compute the left-shift and the prefetch offset
i_offset <<= 2;
i_offset += 8;
- func_addr = call_addr + i_offset;
+ const u32 func_addr = call_addr + i_offset;
callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0'))));
diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp
index 6100d67c5..1bd0bfebc 100644
--- a/src/citra_qt/debugger/registers.cpp
+++ b/src/citra_qt/debugger/registers.cpp
@@ -59,16 +59,14 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
}
void RegistersWidget::OnDebugModeEntered() {
- ARM_Interface* app_core = Core::g_app_core;
-
- if (app_core == nullptr)
+ if (!Core::g_app_core)
return;
for (int i = 0; i < core_registers->childCount(); ++i)
- core_registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0')));
+ core_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0')));
for (int i = 0; i < vfp_registers->childCount(); ++i)
- vfp_registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetVFPReg(i), 8, 16, QLatin1Char('0')));
+ vfp_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0')));
UpdateCPSRValues();
UpdateVFPSystemRegisterValues();
diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp
index dade3c212..1f8d69a03 100644
--- a/src/citra_qt/game_list.cpp
+++ b/src/citra_qt/game_list.cpp
@@ -80,7 +80,7 @@ void GameList::DonePopulating()
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan)
{
if (!FileUtil::Exists(dir_path.toStdString()) || !FileUtil::IsDirectory(dir_path.toStdString())) {
- LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLatin1().data());
+ LOG_ERROR(Frontend, "Could not find game list folder at %s", dir_path.toLocal8Bit().data());
return;
}
@@ -119,13 +119,14 @@ void GameList::LoadInterfaceLayout(QSettings& settings)
void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool deep_scan)
{
- const auto callback = [&](const std::string& directory,
- const std::string& virtual_name) -> int {
+ const auto callback = [&](unsigned* num_entries_out,
+ const std::string& directory,
+ const std::string& virtual_name) -> bool {
std::string physical_name = directory + DIR_SEP + virtual_name;
if (stop_processing)
- return -1; // A negative return value breaks the callback loop.
+ return false; // Breaks the callback loop.
if (deep_scan && FileUtil::IsDirectory(physical_name)) {
AddFstEntriesToGameList(physical_name, true);
@@ -135,11 +136,11 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
Loader::FileType guessed_filetype = Loader::GuessFromExtension(filename_extension);
if (guessed_filetype == Loader::FileType::Unknown)
- return 0;
+ return true;
Loader::FileType filetype = Loader::IdentifyFile(physical_name);
if (filetype == Loader::FileType::Unknown) {
LOG_WARNING(Frontend, "File %s is of indeterminate type and is possibly corrupted.", physical_name.c_str());
- return 0;
+ return true;
}
if (guessed_filetype != filetype) {
LOG_WARNING(Frontend, "Filetype and extension of file %s do not match.", physical_name.c_str());
@@ -152,9 +153,10 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, bool d
});
}
- return 0; // We don't care about the found entries
+ return true;
};
- FileUtil::ScanDirectoryTreeAndCallback(dir_path, callback);
+
+ FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
}
void GameListWorker::run()
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index bf010a2ba..144f11117 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -44,6 +44,7 @@
#include "core/settings.h"
#include "core/system.h"
#include "core/arm/disassembler/load_symbol_map.h"
+#include "core/gdbstub/gdbstub.h"
#include "core/loader/loader.h"
#include "video_core/video_core.h"
@@ -143,6 +144,11 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
game_list->LoadInterfaceLayout(settings);
+ ui.action_Use_Gdbstub->setChecked(Settings::values.use_gdbstub);
+ SetGdbstubEnabled(ui.action_Use_Gdbstub->isChecked());
+
+ GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
+
ui.action_Use_Hardware_Renderer->setChecked(Settings::values.use_hw_renderer);
SetHardwareRendererEnabled(ui.action_Use_Hardware_Renderer->isChecked());
@@ -175,6 +181,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
connect(ui.action_Stop, SIGNAL(triggered()), this, SLOT(OnStopGame()));
connect(ui.action_Use_Hardware_Renderer, SIGNAL(triggered(bool)), this, SLOT(SetHardwareRendererEnabled(bool)));
connect(ui.action_Use_Shader_JIT, SIGNAL(triggered(bool)), this, SLOT(SetShaderJITEnabled(bool)));
+ connect(ui.action_Use_Gdbstub, SIGNAL(triggered(bool)), this, SLOT(SetGdbstubEnabled(bool)));
connect(ui.action_Single_Window_Mode, SIGNAL(triggered(bool)), this, SLOT(ToggleWindowMode()));
connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
@@ -201,7 +208,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
show();
- game_list->PopulateAsync(settings.value("gameListRootDir").toString(), settings.value("gameListDeepScan").toBool());
+ game_list->PopulateAsync(settings.value("gameListRootDir", ".").toString(), settings.value("gameListDeepScan", false).toBool());
QStringList args = QApplication::arguments();
if (args.length() >= 2) {
@@ -240,7 +247,7 @@ void GMainWindow::OnDisplayTitleBars(bool show)
}
void GMainWindow::BootGame(const std::string& filename) {
- LOG_INFO(Frontend, "Citra starting...\n");
+ LOG_INFO(Frontend, "Citra starting...");
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
@@ -355,7 +362,7 @@ void GMainWindow::UpdateRecentFiles() {
}
void GMainWindow::OnGameListLoadFile(QString game_path) {
- BootGame(game_path.toLatin1().data());
+ BootGame(game_path.toLocal8Bit().data());
}
void GMainWindow::OnMenuLoadFile() {
@@ -367,7 +374,7 @@ void GMainWindow::OnMenuLoadFile() {
settings.setValue("romsPath", QFileInfo(filename).path());
StoreRecentFile(filename);
- BootGame(filename.toLatin1().data());
+ BootGame(filename.toLocal8Bit().data());
}
}
@@ -379,7 +386,7 @@ void GMainWindow::OnMenuLoadSymbolMap() {
if (!filename.isEmpty()) {
settings.setValue("symbolsPath", QFileInfo(filename).path());
- LoadSymbolMap(filename.toLatin1().data());
+ LoadSymbolMap(filename.toLocal8Bit().data());
}
}
@@ -400,7 +407,7 @@ void GMainWindow::OnMenuRecentFile() {
QString filename = action->data().toString();
QFileInfo file_info(filename);
if (file_info.exists()) {
- BootGame(filename.toLatin1().data());
+ BootGame(filename.toLocal8Bit().data());
StoreRecentFile(filename); // Put the filename on top of the list
} else {
// Display an error message and remove the file from the list.
@@ -443,10 +450,22 @@ void GMainWindow::OnOpenHotkeysDialog() {
void GMainWindow::SetHardwareRendererEnabled(bool enabled) {
VideoCore::g_hw_renderer_enabled = enabled;
+
+ Config config;
+ Settings::values.use_hw_renderer = enabled;
+ config.Save();
+}
+
+void GMainWindow::SetGdbstubEnabled(bool enabled) {
+ GDBStub::ToggleServer(enabled);
}
void GMainWindow::SetShaderJITEnabled(bool enabled) {
VideoCore::g_shader_jit_enabled = enabled;
+
+ Config config;
+ Settings::values.use_shader_jit = enabled;
+ config.Save();
}
void GMainWindow::ToggleWindowMode() {
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 6d27ce6a9..f6d429cd9 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -99,6 +99,7 @@ private slots:
void OnConfigure();
void OnDisplayTitleBars(bool);
void SetHardwareRendererEnabled(bool);
+ void SetGdbstubEnabled(bool);
void SetShaderJITEnabled(bool);
void ToggleWindowMode();
diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui
index 997597642..1e8a07cfb 100644
--- a/src/citra_qt/main.ui
+++ b/src/citra_qt/main.ui
@@ -75,6 +75,7 @@
<addaction name="separator"/>
<addaction name="action_Use_Hardware_Renderer"/>
<addaction name="action_Use_Shader_JIT"/>
+ <addaction name="action_Use_Gdbstub"/>
<addaction name="action_Configure"/>
</widget>
<widget class="QMenu" name="menu_View">
@@ -170,6 +171,14 @@
<string>Use Shader JIT</string>
</property>
</action>
+ <action name="action_Use_Gdbstub">
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Use Gdbstub</string>
+ </property>
+ </action>
<action name="action_Configure">
<property name="text">
<string>Configure ...</string>
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 1e0d33313..052c0ecd6 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -420,11 +420,16 @@ bool CreateEmptyFile(const std::string &filename)
}
-int ScanDirectoryTreeAndCallback(const std::string &directory, std::function<int(const std::string&, const std::string&)> callback)
+bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback)
{
LOG_TRACE(Common_Filesystem, "directory %s", directory.c_str());
+
// How many files + directories we found
- int found_entries = 0;
+ unsigned found_entries = 0;
+
+ // Save the status of callback function
+ bool callback_error = false;
+
#ifdef _WIN32
// Find the first file in the directory.
WIN32_FIND_DATA ffd;
@@ -432,7 +437,7 @@ int ScanDirectoryTreeAndCallback(const std::string &directory, std::function<int
HANDLE handle_find = FindFirstFile(Common::UTF8ToTStr(directory + "\\*").c_str(), &ffd);
if (handle_find == INVALID_HANDLE_VALUE) {
FindClose(handle_find);
- return found_entries;
+ return false;
}
// windows loop
do {
@@ -442,25 +447,22 @@ int ScanDirectoryTreeAndCallback(const std::string &directory, std::function<int
DIR *dirp = opendir(directory.c_str());
if (!dirp)
- return 0;
+ return false;
// non windows loop
while (!readdir_r(dirp, &dirent, &result) && result) {
const std::string virtual_name(result->d_name);
#endif
- // check for "." and ".."
- if (((virtual_name[0] == '.') && (virtual_name[1] == '\0')) ||
- ((virtual_name[0] == '.') && (virtual_name[1] == '.') &&
- (virtual_name[2] == '\0')))
+
+ if (virtual_name == "." || virtual_name == "..")
continue;
- int ret = callback(directory, virtual_name);
- if (ret < 0) {
- if (ret != -1)
- found_entries = ret;
+ unsigned ret_entries;
+ if (!callback(&ret_entries, directory, virtual_name)) {
+ callback_error = true;
break;
}
- found_entries += ret;
+ found_entries += ret_entries;
#ifdef _WIN32
} while (FindNextFile(handle_find, &ffd) != 0);
@@ -469,16 +471,23 @@ int ScanDirectoryTreeAndCallback(const std::string &directory, std::function<int
}
closedir(dirp);
#endif
- // Return number of entries found.
- return found_entries;
+
+ if (!callback_error) {
+ // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
+ if (num_entries_out != nullptr)
+ *num_entries_out = found_entries;
+ return true;
+ } else {
+ return false;
+ }
}
-int ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
+unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
{
- const auto callback = [&parent_entry](const std::string& directory,
- const std::string& virtual_name) -> int {
+ const auto callback = [&parent_entry](unsigned* num_entries_out,
+ const std::string& directory,
+ const std::string& virtual_name) -> bool {
FSTEntry entry;
- int found_entries = 0;
entry.virtualName = virtual_name;
entry.physicalName = directory + DIR_SEP + virtual_name;
@@ -486,41 +495,40 @@ int ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
entry.isDirectory = true;
// is a directory, lets go inside
entry.size = ScanDirectoryTree(entry.physicalName, entry);
- found_entries += (int)entry.size;
+ *num_entries_out += (int)entry.size;
} else { // is a file
entry.isDirectory = false;
entry.size = GetSize(entry.physicalName);
}
- ++found_entries;
+ (*num_entries_out)++;
+
// Push into the tree
parent_entry.children.push_back(entry);
- return found_entries;
+ return true;
};
- return ScanDirectoryTreeAndCallback(directory, callback);
+ unsigned num_entries;
+ return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0;
}
bool DeleteDirRecursively(const std::string &directory)
{
- const static auto callback = [](const std::string& directory,
- const std::string& virtual_name) -> int {
+ const static auto callback = [](unsigned* num_entries_out,
+ const std::string& directory,
+ const std::string& virtual_name) -> bool {
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
- if (IsDirectory(new_path)) {
- if (!DeleteDirRecursively(new_path)) {
- return -2;
- }
- } else if (!Delete(new_path)) {
- return -2;
- }
- return 0;
+ if (IsDirectory(new_path))
+ return DeleteDirRecursively(new_path);
+
+ return Delete(new_path);
};
- if (ScanDirectoryTreeAndCallback(directory, callback) == -2) {
+ if (!ForeachDirectoryEntry(nullptr, directory, callback))
return false;
- }
- FileUtil::DeleteDir(directory);
+ // Delete the outermost directory
+ FileUtil::DeleteDir(directory);
return true;
}
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 3d617f573..a85121aa6 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -98,19 +98,24 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename);
bool CreateEmptyFile(const std::string &filename);
/**
- * Scans the directory tree, calling the callback for each file/directory found.
- * The callback must return the number of files and directories which the provided path contains.
- * If the callback's return value is -1, the callback loop is broken immediately.
- * If the callback's return value is otherwise negative, the callback loop is broken immediately
- * and the callback's return value is returned from this function (to allow for error handling).
- * @param directory the parent directory to start scanning from
- * @param callback The callback which will be called for each file/directory. It is called
- * with the arguments (const std::string& directory, const std::string& virtual_name).
- * The `directory `parameter is the path to the directory which contains the file/directory.
- * The `virtual_name` parameter is the incomplete file path, without any directory info.
- * @return the total number of files/directories found
+ * @param num_entries_out to be assigned by the callable with the number of iterated directory entries, never null
+ * @param directory the path to the enclosing directory
+ * @param virtual_name the entry name, without any preceding directory info
+ * @return whether handling the entry succeeded
+ */
+using DirectoryEntryCallable = std::function<bool(unsigned* num_entries_out,
+ const std::string& directory,
+ const std::string& virtual_name)>;
+
+/**
+ * Scans a directory, calling the callback for each file/directory contained within.
+ * If the callback returns failure, scanning halts and this function returns failure as well
+ * @param num_entries_out assigned by the function with the number of iterated directory entries, can be null
+ * @param directory the directory to scan
+ * @param callback The callback which will be called for each entry
+ * @return whether scanning the directory succeeded
*/
-int ScanDirectoryTreeAndCallback(const std::string &directory, std::function<int(const std::string&, const std::string&)> callback);
+bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directory, DirectoryEntryCallable callback);
/**
* Scans the directory tree, storing the results.
@@ -118,7 +123,7 @@ int ScanDirectoryTreeAndCallback(const std::string &directory, std::function<int
* @param parent_entry FSTEntry where the filesystem tree results will be stored.
* @return the total number of files/directories found
*/
-int ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry);
+unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry);
// deletes the given directory and anything under it. Returns true on success.
bool DeleteDirRecursively(const std::string &directory);
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 92e8e742d..d186ba8f8 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -29,6 +29,7 @@ namespace Log {
SUB(Debug, Emulated) \
SUB(Debug, GPU) \
SUB(Debug, Breakpoint) \
+ SUB(Debug, GDBStub) \
CLS(Kernel) \
SUB(Kernel, SVC) \
CLS(Service) \
@@ -43,6 +44,7 @@ namespace Log {
SUB(Service, LDR) \
SUB(Service, NIM) \
SUB(Service, NWM) \
+ SUB(Service, CAM) \
SUB(Service, CFG) \
SUB(Service, DSP) \
SUB(Service, HID) \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 5fd3bd7f5..2d9323a7b 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -43,6 +43,7 @@ enum class Class : ClassType {
Debug_Emulated, ///< Debug messages from the emulated programs
Debug_GPU, ///< GPU debugging tools
Debug_Breakpoint, ///< Logging breakpoints and watchpoints
+ Debug_GDBStub, ///< GDB Stub
Kernel, ///< The HLE implementation of the CTR kernel
Kernel_SVC, ///< Kernel system calls
Service, ///< HLE implementation of system services. Each major service
@@ -58,6 +59,7 @@ enum class Class : ClassType {
Service_LDR, ///< The LDR (3ds dll loader) service
Service_NIM, ///< The NIM (Network interface manager) service
Service_NWM, ///< The NWM (Network wlan manager) service
+ Service_CAM, ///< The CAM (Camera) service
Service_CFG, ///< The CFG (Configuration) service
Service_DSP, ///< The DSP (DSP control) service
Service_HID, ///< The HID (Human interface device) service
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 4928c9bf2..02688e35e 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -32,6 +32,7 @@
#pragma once
#include <cmath>
+#include <type_traits>
namespace Math {
@@ -90,6 +91,7 @@ public:
{
x-=other.x; y-=other.y;
}
+ template<typename Q = T,class = typename std::enable_if<std::is_signed<Q>::value>::type>
Vec2<decltype(-T{})> operator -() const
{
return MakeVec(-x,-y);
@@ -220,6 +222,7 @@ public:
{
x-=other.x; y-=other.y; z-=other.z;
}
+ template<typename Q = T,class = typename std::enable_if<std::is_signed<Q>::value>::type>
Vec3<decltype(-T{})> operator -() const
{
return MakeVec(-x,-y,-z);
@@ -390,6 +393,7 @@ public:
{
x-=other.x; y-=other.y; z-=other.z; w-=other.w;
}
+ template<typename Q = T,class = typename std::enable_if<std::is_signed<Q>::value>::type>
Vec4<decltype(-T{})> operator -() const
{
return MakeVec(-x,-y,-z,-w);
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c17290b9b..861b711c7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -22,6 +22,7 @@ set(SRCS
file_sys/archive_systemsavedata.cpp
file_sys/disk_archive.cpp
file_sys/ivfc_archive.cpp
+ gdbstub/gdbstub.cpp
hle/config_mem.cpp
hle/hle.cpp
hle/applets/applet.cpp
@@ -149,6 +150,7 @@ set(HEADERS
file_sys/disk_archive.h
file_sys/file_backend.h
file_sys/ivfc_archive.h
+ gdbstub/gdbstub.h
hle/config_mem.h
hle/function_wrappers.h
hle/hle.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 5cffe513c..533067d4f 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -14,10 +14,6 @@ namespace Core {
/// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable {
public:
- ARM_Interface() {
- num_instructions = 0;
- }
-
virtual ~ARM_Interface() {
}
@@ -146,11 +142,11 @@ public:
virtual void PrepareReschedule() = 0;
/// Getter for num_instructions
- u64 GetNumInstructions() {
+ u64 GetNumInstructions() const {
return num_instructions;
}
- s64 down_count; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
+ s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
protected:
@@ -162,6 +158,5 @@ protected:
private:
- u64 num_instructions; ///< Number of instructions executed
-
+ u64 num_instructions = 0; ///< Number of instructions executed
};
diff --git a/src/core/arm/dyncom/arm_dyncom_dec.cpp b/src/core/arm/dyncom/arm_dyncom_dec.cpp
index ee4288314..8cd6755cb 100644
--- a/src/core/arm/dyncom/arm_dyncom_dec.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_dec.cpp
@@ -6,10 +6,9 @@
#include "core/arm/skyeye_common/armsupp.h"
const InstructionSetEncodingItem arm_instruction[] = {
- { "vmla", 4, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 4, 4, 0 }},
- { "vmls", 7, ARMVFP2, { 28, 31, 0xF, 25, 27, 0x1, 23, 23, 1, 11, 11, 0, 8, 9, 0x2, 6, 6, 1, 4, 4, 0 }},
- { "vnmla", 4, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 4, 4, 0 }},
- { "vnmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
+ { "vmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
+ { "vmls", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
+ { "vnmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
{ "vnmls", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
{ "vnmul", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
{ "vmul", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
@@ -211,7 +210,6 @@ const InstructionSetEncodingItem arm_exclusion_code[] = {
{ "vmla", 0, ARMVFP2, { 0 }},
{ "vmls", 0, ARMVFP2, { 0 }},
{ "vnmla", 0, ARMVFP2, { 0 }},
- { "vnmla", 0, ARMVFP2, { 0 }},
{ "vnmls", 0, ARMVFP2, { 0 }},
{ "vnmul", 0, ARMVFP2, { 0 }},
{ "vmul", 0, ARMVFP2, { 0 }},
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index fbd6f94f9..5f8826034 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -23,6 +23,8 @@
#include "core/arm/skyeye_common/armsupp.h"
#include "core/arm/skyeye_common/vfp/vfp.h"
+#include "core/gdbstub/gdbstub.h"
+
Common::Profiling::TimingCategory profile_execute("DynCom::Execute");
Common::Profiling::TimingCategory profile_decode("DynCom::Decode");
@@ -49,7 +51,7 @@ enum {
typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper);
-static bool CondPassed(ARMul_State* cpu, unsigned int cond) {
+static bool CondPassed(const ARMul_State* cpu, unsigned int cond) {
const bool n_flag = cpu->NFlag != 0;
const bool z_flag = cpu->ZFlag != 0;
const bool c_flag = cpu->CFlag != 0;
@@ -1621,9 +1623,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrb)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index)
@@ -1644,9 +1643,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index)
DEBUG_MSG;
}
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrd)(unsigned int inst, int index)
@@ -1701,9 +1697,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrh)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(unsigned int inst, int index)
@@ -1718,9 +1711,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index)
@@ -1735,9 +1725,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrt)(unsigned int inst, int index)
@@ -2595,9 +2582,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(str)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb)(unsigned int inst, int index)
@@ -2643,9 +2627,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strb)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index)
@@ -2667,9 +2648,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index)
DEBUG_MSG;
}
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(unsigned int inst, int index){
@@ -2683,9 +2661,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(unsigned int inst, int index){
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(strex)(unsigned int inst, int index)
@@ -2727,9 +2702,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strh)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index)
@@ -2755,9 +2727,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index)
DEBUG_MSG;
}
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(sub)(unsigned int inst, int index)
@@ -2806,9 +2775,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(swp)(unsigned int inst, int index)
inst_cream->Rd = BITS(inst, 12, 15);
inst_cream->Rm = BITS(inst, 0, 3);
- if (inst_cream->Rd == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(unsigned int inst, int index){
@@ -2823,9 +2789,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(unsigned int inst, int index){
inst_cream->Rd = BITS(inst, 12, 15);
inst_cream->Rm = BITS(inst, 0, 3);
- if (inst_cream->Rd == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(unsigned int inst, int index){
@@ -2913,9 +2876,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index)
inst_cream->shifter_operand = BITS(inst, 0, 11);
inst_cream->shtop_func = get_shtop(inst);
- if (inst_cream->Rd == 15)
- inst_base->br = INDIRECT_BRANCH;
-
return inst_base;
}
@@ -3242,7 +3202,6 @@ const transop_fp_t arm_instruction_trans[] = {
INTERPRETER_TRANSLATE(vmla),
INTERPRETER_TRANSLATE(vmls),
INTERPRETER_TRANSLATE(vnmla),
- INTERPRETER_TRANSLATE(vnmla),
INTERPRETER_TRANSLATE(vnmls),
INTERPRETER_TRANSLATE(vnmul),
INTERPRETER_TRANSLATE(vmul),
@@ -3548,6 +3507,7 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
CITRA_IGNORE_EXIT(-1);
}
inst_base = arm_instruction_trans[idx](inst, idx);
+
translated:
phys_addr += inst_size;
@@ -3580,6 +3540,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
Common::Profiling::ScopeTimer timer_execute(profile_execute);
MICROPROFILE_SCOPE(DynCom_Execute);
+ GDBStub::BreakpointAddress breakpoint_data;
+
#undef RM
#undef RS
@@ -3604,224 +3566,235 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
#define INC_PC(l) ptr += sizeof(arm_inst) + l
#define INC_PC_STUB ptr += sizeof(arm_inst)
+#define GDB_BP_CHECK \
+ cpu->Cpsr &= ~(1 << 5); \
+ cpu->Cpsr |= cpu->TFlag << 5; \
+ if (GDBStub::g_server_enabled) { \
+ if (GDBStub::IsMemoryBreak() || (breakpoint_data.type != GDBStub::BreakpointType::None && PC == breakpoint_data.address)) { \
+ GDBStub::Break(); \
+ goto END; \
+ } \
+ }
+
// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
// clunky switch statement.
#if defined __GNUC__ || defined __clang__
#define GOTO_NEXT_INST \
+ GDB_BP_CHECK; \
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
num_instrs++; \
goto *InstLabel[inst_base->idx]
#else
#define GOTO_NEXT_INST \
+ GDB_BP_CHECK; \
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
num_instrs++; \
switch(inst_base->idx) { \
case 0: goto VMLA_INST; \
case 1: goto VMLS_INST; \
case 2: goto VNMLA_INST; \
- case 3: goto VNMLA_INST; \
- case 4: goto VNMLS_INST; \
- case 5: goto VNMUL_INST; \
- case 6: goto VMUL_INST; \
- case 7: goto VADD_INST; \
- case 8: goto VSUB_INST; \
- case 9: goto VDIV_INST; \
- case 10: goto VMOVI_INST; \
- case 11: goto VMOVR_INST; \
- case 12: goto VABS_INST; \
- case 13: goto VNEG_INST; \
- case 14: goto VSQRT_INST; \
- case 15: goto VCMP_INST; \
- case 16: goto VCMP2_INST; \
- case 17: goto VCVTBDS_INST; \
- case 18: goto VCVTBFF_INST; \
- case 19: goto VCVTBFI_INST; \
- case 20: goto VMOVBRS_INST; \
- case 21: goto VMSR_INST; \
- case 22: goto VMOVBRC_INST; \
- case 23: goto VMRS_INST; \
- case 24: goto VMOVBCR_INST; \
- case 25: goto VMOVBRRSS_INST; \
- case 26: goto VMOVBRRD_INST; \
- case 27: goto VSTR_INST; \
- case 28: goto VPUSH_INST; \
- case 29: goto VSTM_INST; \
- case 30: goto VPOP_INST; \
- case 31: goto VLDR_INST; \
- case 32: goto VLDM_INST ; \
- case 33: goto SRS_INST; \
- case 34: goto RFE_INST; \
- case 35: goto BKPT_INST; \
- case 36: goto BLX_INST; \
- case 37: goto CPS_INST; \
- case 38: goto PLD_INST; \
- case 39: goto SETEND_INST; \
- case 40: goto CLREX_INST; \
- case 41: goto REV16_INST; \
- case 42: goto USAD8_INST; \
- case 43: goto SXTB_INST; \
- case 44: goto UXTB_INST; \
- case 45: goto SXTH_INST; \
- case 46: goto SXTB16_INST; \
- case 47: goto UXTH_INST; \
- case 48: goto UXTB16_INST; \
- case 49: goto CPY_INST; \
- case 50: goto UXTAB_INST; \
- case 51: goto SSUB8_INST; \
- case 52: goto SHSUB8_INST; \
- case 53: goto SSUBADDX_INST; \
- case 54: goto STREX_INST; \
- case 55: goto STREXB_INST; \
- case 56: goto SWP_INST; \
- case 57: goto SWPB_INST; \
- case 58: goto SSUB16_INST; \
- case 59: goto SSAT16_INST; \
- case 60: goto SHSUBADDX_INST; \
- case 61: goto QSUBADDX_INST; \
- case 62: goto SHADDSUBX_INST; \
- case 63: goto SHADD8_INST; \
- case 64: goto SHADD16_INST; \
- case 65: goto SEL_INST; \
- case 66: goto SADDSUBX_INST; \
- case 67: goto SADD8_INST; \
- case 68: goto SADD16_INST; \
- case 69: goto SHSUB16_INST; \
- case 70: goto UMAAL_INST; \
- case 71: goto UXTAB16_INST; \
- case 72: goto USUBADDX_INST; \
- case 73: goto USUB8_INST; \
- case 74: goto USUB16_INST; \
- case 75: goto USAT16_INST; \
- case 76: goto USADA8_INST; \
- case 77: goto UQSUBADDX_INST; \
- case 78: goto UQSUB8_INST; \
- case 79: goto UQSUB16_INST; \
- case 80: goto UQADDSUBX_INST; \
- case 81: goto UQADD8_INST; \
- case 82: goto UQADD16_INST; \
- case 83: goto SXTAB_INST; \
- case 84: goto UHSUBADDX_INST; \
- case 85: goto UHSUB8_INST; \
- case 86: goto UHSUB16_INST; \
- case 87: goto UHADDSUBX_INST; \
- case 88: goto UHADD8_INST; \
- case 89: goto UHADD16_INST; \
- case 90: goto UADDSUBX_INST; \
- case 91: goto UADD8_INST; \
- case 92: goto UADD16_INST; \
- case 93: goto SXTAH_INST; \
- case 94: goto SXTAB16_INST; \
- case 95: goto QADD8_INST; \
- case 96: goto BXJ_INST; \
- case 97: goto CLZ_INST; \
- case 98: goto UXTAH_INST; \
- case 99: goto BX_INST; \
- case 100: goto REV_INST; \
- case 101: goto BLX_INST; \
- case 102: goto REVSH_INST; \
- case 103: goto QADD_INST; \
- case 104: goto QADD16_INST; \
- case 105: goto QADDSUBX_INST; \
- case 106: goto LDREX_INST; \
- case 107: goto QDADD_INST; \
- case 108: goto QDSUB_INST; \
- case 109: goto QSUB_INST; \
- case 110: goto LDREXB_INST; \
- case 111: goto QSUB8_INST; \
- case 112: goto QSUB16_INST; \
- case 113: goto SMUAD_INST; \
- case 114: goto SMMUL_INST; \
- case 115: goto SMUSD_INST; \
- case 116: goto SMLSD_INST; \
- case 117: goto SMLSLD_INST; \
- case 118: goto SMMLA_INST; \
- case 119: goto SMMLS_INST; \
- case 120: goto SMLALD_INST; \
- case 121: goto SMLAD_INST; \
- case 122: goto SMLAW_INST; \
- case 123: goto SMULW_INST; \
- case 124: goto PKHTB_INST; \
- case 125: goto PKHBT_INST; \
- case 126: goto SMUL_INST; \
- case 127: goto SMLALXY_INST; \
- case 128: goto SMLA_INST; \
- case 129: goto MCRR_INST; \
- case 130: goto MRRC_INST; \
- case 131: goto CMP_INST; \
- case 132: goto TST_INST; \
- case 133: goto TEQ_INST; \
- case 134: goto CMN_INST; \
- case 135: goto SMULL_INST; \
- case 136: goto UMULL_INST; \
- case 137: goto UMLAL_INST; \
- case 138: goto SMLAL_INST; \
- case 139: goto MUL_INST; \
- case 140: goto MLA_INST; \
- case 141: goto SSAT_INST; \
- case 142: goto USAT_INST; \
- case 143: goto MRS_INST; \
- case 144: goto MSR_INST; \
- case 145: goto AND_INST; \
- case 146: goto BIC_INST; \
- case 147: goto LDM_INST; \
- case 148: goto EOR_INST; \
- case 149: goto ADD_INST; \
- case 150: goto RSB_INST; \
- case 151: goto RSC_INST; \
- case 152: goto SBC_INST; \
- case 153: goto ADC_INST; \
- case 154: goto SUB_INST; \
- case 155: goto ORR_INST; \
- case 156: goto MVN_INST; \
- case 157: goto MOV_INST; \
- case 158: goto STM_INST; \
- case 159: goto LDM_INST; \
- case 160: goto LDRSH_INST; \
- case 161: goto STM_INST; \
- case 162: goto LDM_INST; \
- case 163: goto LDRSB_INST; \
- case 164: goto STRD_INST; \
- case 165: goto LDRH_INST; \
- case 166: goto STRH_INST; \
- case 167: goto LDRD_INST; \
- case 168: goto STRT_INST; \
- case 169: goto STRBT_INST; \
- case 170: goto LDRBT_INST; \
- case 171: goto LDRT_INST; \
- case 172: goto MRC_INST; \
- case 173: goto MCR_INST; \
+ case 3: goto VNMLS_INST; \
+ case 4: goto VNMUL_INST; \
+ case 5: goto VMUL_INST; \
+ case 6: goto VADD_INST; \
+ case 7: goto VSUB_INST; \
+ case 8: goto VDIV_INST; \
+ case 9: goto VMOVI_INST; \
+ case 10: goto VMOVR_INST; \
+ case 11: goto VABS_INST; \
+ case 12: goto VNEG_INST; \
+ case 13: goto VSQRT_INST; \
+ case 14: goto VCMP_INST; \
+ case 15: goto VCMP2_INST; \
+ case 16: goto VCVTBDS_INST; \
+ case 17: goto VCVTBFF_INST; \
+ case 18: goto VCVTBFI_INST; \
+ case 19: goto VMOVBRS_INST; \
+ case 20: goto VMSR_INST; \
+ case 21: goto VMOVBRC_INST; \
+ case 22: goto VMRS_INST; \
+ case 23: goto VMOVBCR_INST; \
+ case 24: goto VMOVBRRSS_INST; \
+ case 25: goto VMOVBRRD_INST; \
+ case 26: goto VSTR_INST; \
+ case 27: goto VPUSH_INST; \
+ case 28: goto VSTM_INST; \
+ case 29: goto VPOP_INST; \
+ case 30: goto VLDR_INST; \
+ case 31: goto VLDM_INST ; \
+ case 32: goto SRS_INST; \
+ case 33: goto RFE_INST; \
+ case 34: goto BKPT_INST; \
+ case 35: goto BLX_INST; \
+ case 36: goto CPS_INST; \
+ case 37: goto PLD_INST; \
+ case 38: goto SETEND_INST; \
+ case 39: goto CLREX_INST; \
+ case 40: goto REV16_INST; \
+ case 41: goto USAD8_INST; \
+ case 42: goto SXTB_INST; \
+ case 43: goto UXTB_INST; \
+ case 44: goto SXTH_INST; \
+ case 45: goto SXTB16_INST; \
+ case 46: goto UXTH_INST; \
+ case 47: goto UXTB16_INST; \
+ case 48: goto CPY_INST; \
+ case 49: goto UXTAB_INST; \
+ case 50: goto SSUB8_INST; \
+ case 51: goto SHSUB8_INST; \
+ case 52: goto SSUBADDX_INST; \
+ case 53: goto STREX_INST; \
+ case 54: goto STREXB_INST; \
+ case 55: goto SWP_INST; \
+ case 56: goto SWPB_INST; \
+ case 57: goto SSUB16_INST; \
+ case 58: goto SSAT16_INST; \
+ case 59: goto SHSUBADDX_INST; \
+ case 60: goto QSUBADDX_INST; \
+ case 61: goto SHADDSUBX_INST; \
+ case 62: goto SHADD8_INST; \
+ case 63: goto SHADD16_INST; \
+ case 64: goto SEL_INST; \
+ case 65: goto SADDSUBX_INST; \
+ case 66: goto SADD8_INST; \
+ case 67: goto SADD16_INST; \
+ case 68: goto SHSUB16_INST; \
+ case 69: goto UMAAL_INST; \
+ case 70: goto UXTAB16_INST; \
+ case 71: goto USUBADDX_INST; \
+ case 72: goto USUB8_INST; \
+ case 73: goto USUB16_INST; \
+ case 74: goto USAT16_INST; \
+ case 75: goto USADA8_INST; \
+ case 76: goto UQSUBADDX_INST; \
+ case 77: goto UQSUB8_INST; \
+ case 78: goto UQSUB16_INST; \
+ case 79: goto UQADDSUBX_INST; \
+ case 80: goto UQADD8_INST; \
+ case 81: goto UQADD16_INST; \
+ case 82: goto SXTAB_INST; \
+ case 83: goto UHSUBADDX_INST; \
+ case 84: goto UHSUB8_INST; \
+ case 85: goto UHSUB16_INST; \
+ case 86: goto UHADDSUBX_INST; \
+ case 87: goto UHADD8_INST; \
+ case 88: goto UHADD16_INST; \
+ case 89: goto UADDSUBX_INST; \
+ case 90: goto UADD8_INST; \
+ case 91: goto UADD16_INST; \
+ case 92: goto SXTAH_INST; \
+ case 93: goto SXTAB16_INST; \
+ case 94: goto QADD8_INST; \
+ case 95: goto BXJ_INST; \
+ case 96: goto CLZ_INST; \
+ case 97: goto UXTAH_INST; \
+ case 98: goto BX_INST; \
+ case 99: goto REV_INST; \
+ case 100: goto BLX_INST; \
+ case 101: goto REVSH_INST; \
+ case 102: goto QADD_INST; \
+ case 103: goto QADD16_INST; \
+ case 104: goto QADDSUBX_INST; \
+ case 105: goto LDREX_INST; \
+ case 106: goto QDADD_INST; \
+ case 107: goto QDSUB_INST; \
+ case 108: goto QSUB_INST; \
+ case 109: goto LDREXB_INST; \
+ case 110: goto QSUB8_INST; \
+ case 111: goto QSUB16_INST; \
+ case 112: goto SMUAD_INST; \
+ case 113: goto SMMUL_INST; \
+ case 114: goto SMUSD_INST; \
+ case 115: goto SMLSD_INST; \
+ case 116: goto SMLSLD_INST; \
+ case 117: goto SMMLA_INST; \
+ case 118: goto SMMLS_INST; \
+ case 119: goto SMLALD_INST; \
+ case 120: goto SMLAD_INST; \
+ case 121: goto SMLAW_INST; \
+ case 122: goto SMULW_INST; \
+ case 123: goto PKHTB_INST; \
+ case 124: goto PKHBT_INST; \
+ case 125: goto SMUL_INST; \
+ case 126: goto SMLALXY_INST; \
+ case 127: goto SMLA_INST; \
+ case 128: goto MCRR_INST; \
+ case 129: goto MRRC_INST; \
+ case 130: goto CMP_INST; \
+ case 131: goto TST_INST; \
+ case 132: goto TEQ_INST; \
+ case 133: goto CMN_INST; \
+ case 134: goto SMULL_INST; \
+ case 135: goto UMULL_INST; \
+ case 136: goto UMLAL_INST; \
+ case 137: goto SMLAL_INST; \
+ case 138: goto MUL_INST; \
+ case 139: goto MLA_INST; \
+ case 140: goto SSAT_INST; \
+ case 141: goto USAT_INST; \
+ case 142: goto MRS_INST; \
+ case 143: goto MSR_INST; \
+ case 144: goto AND_INST; \
+ case 145: goto BIC_INST; \
+ case 146: goto LDM_INST; \
+ case 147: goto EOR_INST; \
+ case 148: goto ADD_INST; \
+ case 149: goto RSB_INST; \
+ case 150: goto RSC_INST; \
+ case 151: goto SBC_INST; \
+ case 152: goto ADC_INST; \
+ case 153: goto SUB_INST; \
+ case 154: goto ORR_INST; \
+ case 155: goto MVN_INST; \
+ case 156: goto MOV_INST; \
+ case 157: goto STM_INST; \
+ case 158: goto LDM_INST; \
+ case 159: goto LDRSH_INST; \
+ case 160: goto STM_INST; \
+ case 161: goto LDM_INST; \
+ case 162: goto LDRSB_INST; \
+ case 163: goto STRD_INST; \
+ case 164: goto LDRH_INST; \
+ case 165: goto STRH_INST; \
+ case 166: goto LDRD_INST; \
+ case 167: goto STRT_INST; \
+ case 168: goto STRBT_INST; \
+ case 169: goto LDRBT_INST; \
+ case 170: goto LDRT_INST; \
+ case 171: goto MRC_INST; \
+ case 172: goto MCR_INST; \
+ case 173: goto MSR_INST; \
case 174: goto MSR_INST; \
case 175: goto MSR_INST; \
case 176: goto MSR_INST; \
case 177: goto MSR_INST; \
- case 178: goto MSR_INST; \
- case 179: goto LDRB_INST; \
- case 180: goto STRB_INST; \
- case 181: goto LDR_INST; \
- case 182: goto LDRCOND_INST ; \
- case 183: goto STR_INST; \
- case 184: goto CDP_INST; \
- case 185: goto STC_INST; \
- case 186: goto LDC_INST; \
- case 187: goto LDREXD_INST; \
- case 188: goto STREXD_INST; \
- case 189: goto LDREXH_INST; \
- case 190: goto STREXH_INST; \
- case 191: goto NOP_INST; \
- case 192: goto YIELD_INST; \
- case 193: goto WFE_INST; \
- case 194: goto WFI_INST; \
- case 195: goto SEV_INST; \
- case 196: goto SWI_INST; \
- case 197: goto BBL_INST; \
- case 198: goto B_2_THUMB ; \
- case 199: goto B_COND_THUMB ; \
- case 200: goto BL_1_THUMB ; \
- case 201: goto BL_2_THUMB ; \
- case 202: goto BLX_1_THUMB ; \
- case 203: goto DISPATCH; \
- case 204: goto INIT_INST_LENGTH; \
- case 205: goto END; \
+ case 178: goto LDRB_INST; \
+ case 179: goto STRB_INST; \
+ case 180: goto LDR_INST; \
+ case 181: goto LDRCOND_INST ; \
+ case 182: goto STR_INST; \
+ case 183: goto CDP_INST; \
+ case 184: goto STC_INST; \
+ case 185: goto LDC_INST; \
+ case 186: goto LDREXD_INST; \
+ case 187: goto STREXD_INST; \
+ case 188: goto LDREXH_INST; \
+ case 189: goto STREXH_INST; \
+ case 190: goto NOP_INST; \
+ case 191: goto YIELD_INST; \
+ case 192: goto WFE_INST; \
+ case 193: goto WFI_INST; \
+ case 194: goto SEV_INST; \
+ case 195: goto SWI_INST; \
+ case 196: goto BBL_INST; \
+ case 197: goto B_2_THUMB ; \
+ case 198: goto B_COND_THUMB ; \
+ case 199: goto BL_1_THUMB ; \
+ case 200: goto BL_2_THUMB ; \
+ case 201: goto BLX_1_THUMB ; \
+ case 202: goto DISPATCH; \
+ case 203: goto INIT_INST_LENGTH; \
+ case 204: goto END; \
}
#endif
@@ -3848,7 +3821,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
// to a clunky switch statement.
#if defined __GNUC__ || defined __clang__
void *InstLabel[] = {
- &&VMLA_INST, &&VMLS_INST, &&VNMLA_INST, &&VNMLA_INST, &&VNMLS_INST, &&VNMUL_INST, &&VMUL_INST, &&VADD_INST, &&VSUB_INST,
+ &&VMLA_INST, &&VMLS_INST, &&VNMLA_INST, &&VNMLS_INST, &&VNMUL_INST, &&VMUL_INST, &&VADD_INST, &&VSUB_INST,
&&VDIV_INST, &&VMOVI_INST, &&VMOVR_INST, &&VABS_INST, &&VNEG_INST, &&VSQRT_INST, &&VCMP_INST, &&VCMP2_INST, &&VCVTBDS_INST,
&&VCVTBFF_INST, &&VCVTBFI_INST, &&VMOVBRS_INST, &&VMSR_INST, &&VMOVBRC_INST, &&VMRS_INST, &&VMOVBCR_INST, &&VMOVBRRSS_INST,
&&VMOVBRRD_INST, &&VSTR_INST, &&VPUSH_INST, &&VSTM_INST, &&VPOP_INST, &&VLDR_INST, &&VLDM_INST,
@@ -3903,6 +3876,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
goto END;
}
+ // Find breakpoint if one exists within the block
+ if (GDBStub::g_server_enabled && GDBStub::IsConnected()) {
+ breakpoint_data = GDBStub::GetNextBreakpointFromAddress(cpu->Reg[15], GDBStub::BreakpointType::Execute);
+ }
+
inst_base = (arm_inst *)&inst_buf[ptr];
GOTO_NEXT_INST;
}
@@ -4454,12 +4432,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = Memory::Read8(addr);
-
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
+ cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4472,12 +4445,14 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = Memory::Read8(addr);
+ const u32 dest_index = BITS(inst_cream->inst, 12, 15);
+ const u32 previous_mode = cpu->Mode;
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
+ cpu->ChangePrivilegeMode(USER32MODE);
+ const u8 value = cpu->ReadMemory8(addr);
+ cpu->ChangePrivilegeMode(previous_mode);
+
+ cpu->Reg[dest_index] = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4513,10 +4488,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory32(read_addr);
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(generic_arm_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
@@ -4531,11 +4502,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
cpu->SetExclusiveMemoryAddress(read_addr);
- RD = Memory::Read8(read_addr);
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(generic_arm_inst));
- goto DISPATCH;
- }
+ RD = cpu->ReadMemory8(read_addr);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
@@ -4551,10 +4518,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory16(read_addr);
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(generic_arm_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
@@ -4571,11 +4534,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
RD = cpu->ReadMemory32(read_addr);
RD2 = cpu->ReadMemory32(read_addr + 4);
-
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(generic_arm_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
@@ -4589,10 +4547,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
inst_cream->get_addr(cpu, inst_cream->inst, addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory16(addr);
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4604,15 +4558,11 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- unsigned int value = Memory::Read8(addr);
+ unsigned int value = cpu->ReadMemory8(addr);
if (BIT(value, 7)) {
value |= 0xffffff00;
}
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4630,10 +4580,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
value |= 0xffff0000;
}
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4646,13 +4592,14 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- unsigned int value = cpu->ReadMemory32(addr);
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
+ const u32 dest_index = BITS(inst_cream->inst, 12, 15);
+ const u32 previous_mode = cpu->Mode;
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
+ cpu->ChangePrivilegeMode(USER32MODE);
+ const u32 value = cpu->ReadMemory32(addr);
+ cpu->ChangePrivilegeMode(previous_mode);
+
+ cpu->Reg[dest_index] = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4709,10 +4656,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
}
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(mla_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mla_inst));
@@ -4751,18 +4694,15 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mrc_inst* inst_cream = (mrc_inst*)inst_base->component;
- unsigned int inst = inst_cream->inst;
- if (inst_cream->Rd == 15) {
- DEBUG_MSG;
- }
- if (inst_cream->inst == 0xeef04a10) {
- // Undefined instruction fmrx
- RD = 0x20000000;
- CITRA_IGNORE_EXIT(-1);
- goto END;
- } else {
- if (inst_cream->cp_num == 15)
- RD = cpu->ReadCP15Register(CRn, OPCODE_1, CRm, OPCODE_2);
+ if (inst_cream->cp_num == 15) {
+ const uint32_t value = cpu->ReadCP15Register(CRn, OPCODE_1, CRm, OPCODE_2);
+
+ if (inst_cream->Rd == 15) {
+ cpu->Cpsr = (cpu->Cpsr & ~0xF0000000) | (value & 0xF0000000);
+ LOAD_NZCVT;
+ } else {
+ RD = value;
+ }
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
@@ -4861,10 +4801,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
}
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(mul_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mul_inst));
@@ -6027,7 +5963,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
- Memory::Write8(addr, value);
+ cpu->WriteMemory8(addr, value);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -6039,8 +5975,13 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
- Memory::Write8(addr, value);
+
+ const u32 previous_mode = cpu->Mode;
+ const u32 value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
+
+ cpu->ChangePrivilegeMode(USER32MODE);
+ cpu->WriteMemory8(addr, value);
+ cpu->ChangePrivilegeMode(previous_mode);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -6091,7 +6032,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
if (cpu->IsExclusiveMemoryAccess(write_addr)) {
cpu->UnsetExclusiveMemoryAddress();
- Memory::Write8(write_addr, cpu->Reg[inst_cream->Rm]);
+ cpu->WriteMemory8(write_addr, cpu->Reg[inst_cream->Rm]);
RD = 0;
} else {
// Failed to write due to mutex access
@@ -6174,8 +6115,16 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)];
+ const u32 previous_mode = cpu->Mode;
+ const u32 rt_index = BITS(inst_cream->inst, 12, 15);
+
+ u32 value = cpu->Reg[rt_index];
+ if (rt_index == 15)
+ value += 2 * cpu->GetInstructionSize();
+
+ cpu->ChangePrivilegeMode(USER32MODE);
cpu->WriteMemory32(addr, value);
+ cpu->ChangePrivilegeMode(previous_mode);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -6250,8 +6199,8 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
swp_inst* inst_cream = (swp_inst*)inst_base->component;
addr = RN;
- unsigned int value = Memory::Read8(addr);
- Memory::Write8(addr, (RM & 0xFF));
+ unsigned int value = cpu->ReadMemory8(addr);
+ cpu->WriteMemory8(addr, (RM & 0xFF));
RD = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
diff --git a/src/core/arm/dyncom/arm_dyncom_run.h b/src/core/arm/dyncom/arm_dyncom_run.h
index 13bef17fc..8eb694fee 100644
--- a/src/core/arm/dyncom/arm_dyncom_run.h
+++ b/src/core/arm/dyncom/arm_dyncom_run.h
@@ -30,7 +30,7 @@
* @return If the PC is being read, then the word-aligned PC value is returned.
* If the PC is not being read, then the value stored in the register is returned.
*/
-static inline u32 CHECK_READ_REG15_WA(ARMul_State* cpu, int Rn) {
+inline u32 CHECK_READ_REG15_WA(const ARMul_State* cpu, int Rn) {
return (Rn == 15) ? ((cpu->Reg[15] & ~0x3) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn];
}
@@ -43,6 +43,6 @@ static inline u32 CHECK_READ_REG15_WA(ARMul_State* cpu, int Rn) {
* @return If the PC is being read, then the incremented PC value is returned.
* If the PC is not being read, then the values stored in the register is returned.
*/
-static inline u32 CHECK_READ_REG15(ARMul_State* cpu, int Rn) {
+inline u32 CHECK_READ_REG15(const ARMul_State* cpu, int Rn) {
return (Rn == 15) ? ((cpu->Reg[15] & ~0x1) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn];
}
diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.h b/src/core/arm/dyncom/arm_dyncom_thumb.h
index 447974363..c1be3c735 100644
--- a/src/core/arm/dyncom/arm_dyncom_thumb.h
+++ b/src/core/arm/dyncom/arm_dyncom_thumb.h
@@ -38,7 +38,7 @@ enum class ThumbDecodeStatus {
// Translates a Thumb mode instruction into its ARM equivalent.
ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u32* inst_size);
-static inline u32 GetThumbInstruction(u32 instr, u32 address) {
+inline u32 GetThumbInstruction(u32 instr, u32 address) {
// Normally you would need to handle instruction endianness,
// however, it is fixed to little-endian on the MPCore, so
// there's no need to check for this beforehand.
diff --git a/src/core/arm/skyeye_common/armstate.cpp b/src/core/arm/skyeye_common/armstate.cpp
index 0491717dc..2d814345a 100644
--- a/src/core/arm/skyeye_common/armstate.cpp
+++ b/src/core/arm/skyeye_common/armstate.cpp
@@ -7,6 +7,7 @@
#include "core/memory.h"
#include "core/arm/skyeye_common/armstate.h"
#include "core/arm/skyeye_common/vfp/vfp.h"
+#include "core/gdbstub/gdbstub.h"
ARMul_State::ARMul_State(PrivilegeMode initial_mode)
{
@@ -185,8 +186,25 @@ void ARMul_State::ResetMPCoreCP15Registers()
CP15[CP15_TLB_DEBUG_CONTROL] = 0x00000000;
}
+static void CheckMemoryBreakpoint(u32 address, GDBStub::BreakpointType type)
+{
+ if (GDBStub::g_server_enabled && GDBStub::CheckBreakpoint(address, type)) {
+ LOG_DEBUG(Debug, "Found memory breakpoint @ %08x", address);
+ GDBStub::Break(true);
+ }
+}
+
+u8 ARMul_State::ReadMemory8(u32 address) const
+{
+ CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
+
+ return Memory::Read8(address);
+}
+
u16 ARMul_State::ReadMemory16(u32 address) const
{
+ CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
+
u16 data = Memory::Read16(address);
if (InBigEndianMode())
@@ -197,6 +215,8 @@ u16 ARMul_State::ReadMemory16(u32 address) const
u32 ARMul_State::ReadMemory32(u32 address) const
{
+ CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
+
u32 data = Memory::Read32(address);
if (InBigEndianMode())
@@ -207,6 +227,8 @@ u32 ARMul_State::ReadMemory32(u32 address) const
u64 ARMul_State::ReadMemory64(u32 address) const
{
+ CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Read);
+
u64 data = Memory::Read64(address);
if (InBigEndianMode())
@@ -215,8 +237,17 @@ u64 ARMul_State::ReadMemory64(u32 address) const
return data;
}
+void ARMul_State::WriteMemory8(u32 address, u8 data)
+{
+ CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
+
+ Memory::Write8(address, data);
+}
+
void ARMul_State::WriteMemory16(u32 address, u16 data)
{
+ CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
+
if (InBigEndianMode())
data = Common::swap16(data);
@@ -225,6 +256,8 @@ void ARMul_State::WriteMemory16(u32 address, u16 data)
void ARMul_State::WriteMemory32(u32 address, u32 data)
{
+ CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
+
if (InBigEndianMode())
data = Common::swap32(data);
@@ -233,6 +266,8 @@ void ARMul_State::WriteMemory32(u32 address, u32 data)
void ARMul_State::WriteMemory64(u32 address, u64 data)
{
+ CheckMemoryBreakpoint(address, GDBStub::BreakpointType::Write);
+
if (InBigEndianMode())
data = Common::swap64(data);
diff --git a/src/core/arm/skyeye_common/armstate.h b/src/core/arm/skyeye_common/armstate.h
index ceb159d14..d42ff2669 100644
--- a/src/core/arm/skyeye_common/armstate.h
+++ b/src/core/arm/skyeye_common/armstate.h
@@ -153,9 +153,11 @@ public:
// Reads/writes data in big/little endian format based on the
// state of the E (endian) bit in the APSR.
+ u8 ReadMemory8(u32 address) const;
u16 ReadMemory16(u32 address) const;
u32 ReadMemory32(u32 address) const;
u64 ReadMemory64(u32 address) const;
+ void WriteMemory8(u32 address, u8 data);
void WriteMemory16(u32 address, u16 data);
void WriteMemory32(u32 address, u32 data);
void WriteMemory64(u32 address, u64 data);
@@ -191,23 +193,23 @@ public:
return TFlag ? 2 : 4;
}
- std::array<u32, 16> Reg; // The current register file
- std::array<u32, 2> Reg_usr;
- std::array<u32, 2> Reg_svc; // R13_SVC R14_SVC
- std::array<u32, 2> Reg_abort; // R13_ABORT R14_ABORT
- std::array<u32, 2> Reg_undef; // R13 UNDEF R14 UNDEF
- std::array<u32, 2> Reg_irq; // R13_IRQ R14_IRQ
- std::array<u32, 7> Reg_firq; // R8---R14 FIRQ
- std::array<u32, 7> Spsr; // The exception psr's
- std::array<u32, CP15_REGISTER_COUNT> CP15;
+ std::array<u32, 16> Reg{}; // The current register file
+ std::array<u32, 2> Reg_usr{};
+ std::array<u32, 2> Reg_svc{}; // R13_SVC R14_SVC
+ std::array<u32, 2> Reg_abort{}; // R13_ABORT R14_ABORT
+ std::array<u32, 2> Reg_undef{}; // R13 UNDEF R14 UNDEF
+ std::array<u32, 2> Reg_irq{}; // R13_IRQ R14_IRQ
+ std::array<u32, 7> Reg_firq{}; // R8---R14 FIRQ
+ std::array<u32, 7> Spsr{}; // The exception psr's
+ std::array<u32, CP15_REGISTER_COUNT> CP15{};
// FPSID, FPSCR, and FPEXC
- std::array<u32, VFP_SYSTEM_REGISTER_COUNT> VFP;
+ std::array<u32, VFP_SYSTEM_REGISTER_COUNT> VFP{};
// VFPv2 and VFPv3-D16 has 16 doubleword registers (D0-D16 or S0-S31).
// VFPv3-D32/ASIMD may have up to 32 doubleword registers (D0-D31),
// and only 32 singleword registers are accessible (S0-S31).
- std::array<u32, 64> ExtReg;
+ std::array<u32, 64> ExtReg{};
u32 Emulate; // To start and stop emulation
u32 Cpsr; // The current PSR
diff --git a/src/core/arm/skyeye_common/vfp/vfp.cpp b/src/core/arm/skyeye_common/vfp/vfp.cpp
index 0537135e2..a27a7e194 100644
--- a/src/core/arm/skyeye_common/vfp/vfp.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfp.cpp
@@ -113,26 +113,26 @@ void VMOVR(ARMul_State* state, u32 single, u32 d, u32 m)
/* Miscellaneous functions */
s32 vfp_get_float(ARMul_State* state, unsigned int reg)
{
- LOG_TRACE(Core_ARM11, "VFP get float: s%d=[%08x]\n", reg, state->ExtReg[reg]);
+ LOG_TRACE(Core_ARM11, "VFP get float: s%d=[%08x]", reg, state->ExtReg[reg]);
return state->ExtReg[reg];
}
void vfp_put_float(ARMul_State* state, s32 val, unsigned int reg)
{
- LOG_TRACE(Core_ARM11, "VFP put float: s%d <= [%08x]\n", reg, val);
+ LOG_TRACE(Core_ARM11, "VFP put float: s%d <= [%08x]", reg, val);
state->ExtReg[reg] = val;
}
u64 vfp_get_double(ARMul_State* state, unsigned int reg)
{
u64 result = ((u64) state->ExtReg[reg*2+1])<<32 | state->ExtReg[reg*2];
- LOG_TRACE(Core_ARM11, "VFP get double: s[%d-%d]=[%016llx]\n", reg * 2 + 1, reg * 2, result);
+ LOG_TRACE(Core_ARM11, "VFP get double: s[%d-%d]=[%016llx]", reg * 2 + 1, reg * 2, result);
return result;
}
void vfp_put_double(ARMul_State* state, u64 val, unsigned int reg)
{
- LOG_TRACE(Core_ARM11, "VFP put double: s[%d-%d] <= [%08x-%08x]\n", reg * 2 + 1, reg * 2, (u32)(val >> 32), (u32)(val & 0xffffffff));
+ LOG_TRACE(Core_ARM11, "VFP put double: s[%d-%d] <= [%08x-%08x]", reg * 2 + 1, reg * 2, (u32)(val >> 32), (u32)(val & 0xffffffff));
state->ExtReg[reg*2] = (u32) (val & 0xffffffff);
state->ExtReg[reg*2+1] = (u32) (val>>32);
}
@@ -142,10 +142,10 @@ void vfp_put_double(ARMul_State* state, u64 val, unsigned int reg)
*/
void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x\n", exceptions);
+ LOG_TRACE(Core_ARM11, "VFP: raising exceptions %08x", exceptions);
if (exceptions == VFP_EXCEPTION_ERROR) {
- LOG_CRITICAL(Core_ARM11, "unhandled bounce %x\n", inst);
+ LOG_CRITICAL(Core_ARM11, "unhandled bounce %x", inst);
Crash();
}
diff --git a/src/core/arm/skyeye_common/vfp/vfp.h b/src/core/arm/skyeye_common/vfp/vfp.h
index 88908da9f..60a63e6de 100644
--- a/src/core/arm/skyeye_common/vfp/vfp.h
+++ b/src/core/arm/skyeye_common/vfp/vfp.h
@@ -22,7 +22,7 @@
#include "core/arm/skyeye_common/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
-#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested\n", __FUNCTION__);
+#define VFP_DEBUG_UNTESTED(x) LOG_TRACE(Core_ARM11, "in func %s, " #x " untested", __FUNCTION__);
#define CHECK_VFP_ENABLED
#define CHECK_VFP_CDP_RET vfp_raise_exceptions(cpu, ret, inst_cream->instr, cpu->VFP[VFP_FPSCR]);
diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h
index 91a8d4d57..210972917 100644
--- a/src/core/arm/skyeye_common/vfp/vfp_helper.h
+++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h
@@ -85,7 +85,7 @@ enum : u32 {
#define vfp_single(inst) (((inst) & 0x0000f00) == 0xa00)
-static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
+inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
{
if (shift) {
if (shift < 32)
@@ -96,7 +96,7 @@ static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
return val;
}
-static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
+inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
{
if (shift) {
if (shift < 64)
@@ -107,7 +107,7 @@ static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
return val;
}
-static inline u32 vfp_hi64to32jamming(u64 val)
+inline u32 vfp_hi64to32jamming(u64 val)
{
u32 v;
u32 highval = val >> 32;
@@ -121,7 +121,7 @@ static inline u32 vfp_hi64to32jamming(u64 val)
return v;
}
-static inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
+inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
{
*resl = nl + ml;
*resh = nh + mh;
@@ -129,7 +129,7 @@ static inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
*resh += 1;
}
-static inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
+inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
{
*resl = nl - ml;
*resh = nh - mh;
@@ -137,7 +137,7 @@ static inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
*resh -= 1;
}
-static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
+inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
{
u32 nh, nl, mh, ml;
u64 rh, rma, rmb, rl;
@@ -164,20 +164,20 @@ static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
*resh = rh;
}
-static inline void shift64left(u64* resh, u64* resl, u64 n)
+inline void shift64left(u64* resh, u64* resl, u64 n)
{
*resh = n >> 63;
*resl = n << 1;
}
-static inline u64 vfp_hi64multiply64(u64 n, u64 m)
+inline u64 vfp_hi64multiply64(u64 n, u64 m)
{
u64 rh, rl;
mul64to128(&rh, &rl, n, m);
return rh | (rl != 0);
}
-static inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
+inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
{
u64 mh, ml, remh, reml, termh, terml, z;
@@ -249,7 +249,7 @@ enum : u32 {
VFP_SNAN = (VFP_NAN|VFP_NAN_SIGNAL)
};
-static inline int vfp_single_type(vfp_single* s)
+inline int vfp_single_type(const vfp_single* s)
{
int type = VFP_NUMBER;
if (s->exponent == 255) {
@@ -271,7 +271,7 @@ static inline int vfp_single_type(vfp_single* s)
// Unpack a single-precision float. Note that this returns the magnitude
// of the single-precision float mantissa with the 1. if necessary,
// aligned to bit 30.
-static inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
+inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
{
s->sign = vfp_single_packed_sign(val) >> 16,
s->exponent = vfp_single_packed_exponent(val);
@@ -293,7 +293,7 @@ static inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
// Re-pack a single-precision float. This assumes that the float is
// already normalised such that the MSB is bit 30, _not_ bit 31.
-static inline s32 vfp_single_pack(vfp_single* s)
+inline s32 vfp_single_pack(const vfp_single* s)
{
u32 val = (s->sign << 16) +
(s->exponent << VFP_SINGLE_MANTISSA_BITS) +
@@ -335,7 +335,7 @@ struct vfp_double {
#define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1))
#define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1))
-static inline int vfp_double_type(vfp_double* s)
+inline int vfp_double_type(const vfp_double* s)
{
int type = VFP_NUMBER;
if (s->exponent == 2047) {
@@ -357,7 +357,7 @@ static inline int vfp_double_type(vfp_double* s)
// Unpack a double-precision float. Note that this returns the magnitude
// of the double-precision float mantissa with the 1. if necessary,
// aligned to bit 62.
-static inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
+inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
{
s->sign = vfp_double_packed_sign(val) >> 48;
s->exponent = vfp_double_packed_exponent(val);
@@ -379,7 +379,7 @@ static inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
// Re-pack a double-precision float. This assumes that the float is
// already normalised such that the MSB is bit 30, _not_ bit 31.
-static inline s64 vfp_double_pack(vfp_double* s)
+inline s64 vfp_double_pack(const vfp_double* s)
{
u64 val = ((u64)s->sign << 48) +
((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) +
@@ -415,7 +415,7 @@ struct op {
u32 flags;
};
-static inline u32 fls(u32 x)
+inline u32 fls(u32 x)
{
int r = 32;
diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
index 857e6ce45..45914d479 100644
--- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp
@@ -65,7 +65,7 @@ static struct vfp_double vfp_double_default_qnan = {
static void vfp_double_dump(const char *str, struct vfp_double *d)
{
- LOG_TRACE(Core_ARM11, "VFP: %s: sign=%d exponent=%d significand=%016llx\n",
+ LOG_TRACE(Core_ARM11, "VFP: %s: sign=%d exponent=%d significand=%016llx",
str, d->sign != 0, d->exponent, d->significand);
}
@@ -155,7 +155,7 @@ u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd,
} else if ((rmode == FPSCR_ROUND_PLUSINF) ^ (vd->sign != 0))
incr = (1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1;
- LOG_TRACE(Core_ARM11, "VFP: rounding increment = 0x%08llx\n", incr);
+ LOG_TRACE(Core_ARM11, "VFP: rounding increment = 0x%08llx", incr);
/*
* Is our rounding going to overflow?
@@ -210,7 +210,7 @@ pack:
vfp_double_dump("pack: final", vd);
{
s64 d = vfp_double_pack(vd);
- LOG_TRACE(Core_ARM11, "VFP: %s: d(d%d)=%016llx exceptions=%08x\n", func,
+ LOG_TRACE(Core_ARM11, "VFP: %s: d(d%d)=%016llx exceptions=%08x", func,
dd, d, exceptions);
vfp_put_double(state, d, dd);
}
@@ -267,28 +267,28 @@ vfp_propagate_nan(struct vfp_double *vdd, struct vfp_double *vdn,
*/
static u32 vfp_double_fabs(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_put_double(state, vfp_double_packed_abs(vfp_get_double(state, dm)), dd);
return 0;
}
static u32 vfp_double_fcpy(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_put_double(state, vfp_get_double(state, dm), dd);
return 0;
}
static u32 vfp_double_fneg(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_put_double(state, vfp_double_packed_negate(vfp_get_double(state, dm)), dd);
return 0;
}
static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double vdm, vdd, *vdp;
int ret, tm;
@@ -383,7 +383,7 @@ static u32 vfp_compare(ARMul_State* state, int dd, int signal_on_qnan, int dm, u
s64 d, m;
u32 ret = 0;
- LOG_TRACE(Core_ARM11, "In %s, state=0x%p, fpscr=0x%x\n", __FUNCTION__, state, fpscr);
+ LOG_TRACE(Core_ARM11, "In %s, state=0x%p, fpscr=0x%x", __FUNCTION__, state, fpscr);
m = vfp_get_double(state, dm);
if (vfp_double_packed_exponent(m) == 2047 && vfp_double_packed_mantissa(m)) {
ret |= FPSCR_CFLAG | FPSCR_VFLAG;
@@ -438,32 +438,32 @@ static u32 vfp_compare(ARMul_State* state, int dd, int signal_on_qnan, int dm, u
ret |= FPSCR_CFLAG;
}
}
- LOG_TRACE(Core_ARM11, "In %s, state=0x%p, ret=0x%x\n", __FUNCTION__, state, ret);
+ LOG_TRACE(Core_ARM11, "In %s, state=0x%p, ret=0x%x", __FUNCTION__, state, ret);
return ret;
}
static u32 vfp_double_fcmp(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_compare(state, dd, 0, dm, fpscr);
}
static u32 vfp_double_fcmpe(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_compare(state, dd, 1, dm, fpscr);
}
static u32 vfp_double_fcmpz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_compare(state, dd, 0, VFP_REG_ZERO, fpscr);
}
static u32 vfp_double_fcmpez(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_compare(state, dd, 1, VFP_REG_ZERO, fpscr);
}
@@ -474,7 +474,7 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32
int tm;
u32 exceptions = 0;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
tm = vfp_double_type(&vdm);
@@ -516,7 +516,7 @@ static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32
struct vfp_double vdm;
u32 m = vfp_get_float(state, dm);
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vdm.sign = 0;
vdm.exponent = 1023 + 63 - 1;
vdm.significand = (u64)m;
@@ -529,7 +529,7 @@ static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32
struct vfp_double vdm;
u32 m = vfp_get_float(state, dm);
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vdm.sign = (m & 0x80000000) >> 16;
vdm.exponent = 1023 + 63 - 1;
vdm.significand = vdm.sign ? (~m + 1) : m;
@@ -544,7 +544,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
int rmode = fpscr & FPSCR_RMODE_MASK;
int tm;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
/*
@@ -605,7 +605,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
}
}
- LOG_TRACE(Core_ARM11, "VFP: ftoui: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+ LOG_TRACE(Core_ARM11, "VFP: ftoui: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
vfp_put_float(state, d, sd);
@@ -614,7 +614,7 @@ static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32
static u32 vfp_double_ftouiz(ARMul_State* state, int sd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_ftoui(state, sd, unused, dm, FPSCR_ROUND_TOZERO);
}
@@ -625,7 +625,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
int rmode = fpscr & FPSCR_RMODE_MASK;
int tm;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
vfp_double_dump("VDM", &vdm);
@@ -682,7 +682,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
}
}
- LOG_TRACE(Core_ARM11, "VFP: ftosi: d(s%d)=%08x exceptions=%08x\n", sd, d, exceptions);
+ LOG_TRACE(Core_ARM11, "VFP: ftosi: d(s%d)=%08x exceptions=%08x", sd, d, exceptions);
vfp_put_float(state, (s32)d, sd);
@@ -691,7 +691,7 @@ static u32 vfp_double_ftosi(ARMul_State* state, int sd, int unused, int dm, u32
static u32 vfp_double_ftosiz(ARMul_State* state, int dd, int unused, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_ftosi(state, dd, unused, dm, FPSCR_ROUND_TOZERO);
}
@@ -775,7 +775,7 @@ u32 vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,struct vfp_dou
if (vdn->significand & (1ULL << 63) ||
vdm->significand & (1ULL << 63)) {
- LOG_INFO(Core_ARM11, "VFP: bad FP values in %s\n", __func__);
+ LOG_INFO(Core_ARM11, "VFP: bad FP values in %s", __func__);
vfp_double_dump("VDN", vdn);
vfp_double_dump("VDM", vdm);
}
@@ -843,7 +843,7 @@ vfp_double_multiply(struct vfp_double *vdd, struct vfp_double *vdn,
*/
if (vdn->exponent < vdm->exponent) {
std::swap(vdm, vdn);
- LOG_TRACE(Core_ARM11, "VFP: swapping M <-> N\n");
+ LOG_TRACE(Core_ARM11, "VFP: swapping M <-> N");
}
vdd->sign = vdn->sign ^ vdm->sign;
@@ -927,7 +927,7 @@ vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, int dm, u32 f
*/
static u32 vfp_double_fmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, 0, "fmac");
}
@@ -936,7 +936,7 @@ static u32 vfp_double_fmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
*/
static u32 vfp_double_fnmac(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_MULTIPLY, "fnmac");
}
@@ -945,7 +945,7 @@ static u32 vfp_double_fnmac(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
*/
static u32 vfp_double_fmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT, "fmsc");
}
@@ -954,7 +954,7 @@ static u32 vfp_double_fmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
*/
static u32 vfp_double_fnmsc(ARMul_State* state, int dd, int dn, int dm, u32 fpscr)
{
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
return vfp_double_multiply_accumulate(state, dd, dn, dm, fpscr, NEG_SUBTRACT | NEG_MULTIPLY, "fnmsc");
}
@@ -966,7 +966,7 @@ static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
struct vfp_double vdd, vdn, vdm;
u32 exceptions;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
@@ -987,7 +987,7 @@ static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpsc
struct vfp_double vdd, vdn, vdm;
u32 exceptions;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
@@ -1010,7 +1010,7 @@ static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
struct vfp_double vdd, vdn, vdm;
u32 exceptions;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
@@ -1032,7 +1032,7 @@ static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
struct vfp_double vdd, vdn, vdm;
u32 exceptions;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
if (vdn.exponent == 0 && vdn.significand)
vfp_double_normalise_denormal(&vdn);
@@ -1060,7 +1060,7 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr
u32 exceptions = 0;
int tm, tn;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vfp_double_unpack(&vdn, vfp_get_double(state, dn), &fpscr);
vfp_double_unpack(&vdm, vfp_get_double(state, dm), &fpscr);
@@ -1185,7 +1185,7 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
unsigned int vecitr, veclen, vecstride;
struct op *fop;
- LOG_TRACE(Core_ARM11, "In %s\n", __FUNCTION__);
+ LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__);
vecstride = (1 + ((fpscr & FPSCR_STRIDE_MASK) == FPSCR_STRIDE_MASK));
fop = (op == FOP_EXT) ? &fops_ext[FEXT_TO_IDX(inst)] : &fops[FOP_TO_IDX(op)];
@@ -1216,7 +1216,7 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
else
veclen = fpscr & FPSCR_LENGTH_MASK;
- LOG_TRACE(Core_ARM11, "VFP: vecstride=%u veclen=%u\n", vecstride,
+ LOG_TRACE(Core_ARM11, "VFP: vecstride=%u veclen=%u", vecstride,
(veclen >> FPSCR_LENGTH_BIT) + 1);
if (!fop->fn) {
@@ -1230,16 +1230,16 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr)
type = (fop->flags & OP_SD) ? 's' : 'd';
if (op == FOP_EXT)
- LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = op[%u] (d%u)\n",
+ LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = op[%u] (d%u)",
vecitr >> FPSCR_LENGTH_BIT,
type, dest, dn, dm);
else
- LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)\n",
+ LOG_TRACE(Core_ARM11, "VFP: itr%d (%c%u) = (d%u) op[%u] (d%u)",
vecitr >> FPSCR_LENGTH_BIT,
type, dest, dn, FOP_TO_IDX(op), dm);
except = fop->fn(state, dest, dn, dm, fpscr);
- LOG_TRACE(Core_ARM11, "VFP: itr%d: exceptions=%08x\n",
+ LOG_TRACE(Core_ARM11, "VFP: itr%d: exceptions=%08x",
vecitr >> FPSCR_LENGTH_BIT, except);
exceptions |= except;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index dddc16708..453c7162d 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -2,6 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <memory>
+
+#include "common/make_unique.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -13,13 +16,30 @@
#include "core/hle/kernel/thread.h"
#include "core/hw/hw.h"
+#include "core/gdbstub/gdbstub.h"
+
namespace Core {
-ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
-ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
+std::unique_ptr<ARM_Interface> g_app_core; ///< ARM11 application core
+std::unique_ptr<ARM_Interface> g_sys_core; ///< ARM11 system (OS) core
/// Run the core CPU loop
void RunLoop(int tight_loop) {
+ if (GDBStub::g_server_enabled) {
+ GDBStub::HandlePacket();
+
+ // If the loop is halted and we want to step, use a tiny (1) number of instructions to execute.
+ // Otherwise get out of the loop function.
+ if (GDBStub::GetCpuHaltFlag()) {
+ if (GDBStub::GetCpuStepFlag()) {
+ GDBStub::SetCpuStepFlag(false);
+ tight_loop = 1;
+ } else {
+ return;
+ }
+ }
+ }
+
// If we don't have a currently active thread then don't execute instructions,
// instead advance to the next event and try to yield to the next thread
if (Kernel::GetCurrentThread() == nullptr) {
@@ -54,16 +74,16 @@ void Stop() {
/// Initialize the core
int Init() {
- g_sys_core = new ARM_DynCom(USER32MODE);
- g_app_core = new ARM_DynCom(USER32MODE);
+ g_sys_core = Common::make_unique<ARM_DynCom>(USER32MODE);
+ g_app_core = Common::make_unique<ARM_DynCom>(USER32MODE);
LOG_DEBUG(Core, "Initialized OK");
return 0;
}
void Shutdown() {
- delete g_app_core;
- delete g_sys_core;
+ g_app_core.reset();
+ g_sys_core.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
diff --git a/src/core/core.h b/src/core/core.h
index 278f0f1cc..453e0a5f0 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -4,6 +4,7 @@
#pragma once
+#include <memory>
#include "common/common_types.h"
class ARM_Interface;
@@ -18,13 +19,13 @@ struct ThreadContext {
u32 lr;
u32 pc;
u32 cpsr;
- u32 fpu_registers[32];
+ u32 fpu_registers[64];
u32 fpscr;
u32 fpexc;
};
-extern ARM_Interface* g_app_core; ///< ARM11 application core
-extern ARM_Interface* g_sys_core; ///< ARM11 system (OS) core
+extern std::unique_ptr<ARM_Interface> g_app_core; ///< ARM11 application core
+extern std::unique_ptr<ARM_Interface> g_sys_core; ///< ARM11 system (OS) core
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
index c6a1be79d..e7a59a1ed 100644
--- a/src/core/file_sys/archive_backend.h
+++ b/src/core/file_sys/archive_backend.h
@@ -131,6 +131,12 @@ public:
* @return Opened directory, or nullptr
*/
virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0;
+
+ /**
+ * Get the free space
+ * @return The number of free bytes in the archive
+ */
+ virtual u64 GetFreeBytes() const = 0;
};
class ArchiveFactory : NonCopyable {
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index e9ecd2b1c..0ba502200 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -74,6 +74,11 @@ std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) c
return std::move(directory);
}
+u64 DiskArchive::GetFreeBytes() const {
+ // TODO: Stubbed to return 1GiB
+ return 1024 * 1024 * 1024;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode) {
diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h
index aaac65b17..ef9a98057 100644
--- a/src/core/file_sys/disk_archive.h
+++ b/src/core/file_sys/disk_archive.h
@@ -41,6 +41,7 @@ public:
bool CreateDirectory(const Path& path) const override;
bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
+ u64 GetFreeBytes() const override;
protected:
friend class DiskFile;
diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp
index 441ca9b53..2efc31a8c 100644
--- a/src/core/file_sys/ivfc_archive.cpp
+++ b/src/core/file_sys/ivfc_archive.cpp
@@ -59,6 +59,11 @@ std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) c
return Common::make_unique<IVFCDirectory>();
}
+u64 IVFCArchive::GetFreeBytes() const {
+ LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive");
+ return 0;
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h
index c15a6c4ae..f3fd82de4 100644
--- a/src/core/file_sys/ivfc_archive.h
+++ b/src/core/file_sys/ivfc_archive.h
@@ -42,6 +42,7 @@ public:
bool CreateDirectory(const Path& path) const override;
bool RenameDirectory(const Path& src_path, const Path& dest_path) const override;
std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override;
+ u64 GetFreeBytes() const override;
protected:
std::shared_ptr<FileUtil::IOFile> romfs_file;
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
new file mode 100644
index 000000000..3a2445241
--- /dev/null
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -0,0 +1,960 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
+
+#include <algorithm>
+#include <climits>
+#include <csignal>
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+#include <fcntl.h>
+#include <map>
+#include <numeric>
+
+#ifdef _MSC_VER
+#include <WinSock2.h>
+#include <ws2tcpip.h>
+#include <common/x64/abi.h>
+#include <io.h>
+#include <iphlpapi.h>
+#define SHUT_RDWR 2
+#else
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#endif
+
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/memory.h"
+#include "core/arm/arm_interface.h"
+#include "core/gdbstub/gdbstub.h"
+
+const int GDB_BUFFER_SIZE = 10000;
+
+const char GDB_STUB_START = '$';
+const char GDB_STUB_END = '#';
+const char GDB_STUB_ACK = '+';
+const char GDB_STUB_NACK = '-';
+
+#ifndef SIGTRAP
+const u32 SIGTRAP = 5;
+#endif
+
+#ifndef SIGTERM
+const u32 SIGTERM = 15;
+#endif
+
+#ifndef MSG_WAITALL
+const u32 MSG_WAITALL = 8;
+#endif
+
+const u32 R0_REGISTER = 0;
+const u32 R15_REGISTER = 15;
+const u32 CPSR_REGISTER = 25;
+const u32 FPSCR_REGISTER = 58;
+
+namespace GDBStub {
+
+static int gdbserver_socket = -1;
+
+static u8 command_buffer[GDB_BUFFER_SIZE];
+static u32 command_length;
+
+static u32 latest_signal = 0;
+static bool step_break = false;
+static bool memory_break = false;
+
+// Binding to a port within the reserved ports range (0-1023) requires root permissions,
+// so default to a port outside of that range.
+static u16 gdbstub_port = 24689;
+
+static bool halt_loop = true;
+static bool step_loop = false;
+std::atomic<bool> g_server_enabled(false);
+
+#ifdef _WIN32
+WSADATA InitData;
+#endif
+
+struct Breakpoint {
+ bool active;
+ PAddr addr;
+ u32 len;
+};
+
+static std::map<u32, Breakpoint> breakpoints_execute;
+static std::map<u32, Breakpoint> breakpoints_read;
+static std::map<u32, Breakpoint> breakpoints_write;
+
+/**
+ * Turns hex string character into the equivalent byte.
+ *
+ * @param hex Input hex character to be turned into byte.
+ */
+static u8 HexCharToValue(u8 hex) {
+ if (hex >= '0' && hex <= '9') {
+ return hex - '0';
+ } else if (hex >= 'a' && hex <= 'f') {
+ return hex - 'a' + 0xA;
+ } else if (hex >= 'A' && hex <= 'F') {
+ return hex - 'A' + 0xA;
+ }
+
+ LOG_ERROR(Debug_GDBStub, "Invalid nibble: %c (%02x)\n", hex, hex);
+ return 0;
+}
+
+/**
+ * Turn nibble of byte into hex string character.
+ *
+ * @param n Nibble to be turned into hex character.
+ */
+static u8 NibbleToHex(u8 n) {
+ n &= 0xF;
+ if (n < 0xA) {
+ return '0' + n;
+ } else {
+ return 'A' + n - 0xA;
+ }
+}
+
+/**
+* Converts input hex string characters into an array of equivalent of u8 bytes.
+*
+* @param dest Pointer to buffer to store u8 bytes.
+* @param src Pointer to array of output hex string characters.
+* @param len Length of src array.
+*/
+static u32 HexToInt(u8* src, u32 len) {
+ u32 output = 0;
+ while (len-- > 0) {
+ output = (output << 4) | HexCharToValue(src[0]);
+ src++;
+ }
+ return output;
+}
+
+/**
+ * Converts input array of u8 bytes into their equivalent hex string characters.
+ *
+ * @param dest Pointer to buffer to store output hex string characters.
+ * @param src Pointer to array of u8 bytes.
+ * @param len Length of src array.
+ */
+static void MemToGdbHex(u8* dest, u8* src, u32 len) {
+ while (len-- > 0) {
+ u8 tmp = *src++;
+ *dest++ = NibbleToHex(tmp >> 4);
+ *dest++ = NibbleToHex(tmp);
+ }
+}
+
+/**
+ * Converts input gdb-formatted hex string characters into an array of equivalent of u8 bytes.
+ *
+ * @param dest Pointer to buffer to store u8 bytes.
+ * @param src Pointer to array of output hex string characters.
+ * @param len Length of src array.
+ */
+static void GdbHexToMem(u8* dest, u8* src, u32 len) {
+ while (len-- > 0) {
+ *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]);
+ src += 2;
+ }
+}
+
+/**
+ * Convert a u32 into a gdb-formatted hex string.
+ *
+ * @param dest Pointer to buffer to store output hex string characters.
+ */
+static void IntToGdbHex(u8* dest, u32 v) {
+ for (int i = 0; i < 8; i += 2) {
+ dest[i + 1] = NibbleToHex(v >> (4 * i));
+ dest[i] = NibbleToHex(v >> (4 * (i + 1)));
+ }
+}
+
+/**
+ * Convert a gdb-formatted hex string into a u32.
+ *
+ * @param src Pointer to hex string.
+ */
+static u32 GdbHexToInt(u8* src) {
+ u32 output = 0;
+
+ for (int i = 0; i < 8; i += 2) {
+ output = (output << 4) | HexCharToValue(src[7 - i - 1]);
+ output = (output << 4) | HexCharToValue(src[7 - i]);
+ }
+
+ return output;
+}
+
+/// Read a byte from the gdb client.
+static u8 ReadByte() {
+ u8 c;
+ size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL);
+ if (received_size != 1) {
+ LOG_ERROR(Debug_GDBStub, "recv failed : %ld", received_size);
+ Shutdown();
+ }
+
+ return c;
+}
+
+/// Calculate the checksum of the current command buffer.
+static u8 CalculateChecksum(u8 *buffer, u32 length) {
+ return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>()));
+}
+
+/**
+ * Get the list of breakpoints for a given breakpoint type.
+ *
+ * @param type Type of breakpoint list.
+ */
+static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) {
+ switch (type) {
+ case BreakpointType::Execute:
+ return breakpoints_execute;
+ case BreakpointType::Read:
+ return breakpoints_read;
+ case BreakpointType::Write:
+ return breakpoints_write;
+ default:
+ return breakpoints_read;
+ }
+}
+
+/**
+ * Remove the breakpoint from the given address of the specified type.
+ *
+ * @param type Type of breakpoint.
+ * @param addr Address of breakpoint.
+ */
+static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
+ std::map<u32, Breakpoint>& p = GetBreakpointList(type);
+
+ auto bp = p.find(addr);
+ if (bp != p.end()) {
+ LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", bp->second.len, bp->second.addr, type);
+ p.erase(addr);
+ }
+}
+
+BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
+ std::map<u32, Breakpoint>& p = GetBreakpointList(type);
+ auto next_breakpoint = p.lower_bound(addr);
+ BreakpointAddress breakpoint;
+
+ if (next_breakpoint != p.end()) {
+ breakpoint.address = next_breakpoint->first;
+ breakpoint.type = type;
+ } else {
+ breakpoint.address = 0;
+ breakpoint.type = BreakpointType::None;
+ }
+
+ return breakpoint;
+}
+
+bool CheckBreakpoint(PAddr addr, BreakpointType type) {
+ if (!IsConnected()) {
+ return false;
+ }
+
+ std::map<u32, Breakpoint>& p = GetBreakpointList(type);
+
+ auto bp = p.find(addr);
+ if (bp != p.end()) {
+ u32 len = bp->second.len;
+
+ // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
+ // no matter if it's a 4-byte or 2-byte instruction. When you execute a
+ // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on
+ // two instructions instead of the single instruction you placed the breakpoint
+ // on. So, as a way to make sure that execution breakpoints are only breaking
+ // on the instruction that was specified, set the length of an execution
+ // breakpoint to 1. This should be fine since the CPU should never begin executing
+ // an instruction anywhere except the beginning of the instruction.
+ if (type == BreakpointType::Execute) {
+ len = 1;
+ }
+
+ if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
+ LOG_DEBUG(Debug_GDBStub, "Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type, addr, bp->second.addr, bp->second.addr + len, len);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Send packet to gdb client.
+ *
+ * @param packet Packet to be sent to client.
+ */
+static void SendPacket(const char packet) {
+ size_t sent_size = send(gdbserver_socket, &packet, 1, 0);
+ if (sent_size != 1) {
+ LOG_ERROR(Debug_GDBStub, "send failed");
+ }
+}
+
+/**
+ * Send reply to gdb client.
+ *
+ * @param reply Reply to be sent to client.
+ */
+static void SendReply(const char* reply) {
+ if (!IsConnected()) {
+ return;
+ }
+
+ memset(command_buffer, 0, sizeof(command_buffer));
+
+ command_length = strlen(reply);
+ if (command_length + 4 > sizeof(command_buffer)) {
+ LOG_ERROR(Debug_GDBStub, "command_buffer overflow in SendReply");
+ return;
+ }
+
+ memcpy(command_buffer + 1, reply, command_length);
+
+ u8 checksum = CalculateChecksum(command_buffer, command_length + 1);
+ command_buffer[0] = GDB_STUB_START;
+ command_buffer[command_length + 1] = GDB_STUB_END;
+ command_buffer[command_length + 2] = NibbleToHex(checksum >> 4);
+ command_buffer[command_length + 3] = NibbleToHex(checksum);
+
+ u8* ptr = command_buffer;
+ u32 left = command_length + 4;
+ while (left > 0) {
+ int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0);
+ if (sent_size < 0) {
+ LOG_ERROR(Debug_GDBStub, "gdb: send failed");
+ return Shutdown();
+ }
+
+ left -= sent_size;
+ ptr += sent_size;
+ }
+}
+
+/// Handle query command from gdb client.
+static void HandleQuery() {
+ LOG_DEBUG(Debug_GDBStub, "gdb: query '%s'\n", command_buffer + 1);
+
+ if (!strcmp(reinterpret_cast<const char*>(command_buffer + 1), "TStatus")) {
+ SendReply("T0");
+ } else {
+ SendReply("");
+ }
+}
+
+/// Handle set thread command from gdb client.
+static void HandleSetThread() {
+ if (memcmp(command_buffer, "Hg0", 3) == 0 ||
+ memcmp(command_buffer, "Hc-1", 4) == 0 ||
+ memcmp(command_buffer, "Hc0", 4) == 0 ||
+ memcmp(command_buffer, "Hc1", 4) == 0) {
+ return SendReply("OK");
+ }
+
+ SendReply("E01");
+}
+
+/**
+ * Send signal packet to client.
+ *
+ * @param signal Signal to be sent to client.
+ */
+void SendSignal(u32 signal) {
+ if (gdbserver_socket == -1) {
+ return;
+ }
+
+ latest_signal = signal;
+
+ std::string buffer = Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15, htonl(Core::g_app_core->GetPC()), 13, htonl(Core::g_app_core->GetReg(13)));
+ LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str());
+ SendReply(buffer.c_str());
+}
+
+/// Read command from gdb client.
+static void ReadCommand() {
+ command_length = 0;
+ memset(command_buffer, 0, sizeof(command_buffer));
+
+ u8 c = ReadByte();
+ if (c == '+') {
+ //ignore ack
+ return;
+ } else if (c == 0x03) {
+ LOG_INFO(Debug_GDBStub, "gdb: found break command\n");
+ halt_loop = true;
+ SendSignal(SIGTRAP);
+ return;
+ } else if (c != GDB_STUB_START) {
+ LOG_DEBUG(Debug_GDBStub, "gdb: read invalid byte %02x\n", c);
+ return;
+ }
+
+ while ((c = ReadByte()) != GDB_STUB_END) {
+ if (command_length >= sizeof(command_buffer)) {
+ LOG_ERROR(Debug_GDBStub, "gdb: command_buffer overflow\n");
+ SendPacket(GDB_STUB_NACK);
+ return;
+ }
+ command_buffer[command_length++] = c;
+ }
+
+ u8 checksum_received = HexCharToValue(ReadByte()) << 4;
+ checksum_received |= HexCharToValue(ReadByte());
+
+ u8 checksum_calculated = CalculateChecksum(command_buffer, command_length);
+
+ if (checksum_received != checksum_calculated) {
+ LOG_ERROR(Debug_GDBStub, "gdb: invalid checksum: calculated %02x and read %02x for $%s# (length: %d)\n",
+ checksum_calculated, checksum_received, command_buffer, command_length);
+
+ command_length = 0;
+
+ SendPacket(GDB_STUB_NACK);
+ return;
+ }
+
+ SendPacket(GDB_STUB_ACK);
+}
+
+/// Check if there is data to be read from the gdb client.
+static bool IsDataAvailable() {
+ if (!IsConnected()) {
+ return false;
+ }
+
+ fd_set fd_socket;
+
+ FD_ZERO(&fd_socket);
+ FD_SET(gdbserver_socket, &fd_socket);
+
+ struct timeval t;
+ t.tv_sec = 0;
+ t.tv_usec = 0;
+
+ if (select(gdbserver_socket + 1, &fd_socket, nullptr, nullptr, &t) < 0) {
+ LOG_ERROR(Debug_GDBStub, "select failed");
+ return false;
+ }
+
+ return FD_ISSET(gdbserver_socket, &fd_socket);
+}
+
+/// Send requested register to gdb client.
+static void ReadRegister() {
+ static u8 reply[64];
+ memset(reply, 0, sizeof(reply));
+
+ u32 id = HexCharToValue(command_buffer[1]);
+ if (command_buffer[2] != '\0') {
+ id <<= 4;
+ id |= HexCharToValue(command_buffer[2]);
+ }
+
+ if (id >= R0_REGISTER && id <= R15_REGISTER) {
+ IntToGdbHex(reply, Core::g_app_core->GetReg(id));
+ } else if (id == CPSR_REGISTER) {
+ IntToGdbHex(reply, Core::g_app_core->GetCPSR());
+ } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
+ IntToGdbHex(reply, Core::g_app_core->GetVFPReg(id - CPSR_REGISTER - 1)); // VFP registers should start at 26, so one after CSPR_REGISTER
+ } else if (id == FPSCR_REGISTER) {
+ IntToGdbHex(reply, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR)); // Get FPSCR
+ IntToGdbHex(reply + 8, 0);
+ } else {
+ return SendReply("E01");
+ }
+
+ SendReply(reinterpret_cast<char*>(reply));
+}
+
+/// Send all registers to the gdb client.
+static void ReadRegisters() {
+ static u8 buffer[GDB_BUFFER_SIZE - 4];
+ memset(buffer, 0, sizeof(buffer));
+
+ u8* bufptr = buffer;
+ for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
+ if (reg <= R15_REGISTER) {
+ IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetReg(reg));
+ } else if (reg == CPSR_REGISTER) {
+ IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetCPSR());
+ } else if (reg == CPSR_REGISTER - 1) {
+ // Dummy FPA register, ignore
+ IntToGdbHex(bufptr + i * CHAR_BIT, 0);
+ } else if (reg < CPSR_REGISTER) {
+ // Dummy FPA registers, ignore
+ IntToGdbHex(bufptr + i * CHAR_BIT, 0);
+ IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
+ IntToGdbHex(bufptr + (i + 2) * CHAR_BIT, 0);
+ i += 2;
+ } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) {
+ IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPReg(reg - CPSR_REGISTER - 1));
+ IntToGdbHex(bufptr + (i + 1) * CHAR_BIT, 0);
+ i++;
+ } else if (reg == FPSCR_REGISTER) {
+ IntToGdbHex(bufptr + i * CHAR_BIT, Core::g_app_core->GetVFPSystemReg(VFP_FPSCR));
+ }
+ }
+
+ SendReply(reinterpret_cast<char*>(buffer));
+}
+
+/// Modify data of register specified by gdb client.
+static void WriteRegister() {
+ u8* buffer_ptr = command_buffer + 3;
+
+ u32 id = HexCharToValue(command_buffer[1]);
+ if (command_buffer[2] != '=') {
+ ++buffer_ptr;
+ id <<= 4;
+ id |= HexCharToValue(command_buffer[2]);
+ }
+
+ if (id >= R0_REGISTER && id <= R15_REGISTER) {
+ Core::g_app_core->SetReg(id, GdbHexToInt(buffer_ptr));
+ } else if (id == CPSR_REGISTER) {
+ Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr));
+ } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
+ Core::g_app_core->SetVFPReg(id - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr));
+ } else if (id == FPSCR_REGISTER) {
+ Core::g_app_core->SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr));
+ } else {
+ return SendReply("E01");
+ }
+
+ SendReply("OK");
+}
+
+/// Modify all registers with data received from the client.
+static void WriteRegisters() {
+ u8* buffer_ptr = command_buffer + 1;
+
+ if (command_buffer[0] != 'G')
+ return SendReply("E01");
+
+ for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
+ if (reg <= R15_REGISTER) {
+ Core::g_app_core->SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
+ } else if (reg == CPSR_REGISTER) {
+ Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT));
+ } else if (reg == CPSR_REGISTER - 1) {
+ // Dummy FPA register, ignore
+ } else if (reg < CPSR_REGISTER) {
+ // Dummy FPA registers, ignore
+ i += 2;
+ } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) {
+ Core::g_app_core->SetVFPReg(reg - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
+ i++; // Skip padding
+ } else if (reg == FPSCR_REGISTER) {
+ Core::g_app_core->SetVFPSystemReg(VFP_FPSCR, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
+ }
+ }
+
+ SendReply("OK");
+}
+
+/// Read location in memory specified by gdb client.
+static void ReadMemory() {
+ static u8 reply[GDB_BUFFER_SIZE - 4];
+
+ auto start_offset = command_buffer+1;
+ auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
+ PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+
+ start_offset = addr_pos+1;
+ u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+
+ LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len);
+
+ if (len * 2 > sizeof(reply)) {
+ SendReply("E01");
+ }
+
+ u8* data = Memory::GetPointer(addr);
+ if (!data) {
+ return SendReply("E0");
+ }
+
+ MemToGdbHex(reply, data, len);
+ reply[len * 2] = '\0';
+ SendReply(reinterpret_cast<char*>(reply));
+}
+
+/// Modify location in memory with data received from the gdb client.
+static void WriteMemory() {
+ auto start_offset = command_buffer+1;
+ auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
+ PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+
+ start_offset = addr_pos+1;
+ auto len_pos = std::find(start_offset, command_buffer+command_length, ':');
+ u32 len = HexToInt(start_offset, len_pos - start_offset);
+
+ u8* dst = Memory::GetPointer(addr);
+ if (!dst) {
+ return SendReply("E00");
+ }
+
+ GdbHexToMem(dst, len_pos + 1, len);
+ SendReply("OK");
+}
+
+void Break(bool is_memory_break) {
+ if (!halt_loop) {
+ halt_loop = true;
+ SendSignal(SIGTRAP);
+ }
+
+ memory_break = is_memory_break;
+}
+
+/// Tell the CPU that it should perform a single step.
+static void Step() {
+ step_loop = true;
+ halt_loop = true;
+ step_break = true;
+ SendSignal(SIGTRAP);
+}
+
+bool IsMemoryBreak() {
+ if (IsConnected()) {
+ return false;
+ }
+
+ return memory_break;
+}
+
+/// Tell the CPU to continue executing.
+static void Continue() {
+ memory_break = false;
+ step_break = false;
+ step_loop = false;
+ halt_loop = false;
+}
+
+/**
+ * Commit breakpoint to list of breakpoints.
+ *
+ * @param type Type of breakpoint.
+ * @param addr Address of breakpoint.
+ * @param len Length of breakpoint.
+ */
+bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
+ std::map<u32, Breakpoint>& p = GetBreakpointList(type);
+
+ Breakpoint breakpoint;
+ breakpoint.active = true;
+ breakpoint.addr = addr;
+ breakpoint.len = len;
+ p.insert({ addr, breakpoint });
+
+ LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len, breakpoint.addr);
+
+ return true;
+}
+
+/// Handle add breakpoint command from gdb client.
+static void AddBreakpoint() {
+ BreakpointType type;
+
+ u8 type_id = HexCharToValue(command_buffer[1]);
+ switch (type_id) {
+ case 0:
+ case 1:
+ type = BreakpointType::Execute;
+ break;
+ case 2:
+ type = BreakpointType::Write;
+ break;
+ case 3:
+ type = BreakpointType::Read;
+ break;
+ case 4:
+ type = BreakpointType::Access;
+ break;
+ default:
+ return SendReply("E01");
+ }
+
+ auto start_offset = command_buffer+3;
+ auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
+ PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+
+ start_offset = addr_pos+1;
+ u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+
+ if (type == BreakpointType::Access) {
+ // Access is made up of Read and Write types, so add both breakpoints
+ type = BreakpointType::Read;
+
+ if (!CommitBreakpoint(type, addr, len)) {
+ return SendReply("E02");
+ }
+
+ type = BreakpointType::Write;
+ }
+
+ if (!CommitBreakpoint(type, addr, len)) {
+ return SendReply("E02");
+ }
+
+ SendReply("OK");
+}
+
+/// Handle remove breakpoint command from gdb client.
+static void RemoveBreakpoint() {
+ BreakpointType type;
+
+ u8 type_id = HexCharToValue(command_buffer[1]);
+ switch (type_id) {
+ case 0:
+ case 1:
+ type = BreakpointType::Execute;
+ break;
+ case 2:
+ type = BreakpointType::Write;
+ break;
+ case 3:
+ type = BreakpointType::Read;
+ break;
+ case 4:
+ type = BreakpointType::Access;
+ break;
+ default:
+ return SendReply("E01");
+ }
+
+ auto start_offset = command_buffer+3;
+ auto addr_pos = std::find(start_offset, command_buffer+command_length, ',');
+ PAddr addr = HexToInt(start_offset, addr_pos - start_offset);
+
+ start_offset = addr_pos+1;
+ u32 len = HexToInt(start_offset, (command_buffer + command_length) - start_offset);
+
+ if (type == BreakpointType::Access) {
+ // Access is made up of Read and Write types, so add both breakpoints
+ type = BreakpointType::Read;
+ RemoveBreakpoint(type, addr);
+
+ type = BreakpointType::Write;
+ }
+
+ RemoveBreakpoint(type, addr);
+ SendReply("OK");
+}
+
+void HandlePacket() {
+ if (!IsConnected()) {
+ return;
+ }
+
+ if (!IsDataAvailable()) {
+ return;
+ }
+
+ ReadCommand();
+ if (command_length == 0) {
+ return;
+ }
+
+ LOG_DEBUG(Debug_GDBStub, "Packet: %s", command_buffer);
+
+ switch (command_buffer[0]) {
+ case 'q':
+ HandleQuery();
+ break;
+ case 'H':
+ HandleSetThread();
+ break;
+ case '?':
+ SendSignal(latest_signal);
+ break;
+ case 'k':
+ Shutdown();
+ LOG_INFO(Debug_GDBStub, "killed by gdb");
+ return;
+ case 'g':
+ ReadRegisters();
+ break;
+ case 'G':
+ WriteRegisters();
+ break;
+ case 'p':
+ ReadRegister();
+ break;
+ case 'P':
+ WriteRegister();
+ break;
+ case 'm':
+ ReadMemory();
+ break;
+ case 'M':
+ WriteMemory();
+ break;
+ case 's':
+ Step();
+ return;
+ case 'C':
+ case 'c':
+ Continue();
+ return;
+ case 'z':
+ RemoveBreakpoint();
+ break;
+ case 'Z':
+ AddBreakpoint();
+ break;
+ default:
+ SendReply("");
+ break;
+ }
+}
+
+void SetServerPort(u16 port) {
+ gdbstub_port = port;
+}
+
+void ToggleServer(bool status) {
+ if (status) {
+ g_server_enabled = status;
+
+ // Start server
+ if (!IsConnected() && Core::g_sys_core != nullptr) {
+ Init();
+ }
+ }
+ else {
+ // Stop server
+ if (IsConnected()) {
+ Shutdown();
+ }
+
+ g_server_enabled = status;
+ }
+}
+
+void Init(u16 port) {
+ if (!g_server_enabled) {
+ // Set the halt loop to false in case the user enabled the gdbstub mid-execution.
+ // This way the CPU can still execute normally.
+ halt_loop = false;
+ step_loop = false;
+ return;
+ }
+
+ // Setup initial gdbstub status
+ halt_loop = true;
+ step_loop = false;
+
+ breakpoints_execute.clear();
+ breakpoints_read.clear();
+ breakpoints_write.clear();
+
+ // Start gdb server
+ LOG_INFO(Debug_GDBStub, "Starting GDB server on port %d...", port);
+
+ sockaddr_in saddr_server = {};
+ saddr_server.sin_family = AF_INET;
+ saddr_server.sin_port = htons(port);
+ saddr_server.sin_addr.s_addr = INADDR_ANY;
+
+#ifdef _WIN32
+ WSAStartup(MAKEWORD(2, 2), &InitData);
+#endif
+
+ int tmpsock = socket(PF_INET, SOCK_STREAM, 0);
+ if (tmpsock == -1) {
+ LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket");
+ }
+
+ const sockaddr* server_addr = reinterpret_cast<const sockaddr*>(&saddr_server);
+ socklen_t server_addrlen = sizeof(saddr_server);
+ if (bind(tmpsock, server_addr, server_addrlen) < 0) {
+ LOG_ERROR(Debug_GDBStub, "Failed to bind gdb socket");
+ }
+
+ if (listen(tmpsock, 1) < 0) {
+ LOG_ERROR(Debug_GDBStub, "Failed to listen to gdb socket");
+ }
+
+ // Wait for gdb to connect
+ LOG_INFO(Debug_GDBStub, "Waiting for gdb to connect...\n");
+ sockaddr_in saddr_client;
+ sockaddr* client_addr = reinterpret_cast<sockaddr*>(&saddr_client);
+ socklen_t client_addrlen = sizeof(saddr_client);
+ gdbserver_socket = accept(tmpsock, client_addr, &client_addrlen);
+ if (gdbserver_socket < 0) {
+ // In the case that we couldn't start the server for whatever reason, just start CPU execution like normal.
+ halt_loop = false;
+ step_loop = false;
+
+ LOG_ERROR(Debug_GDBStub, "Failed to accept gdb client");
+ }
+ else {
+ LOG_INFO(Debug_GDBStub, "Client connected.\n");
+ saddr_client.sin_addr.s_addr = ntohl(saddr_client.sin_addr.s_addr);
+ }
+
+ // Clean up temporary socket if it's still alive at this point.
+ if (tmpsock != -1) {
+ shutdown(tmpsock, SHUT_RDWR);
+ }
+}
+
+void Init() {
+ Init(gdbstub_port);
+}
+
+void Shutdown() {
+ if (!g_server_enabled) {
+ return;
+ }
+
+ LOG_INFO(Debug_GDBStub, "Stopping GDB ...");
+ if (gdbserver_socket != -1) {
+ shutdown(gdbserver_socket, SHUT_RDWR);
+ gdbserver_socket = -1;
+ }
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+
+ LOG_INFO(Debug_GDBStub, "GDB stopped.");
+}
+
+bool IsConnected() {
+ return g_server_enabled && gdbserver_socket != -1;
+}
+
+bool GetCpuHaltFlag() {
+ return halt_loop;
+}
+
+bool GetCpuStepFlag() {
+ return step_loop;
+}
+
+void SetCpuStepFlag(bool is_step) {
+ step_loop = is_step;
+}
+
+};
diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h
new file mode 100644
index 000000000..aff705a32
--- /dev/null
+++ b/src/core/gdbstub/gdbstub.h
@@ -0,0 +1,94 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+// Originally written by Sven Peter <sven@fail0verflow.com> for anergistic.
+
+#pragma once
+#include <atomic>
+
+namespace GDBStub {
+
+/// Breakpoint Method
+enum class BreakpointType {
+ None, ///< None
+ Execute, ///< Execution Breakpoint
+ Read, ///< Read Breakpoint
+ Write, ///< Write Breakpoint
+ Access ///< Access (R/W) Breakpoint
+};
+
+struct BreakpointAddress {
+ PAddr address;
+ BreakpointType type;
+};
+
+/// If set to false, the server will never be started and no gdbstub-related functions will be executed.
+extern std::atomic<bool> g_server_enabled;
+
+/**
+ * Set the port the gdbstub should use to listen for connections.
+ *
+ * @param port Port to listen for connection
+ */
+void SetServerPort(u16 port);
+
+/**
+ * Set the g_server_enabled flag and start or stop the server if possible.
+ *
+ * @param status Set the server to enabled or disabled.
+ */
+void ToggleServer(bool status);
+
+/// Start the gdbstub server.
+void Init();
+
+/// Stop gdbstub server.
+void Shutdown();
+
+/// Returns true if there is an active socket connection.
+bool IsConnected();
+
+/**
+ * Signal to the gdbstub server that it should halt CPU execution.
+ *
+ * @param is_memory_break If true, the break resulted from a memory breakpoint.
+ */
+void Break(bool is_memory_break = false);
+
+/// Determine if there was a memory breakpoint.
+bool IsMemoryBreak();
+
+/// Read and handle packet from gdb client.
+void HandlePacket();
+
+/**
+ * Get the nearest breakpoint of the specified type at the given address.
+ *
+ * @param addr Address to search from.
+ * @param type Type of breakpoint.
+ */
+BreakpointAddress GetNextBreakpointFromAddress(u32 addr, GDBStub::BreakpointType type);
+
+/**
+ * Check if a breakpoint of the specified type exists at the given address.
+ *
+ * @param addr Address of breakpoint.
+ * @param type Type of breakpoint.
+ */
+bool CheckBreakpoint(u32 addr, GDBStub::BreakpointType type);
+
+// If set to true, the CPU will halt at the beginning of the next CPU loop.
+bool GetCpuHaltFlag();
+
+// If set to true and the CPU is halted, the CPU will step one instruction.
+bool GetCpuStepFlag();
+
+/**
+ * When set to true, the CPU will step one instruction when the CPU is halted next.
+ *
+ * @param is_step
+ */
+void SetCpuStepFlag(bool is_step);
+
+}
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 5846a161b..3501e45db 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -159,6 +159,14 @@ template<ResultCode func(s32*, u32, s32)> void Wrap() {
FuncReturn(retval);
}
+template<ResultCode func(s64*, u32, s32)> void Wrap() {
+ s64 param_1 = 0;
+ u32 retval = func(&param_1, PARAM(1), PARAM(2)).raw;
+ Core::g_app_core->SetReg(1, (u32)param_1);
+ Core::g_app_core->SetReg(2, (u32)(param_1 >> 32));
+ FuncReturn(retval);
+}
+
template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw;
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 195286422..5c3c47acf 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -45,30 +45,32 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
- if ((s32)Memory::Read32(address) <= value) {
+ if ((s32)Memory::Read32(address) < value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
}
break;
case ArbitrationType::WaitIfLessThanWithTimeout:
- if ((s32)Memory::Read32(address) <= value) {
+ if ((s32)Memory::Read32(address) < value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
break;
case ArbitrationType::DecrementAndWaitIfLessThan:
{
- s32 memory_value = Memory::Read32(address) - 1;
- Memory::Write32(address, memory_value);
- if (memory_value <= value) {
+ s32 memory_value = Memory::Read32(address);
+ if (memory_value < value) {
+ // Only change the memory value if the thread should wait
+ Memory::Write32(address, (s32)memory_value - 1);
Kernel::WaitCurrentThread_ArbitrateAddress(address);
}
break;
}
case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout:
{
- s32 memory_value = Memory::Read32(address) - 1;
- Memory::Write32(address, memory_value);
- if (memory_value <= value) {
+ s32 memory_value = Memory::Read32(address);
+ if (memory_value < value) {
+ // Only change the memory value if the thread should wait
+ Memory::Write32(address, (s32)memory_value - 1);
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
@@ -82,6 +84,13 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
HLE::Reschedule(__func__);
+ // The calls that use a timeout seem to always return a Timeout error even if they did not put the thread to sleep
+ if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
+ type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
+
+ return ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
+ ErrorSummary::StatusChanged, ErrorLevel::Info);
+ }
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index e4fc5f3c4..0cfb43fc7 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -51,6 +51,7 @@ void MemoryInit(u32 mem_type) {
for (int i = 0; i < 3; ++i) {
memory_regions[i].base = base;
memory_regions[i].size = memory_region_sizes[mem_type][i];
+ memory_regions[i].used = 0;
memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>();
base += memory_regions[i].size;
@@ -72,6 +73,7 @@ void MemoryShutdown() {
for (auto& region : memory_regions) {
region.base = 0;
region.size = 0;
+ region.used = 0;
region.linear_heap_memory = nullptr;
}
}
diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h
index 36690b091..091c1f89f 100644
--- a/src/core/hle/kernel/memory.h
+++ b/src/core/hle/kernel/memory.h
@@ -17,6 +17,7 @@ class VMManager;
struct MemoryRegionInfo {
u32 base; // Not an address, but offset from start of FCRAM
u32 size;
+ u32 used;
std::shared_ptr<std::vector<u8>> linear_heap_memory;
};
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index c2b4963d4..d148efde2 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -111,6 +111,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
segment.offset, segment.size, memory_state).Unwrap();
vm_manager.Reprotect(vma, permissions);
misc_memory_used += segment.size;
+ memory_region->used += segment.size;
};
// Map CodeSet segments
@@ -123,6 +124,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Locked
).Unwrap();
misc_memory_used += stack_size;
+ memory_region->used += stack_size;
vm_manager.LogLayout(Log::Level::Debug);
Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority);
@@ -165,6 +167,7 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u32 size, VMAPermission per
vm_manager.Reprotect(vma, perms);
heap_used += size;
+ memory_region->used += size;
return MakeResult<VAddr>(heap_end - size);
}
@@ -182,6 +185,7 @@ ResultCode Process::HeapFree(VAddr target, u32 size) {
if (result.IsError()) return result;
heap_used -= size;
+ memory_region->used -= size;
return RESULT_SUCCESS;
}
@@ -217,6 +221,7 @@ ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission p
vm_manager.Reprotect(vma, perms);
linear_heap_used += size;
+ memory_region->used += size;
return MakeResult<VAddr>(target);
}
@@ -243,6 +248,7 @@ ResultCode Process::LinearFree(VAddr target, u32 size) {
if (result.IsError()) return result;
linear_heap_used -= size;
+ memory_region->used -= size;
if (target + size == heap_end) {
// End of linear heap has been freed, so check what's the last allocated block in it and
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 00fa995f6..bf32f653d 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -20,6 +20,7 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -118,6 +119,7 @@ void Thread::Stop() {
Kernel::g_current_process->used_tls_slots[tls_index] = false;
g_current_process->misc_memory_used -= Memory::TLS_ENTRY_SIZE;
+ g_current_process->memory_region->used -= Memory::TLS_ENTRY_SIZE;
HLE::Reschedule(__func__);
}
@@ -298,7 +300,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
thread->waitsynch_waited = false;
- if (thread->status == THREADSTATUS_WAIT_SYNCH) {
+ if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) {
thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
ErrorSummary::StatusChanged, ErrorLevel::Info));
@@ -416,6 +418,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
ASSERT_MSG(thread->tls_index != -1, "Out of TLS space");
g_current_process->misc_memory_used += Memory::TLS_ENTRY_SIZE;
+ g_current_process->memory_region->used += Memory::TLS_ENTRY_SIZE;
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 08b3ea8c0..ce6bbd719 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -42,6 +42,9 @@ bool Timer::ShouldWait() {
void Timer::Acquire() {
ASSERT_MSG( !ShouldWait(), "object unavailable!");
+
+ if (reset_type == RESETTYPE_ONESHOT)
+ signaled = false;
}
void Timer::Set(s64 initial, s64 interval) {
@@ -84,9 +87,6 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
// Resume all waiting threads
timer->WakeupAllWaitingThreads();
- if (timer->reset_type == RESETTYPE_ONESHOT)
- timer->signaled = false;
-
if (timer->interval_delay != 0) {
// Reschedule the timer with the interval delay
u64 interval_microseconds = timer->interval_delay / 1000;
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp
index 57f49c91f..bbe8e1625 100644
--- a/src/core/hle/service/act_u.cpp
+++ b/src/core/hle/service/act_u.cpp
@@ -10,14 +10,15 @@
namespace ACT_U {
-// Empty arrays are illegal -- commented out until an entry is added.
-//const Interface::FunctionInfo FunctionTable[] = { };
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000600C2, nullptr, "GetAccountDataBlock"},
+};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
- //Register(FunctionTable);
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp
index aa391f3b2..7515a4e6e 100644
--- a/src/core/hle/service/am/am_net.cpp
+++ b/src/core/hle/service/am/am_net.cpp
@@ -10,6 +10,36 @@ namespace Service {
namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
+ {0x00020082, GetTitleIDList, "GetTitleIDList"},
+ {0x00030084, nullptr, "ListTitles"},
+ {0x000400C0, nullptr, "DeleteApplicationTitle"},
+ {0x000500C0, nullptr, "GetTitleProductCode"},
+ {0x00080000, nullptr, "TitleIDListGetTotal3"},
+ {0x00090082, nullptr, "GetTitleIDList3"},
+ {0x000A0000, nullptr, "GetDeviceID"},
+ {0x000D0084, nullptr, "ListTitles2"},
+ {0x00140040, nullptr, "FinishInstallToMedia"},
+ {0x00180080, nullptr, "InitializeTitleDatabase"},
+ {0x00190040, nullptr, "ReloadDBS"},
+ {0x001A00C0, nullptr, "GetDSiWareExportSize"},
+ {0x001B0144, nullptr, "ExportDSiWare"},
+ {0x001C0084, nullptr, "ImportDSiWare"},
+ {0x00230080, nullptr, "TitleIDListGetTotal2"},
+ {0x002400C2, nullptr, "GetTitleIDList2"},
+ {0x04010080, nullptr, "InstallFIRM"},
+ {0x04020040, nullptr, "StartInstallCIADB0"},
+ {0x04030000, nullptr, "StartInstallCIADB1"},
+ {0x04040002, nullptr, "AbortCIAInstall"},
+ {0x04050002, nullptr, "CloseCIAFinalizeInstall"},
+ {0x04060002, nullptr, "CloseCIA"},
+ {0x040700C2, nullptr, "FinalizeTitlesInstall"},
+ {0x04080042, nullptr, "GetCiaFileInfo"},
+ {0x040E00C2, nullptr, "InstallTitlesFinish"},
+ {0x040F0000, nullptr, "InstallNATIVEFIRM"},
+ {0x041000C0, nullptr, "DeleteTitle"},
+ {0x04120000, nullptr, "Initialize"},
+ {0x041700C0, nullptr, "MigrateAGBtoSAV"},
{0x08010000, nullptr, "OpenTicket"},
{0x08020002, nullptr, "TicketAbortInstall"},
{0x08030002, nullptr, "TicketFinalizeInstall"},
diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp
index 864fc14df..715b7b55d 100644
--- a/src/core/hle/service/am/am_sys.cpp
+++ b/src/core/hle/service/am/am_sys.cpp
@@ -12,6 +12,21 @@ namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
{0x00020082, GetTitleIDList, "GetTitleIDList"},
+ {0x00030084, nullptr, "ListTitles"},
+ {0x000400C0, nullptr, "DeleteApplicationTitle"},
+ {0x000500C0, nullptr, "GetTitleProductCode"},
+ {0x00080000, nullptr, "TitleIDListGetTotal3"},
+ {0x00090082, nullptr, "GetTitleIDList3"},
+ {0x000A0000, nullptr, "GetDeviceID"},
+ {0x000D0084, nullptr, "ListTitles2"},
+ {0x00140040, nullptr, "FinishInstallToMedia"},
+ {0x00180080, nullptr, "InitializeTitleDatabase"},
+ {0x00190040, nullptr, "ReloadDBS"},
+ {0x001A00C0, nullptr, "GetDSiWareExportSize"},
+ {0x001B0144, nullptr, "ExportDSiWare"},
+ {0x001C0084, nullptr, "ImportDSiWare"},
+ {0x00230080, nullptr, "TitleIDListGetTotal2"},
+ {0x002400C2, nullptr, "GetTitleIDList2"}
};
AM_SYS_Interface::AM_SYS_Interface() {
diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp
index 6bf84b36b..b1e1ea5e4 100644
--- a/src/core/hle/service/am/am_u.cpp
+++ b/src/core/hle/service/am/am_u.cpp
@@ -12,6 +12,34 @@ namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
{0x00020082, GetTitleIDList, "GetTitleIDList"},
+ {0x00030084, nullptr, "ListTitles"},
+ {0x000400C0, nullptr, "DeleteApplicationTitle"},
+ {0x000500C0, nullptr, "GetTitleProductCode"},
+ {0x00080000, nullptr, "TitleIDListGetTotal3"},
+ {0x00090082, nullptr, "GetTitleIDList3"},
+ {0x000A0000, nullptr, "GetDeviceID"},
+ {0x000D0084, nullptr, "ListTitles2"},
+ {0x00140040, nullptr, "FinishInstallToMedia"},
+ {0x00180080, nullptr, "InitializeTitleDatabase"},
+ {0x00190040, nullptr, "ReloadDBS"},
+ {0x001A00C0, nullptr, "GetDSiWareExportSize"},
+ {0x001B0144, nullptr, "ExportDSiWare"},
+ {0x001C0084, nullptr, "ImportDSiWare"},
+ {0x00230080, nullptr, "TitleIDListGetTotal2"},
+ {0x002400C2, nullptr, "GetTitleIDList2"},
+ {0x04010080, nullptr, "InstallFIRM"},
+ {0x04020040, nullptr, "StartInstallCIADB0"},
+ {0x04030000, nullptr, "StartInstallCIADB1"},
+ {0x04040002, nullptr, "AbortCIAInstall"},
+ {0x04050002, nullptr, "CloseCIAFinalizeInstall"},
+ {0x04060002, nullptr, "CloseCIA"},
+ {0x040700C2, nullptr, "FinalizeTitlesInstall"},
+ {0x04080042, nullptr, "GetCiaFileInfo"},
+ {0x040E00C2, nullptr, "InstallTitlesFinish"},
+ {0x040F0000, nullptr, "InstallNATIVEFIRM"},
+ {0x041000C0, nullptr, "DeleteTitle"},
+ {0x04120000, nullptr, "Initialize"},
+ {0x041700C0, nullptr, "MigrateAGBtoSAV"}
};
AM_U_Interface::AM_U_Interface() {
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index 3ac6ff94f..e5fd9165c 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -91,6 +91,12 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x004E0000, nullptr, "HardwareResetAsync"},
{0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
{0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
+ {0x00510080, nullptr, "GetStartupArgument"},
+ {0x00520104, nullptr, "Wrap1"},
+ {0x00530104, nullptr, "Unwrap1"},
+ {0x00580002, nullptr, "GetProgramID"},
+ {0x01010000, nullptr, "CheckNew3DSApp"},
+ {0x01020000, nullptr, "CheckNew3DS"}
};
APT_S_Interface::APT_S_Interface() {
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index 146bfd595..aba627f54 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -92,6 +92,12 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x004E0000, nullptr, "HardwareResetAsync"},
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
+ {0x00510080, nullptr, "GetStartupArgument"},
+ {0x00520104, nullptr, "Wrap1"},
+ {0x00530104, nullptr, "Unwrap1"},
+ {0x00580002, nullptr, "GetProgramID"},
+ {0x01010000, nullptr, "CheckNew3DSApp"},
+ {0x01020000, nullptr, "CheckNew3DS"}
};
APT_U_Interface::APT_U_Interface() {
diff --git a/src/core/hle/service/boss/boss_u.cpp b/src/core/hle/service/boss/boss_u.cpp
index ed978b963..9f17711bb 100644
--- a/src/core/hle/service/boss/boss_u.cpp
+++ b/src/core/hle/service/boss/boss_u.cpp
@@ -11,6 +11,9 @@ namespace BOSS {
const Interface::FunctionInfo FunctionTable[] = {
{0x00020100, nullptr, "GetStorageInfo"},
+ {0x000C0082, nullptr, "UnregisterTask"},
+ {0x001E0042, nullptr, "CancelTask"},
+ {0x00330042, nullptr, "StartBgImmediate"},
};
BOSS_U_Interface::BOSS_U_Interface() {
diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h
index edd524841..e9abdcb1f 100644
--- a/src/core/hle/service/cam/cam.h
+++ b/src/core/hle/service/cam/cam.h
@@ -10,6 +10,162 @@
namespace Service {
namespace CAM {
+enum class Port : u8 {
+ None = 0,
+ Cam1 = 1,
+ Cam2 = 2,
+ Both = Cam1 | Cam2
+};
+
+enum class CameraSelect : u8 {
+ None = 0,
+ Out1 = 1,
+ In1 = 2,
+ Out2 = 4,
+ In1Out1 = Out1 | In1,
+ Out1Out2 = Out1 | Out2,
+ In1Out2 = In1 | Out2,
+ All = Out1 | In1 | Out2
+};
+
+enum class Effect : u8 {
+ None = 0,
+ Mono = 1,
+ Sepia = 2,
+ Negative = 3,
+ Negafilm = 4,
+ Sepia01 = 5
+};
+
+enum class Context : u8 {
+ None = 0,
+ A = 1,
+ B = 2,
+ Both = A | B
+};
+
+enum class Flip : u8 {
+ None = 0,
+ Horizontal = 1,
+ Vertical = 2,
+ Reverse = 3
+};
+
+enum class Size : u8 {
+ VGA = 0,
+ QVGA = 1,
+ QQVGA = 2,
+ CIF = 3,
+ QCIF = 4,
+ DS_LCD = 5,
+ DS_LCDx4 = 6,
+ CTR_TOP_LCD = 7,
+ CTR_BOTTOM_LCD = QVGA
+};
+
+enum class FrameRate : u8 {
+ Rate_15 = 0,
+ Rate_15_To_5 = 1,
+ Rate_15_To_2 = 2,
+ Rate_10 = 3,
+ Rate_8_5 = 4,
+ Rate_5 = 5,
+ Rate_20 = 6,
+ Rate_20_To_5 = 7,
+ Rate_30 = 8,
+ Rate_30_To_5 = 9,
+ Rate_15_To_10 = 10,
+ Rate_20_To_10 = 11,
+ Rate_30_To_10 = 12
+};
+
+enum class ShutterSoundType : u8 {
+ Normal = 0,
+ Movie = 1,
+ MovieEnd = 2
+};
+
+enum class WhiteBalance : u8 {
+ BalanceAuto = 0,
+ Balance3200K = 1,
+ Balance4150K = 2,
+ Balance5200K = 3,
+ Balance6000K = 4,
+ Balance7000K = 5,
+ BalanceMax = 6,
+ BalanceNormal = BalanceAuto,
+ BalanceTungsten = Balance3200K,
+ BalanceWhiteFluorescentLight = Balance4150K,
+ BalanceDaylight = Balance5200K,
+ BalanceCloudy = Balance6000K,
+ BalanceHorizon = Balance6000K,
+ BalanceShade = Balance7000K
+};
+
+enum class PhotoMode : u8 {
+ Normal = 0,
+ Portrait = 1,
+ Landscape = 2,
+ Nightview = 3,
+ Letter0 = 4
+};
+
+enum class LensCorrection : u8 {
+ Off = 0,
+ On70 = 1,
+ On90 = 2,
+ Dark = Off,
+ Normal = On70,
+ Bright = On90
+};
+
+enum class Contrast : u8 {
+ Pattern01 = 1,
+ Pattern02 = 2,
+ Pattern03 = 3,
+ Pattern04 = 4,
+ Pattern05 = 5,
+ Pattern06 = 6,
+ Pattern07 = 7,
+ Pattern08 = 8,
+ Pattern09 = 9,
+ Pattern10 = 10,
+ Pattern11 = 11,
+ Low = Pattern05,
+ Normal = Pattern06,
+ High = Pattern07
+};
+
+enum class OutputFormat : u8 {
+ YUV422 = 0,
+ RGB565 = 1
+};
+
+struct PackageParameterCameraSelect {
+ CameraSelect camera;
+ s8 exposure;
+ WhiteBalance white_balance;
+ s8 sharpness;
+ bool auto_exposure;
+ bool auto_white_balance;
+ FrameRate frame_rate;
+ PhotoMode photo_mode;
+ Contrast contrast;
+ LensCorrection lens_correction;
+ bool noise_filter;
+ u8 padding;
+ s16 auto_exposure_window_x;
+ s16 auto_exposure_window_y;
+ s16 auto_exposure_window_width;
+ s16 auto_exposure_window_height;
+ s16 auto_white_balance_window_x;
+ s16 auto_white_balance_window_y;
+ s16 auto_white_balance_window_width;
+ s16 auto_white_balance_window_height;
+};
+
+static_assert(sizeof(PackageParameterCameraSelect) == 28, "PackageParameterCameraSelect structure size is wrong");
+
/// Initialize CAM service(s)
void Init();
diff --git a/src/core/hle/service/cam/cam_u.cpp b/src/core/hle/service/cam/cam_u.cpp
index 55083e0c7..1c292ea23 100644
--- a/src/core/hle/service/cam/cam_u.cpp
+++ b/src/core/hle/service/cam/cam_u.cpp
@@ -54,12 +54,17 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x002A0080, nullptr, "GetLatestVsyncTiming"},
{0x002B0000, nullptr, "GetStereoCameraCalibrationData"},
{0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
+ {0x002D00C0, nullptr, "WriteRegisterI2c"},
+ {0x002E00C0, nullptr, "WriteMcuVariableI2c"},
+ {0x002F0080, nullptr, "ReadRegisterI2cExclusive"},
+ {0x00300080, nullptr, "ReadMcuVariableI2cExclusive"},
{0x00310180, nullptr, "SetImageQualityCalibrationData"},
{0x00320000, nullptr, "GetImageQualityCalibrationData"},
{0x003302C0, nullptr, "SetPackageParameterWithoutContext"},
{0x00340140, nullptr, "SetPackageParameterWithContext"},
{0x003501C0, nullptr, "SetPackageParameterWithContextDetail"},
{0x00360000, nullptr, "GetSuitableY2rStandardCoefficient"},
+ {0x00370202, nullptr, "PlayShutterSoundWithWave"},
{0x00380040, nullptr, "PlayShutterSound"},
{0x00390000, nullptr, "DriverInitialize"},
{0x003A0000, nullptr, "DriverFinalize"},
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 6a1d961ac..6318bf2a7 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -2,7 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cstring>
#include "core/hle/hle.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/csnd_snd.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,17 +14,18 @@
namespace CSND_SND {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010140, nullptr, "Initialize"},
- {0x00020000, nullptr, "Shutdown"},
- {0x00030040, nullptr, "ExecuteType0Commands"},
+ {0x00010140, Initialize, "Initialize"},
+ {0x00020000, Shutdown, "Shutdown"},
+ {0x00030040, ExecuteType0Commands, "ExecuteType0Commands"},
{0x00040080, nullptr, "ExecuteType1Commands"},
- {0x00050000, nullptr, "AcquireSoundChannels"},
+ {0x00050000, AcquireSoundChannels, "AcquireSoundChannels"},
{0x00060000, nullptr, "ReleaseSoundChannels"},
{0x00070000, nullptr, "AcquireCaptureDevice"},
{0x00080040, nullptr, "ReleaseCaptureDevice"},
- {0x00090082, nullptr, "FlushDCache"},
- {0x000A0082, nullptr, "StoreDCache"},
- {0x000B0082, nullptr, "InvalidateDCache"},
+ {0x00090082, nullptr, "FlushDataCache"},
+ {0x000A0082, nullptr, "StoreDataCache"},
+ {0x000B0082, nullptr, "InvalidateDataCache"},
+ {0x000C0000, nullptr, "Reset"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -31,4 +35,51 @@ Interface::Interface() {
Register(FunctionTable);
}
+static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory = nullptr;
+static Kernel::SharedPtr<Kernel::Mutex> mutex = nullptr;
+
+void Initialize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ shared_memory = Kernel::SharedMemory::Create(cmd_buff[1],
+ Kernel::MemoryPermission::ReadWrite,
+ Kernel::MemoryPermission::ReadWrite, "CSNDSharedMem");
+
+ mutex = Kernel::Mutex::Create(false);
+
+ cmd_buff[1] = 0;
+ cmd_buff[2] = 0x4000000;
+ cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom();
+ cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom();
+}
+
+void ExecuteType0Commands(Service::Interface* self) {
+ u32* const cmd_buff = Kernel::GetCommandBuffer();
+ u8* const ptr = shared_memory->GetPointer(cmd_buff[1]);
+
+ if (shared_memory != nullptr && ptr != nullptr) {
+ Type0Command command;
+ std::memcpy(&command, ptr, sizeof(Type0Command));
+
+ LOG_WARNING(Service, "(STUBBED) CSND_SND::ExecuteType0Commands");
+ command.finished |= 1;
+ cmd_buff[1] = 0;
+
+ std::memcpy(ptr, &command, sizeof(Type0Command));
+ } else {
+ cmd_buff[1] = 1;
+ }
+}
+
+void AcquireSoundChannels(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ cmd_buff[1] = 0;
+ cmd_buff[2] = 0xFFFFFF00;
+}
+
+void Shutdown(Service::Interface* self) {
+ shared_memory = nullptr;
+ mutex = nullptr;
+}
+
} // namespace
diff --git a/src/core/hle/service/csnd_snd.h b/src/core/hle/service/csnd_snd.h
index a84752473..e861f3327 100644
--- a/src/core/hle/service/csnd_snd.h
+++ b/src/core/hle/service/csnd_snd.h
@@ -20,4 +20,17 @@ public:
}
};
+struct Type0Command {
+ // command id and next command offset
+ u32 command_id;
+ u32 finished;
+ u32 flags;
+ u8 parameters[20];
+};
+
+void Initialize(Service::Interface* self);
+void ExecuteType0Commands(Service::Interface* self);
+void AcquireSoundChannels(Service::Interface* self);
+void Shutdown(Service::Interface* self);
+
} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index ce5619069..d6b8d1318 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -150,13 +150,13 @@ static void RegisterInterruptEvents(Service::Interface* self) {
}
/**
- * DSP_DSP::WriteReg0x10 service function
+ * DSP_DSP::SetSemaphore service function
* Inputs:
* 1 : Unknown (observed only half word used)
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
-static void WriteReg0x10(Service::Interface* self) {
+static void SetSemaphore(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
SignalInterrupt();
@@ -276,12 +276,17 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00020040, nullptr, "RecvDataIsReady"},
{0x00030080, nullptr, "SendData"},
{0x00040040, nullptr, "SendDataIsEmpty"},
- {0x00070040, WriteReg0x10, "WriteReg0x10"},
+ {0x000500C2, nullptr, "SendFifoEx"},
+ {0x000600C0, nullptr, "RecvFifoEx"},
+ {0x00070040, SetSemaphore, "SetSemaphore"},
{0x00080000, nullptr, "GetSemaphore"},
{0x00090040, nullptr, "ClearSemaphore"},
+ {0x000A0040, nullptr, "MaskSemaphore"},
{0x000B0000, nullptr, "CheckSemaphoreRequest"},
{0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
{0x000D0082, WriteProcessPipe, "WriteProcessPipe"},
+ {0x000E00C0, nullptr, "ReadPipe"},
+ {0x000F0080, nullptr, "GetPipeReadableSize"},
{0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"},
{0x001100C2, LoadComponent, "LoadComponent"},
{0x00120000, nullptr, "UnloadComponent"},
@@ -295,7 +300,10 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
{0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
{0x001C0082, nullptr, "SetIirFilterEQ"},
+ {0x001D00C0, nullptr, "ReadMultiEx_SPI2"},
+ {0x001E00C2, nullptr, "WriteMultiEx_SPI2"},
{0x001F0000, GetHeadphoneStatus, "GetHeadphoneStatus"},
+ {0x00200040, nullptr, "ForceHeadphoneOut"},
{0x00210000, nullptr, "GetIsDspOccupied"},
};
diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp
index 3a5897d06..9e70ec901 100644
--- a/src/core/hle/service/frd/frd_u.cpp
+++ b/src/core/hle/service/frd/frd_u.cpp
@@ -11,25 +11,58 @@ namespace FRD {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010000, nullptr, "HasLoggedIn"},
+ {0x00020000, nullptr, "IsOnline"},
{0x00030000, nullptr, "Login"},
{0x00040000, nullptr, "Logout"},
- {0x00050000, nullptr, "GetFriendKey"},
+ {0x00050000, nullptr, "GetMyFriendKey"},
+ {0x00060000, nullptr, "GetMyPreference"},
+ {0x00070000, nullptr, "GetMyProfile"},
{0x00080000, nullptr, "GetMyPresence"},
{0x00090000, nullptr, "GetMyScreenName"},
- {0x00100040, nullptr, "GetPassword"},
+ {0x000A0000, nullptr, "GetMyMii"},
+ {0x000B0000, nullptr, "GetMyLocalAccountId"},
+ {0x000C0000, nullptr, "GetMyPlayingGame"},
+ {0x000D0000, nullptr, "GetMyFavoriteGame"},
+ {0x000E0000, nullptr, "GetMyNcPrincipalId"},
+ {0x000F0000, nullptr, "GetMyComment"},
+ {0x00100040, nullptr, "GetMyPassword"},
{0x00110080, nullptr, "GetFriendKeyList"},
+ {0x00120042, nullptr, "GetFriendPresence"},
+ {0x00130142, nullptr, "GetFriendScreenName"},
+ {0x00140044, nullptr, "GetFriendMii"},
+ {0x00150042, nullptr, "GetFriendProfile"},
+ {0x00160042, nullptr, "GetFriendRelationship"},
+ {0x00170042, nullptr, "GetFriendAttributeFlags"},
+ {0x00180044, nullptr, "GetFriendPlayingGame"},
{0x00190042, nullptr, "GetFriendFavoriteGame"},
{0x001A00C4, nullptr, "GetFriendInfo"},
- {0x001B0080, nullptr, "IsOnFriendList"},
- {0x001C0042, nullptr, "DecodeLocalFriendCode"},
- {0x001D0002, nullptr, "SetCurrentlyPlayingText"},
+ {0x001B0080, nullptr, "IsIncludedInFriendList"},
+ {0x001C0042, nullptr, "UnscrambleLocalFriendCode"},
+ {0x001D0002, nullptr, "UpdateGameModeDescription"},
+ {0x001E02C2, nullptr, "UpdateGameMode"},
+ {0x001F0042, nullptr, "SendInvitation"},
+ {0x00200002, nullptr, "AttachToEventNotification"},
+ {0x00210040, nullptr, "SetNotificationMask"},
+ {0x00220040, nullptr, "GetEventNotification"},
{0x00230000, nullptr, "GetLastResponseResult"},
+ {0x00240040, nullptr, "PrincipalIdToFriendCode"},
+ {0x00250080, nullptr, "FriendCodeToPrincipalId"},
+ {0x00260080, nullptr, "IsValidFriendCode"},
{0x00270040, nullptr, "ResultToErrorCode"},
{0x00280244, nullptr, "RequestGameAuthentication"},
{0x00290000, nullptr, "GetGameAuthenticationData"},
{0x002A0204, nullptr, "RequestServiceLocator"},
{0x002B0000, nullptr, "GetServiceLocatorData"},
+ {0x002C0002, nullptr, "DetectNatProperties"},
+ {0x002D0000, nullptr, "GetNatProperties"},
+ {0x002E0000, nullptr, "GetServerTimeInterval"},
+ {0x002F0040, nullptr, "AllowHalfAwake"},
+ {0x00300000, nullptr, "GetServerTypes"},
+ {0x00310082, nullptr, "GetFriendComment"},
{0x00320042, nullptr, "SetClientSdkVersion"},
+ {0x00330000, nullptr, "GetMyApproachContext"},
+ {0x00340046, nullptr, "AddFriendWithApproach"},
+ {0x00350082, nullptr, "DecryptApproachContext"},
};
FRD_U_Interface::FRD_U_Interface() {
diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp
index 6c0df67c3..d64b3656a 100644
--- a/src/core/hle/service/fs/archive.cpp
+++ b/src/core/hle/service/fs/archive.cpp
@@ -403,6 +403,13 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory));
}
+ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle) {
+ ArchiveBackend* archive = GetArchive(archive_handle);
+ if (archive == nullptr)
+ return ERR_INVALID_HANDLE;
+ return MakeResult<u64>(archive->GetFreeBytes());
+}
+
ResultCode FormatArchive(ArchiveIdCode id_code, const FileSys::Path& path) {
auto archive_itr = id_code_map.find(id_code);
if (archive_itr == id_code_map.end()) {
diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h
index 6f7048710..952deb4d4 100644
--- a/src/core/hle/service/fs/archive.h
+++ b/src/core/hle/service/fs/archive.h
@@ -167,6 +167,13 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a
const FileSys::Path& path);
/**
+ * Get the free space in an Archive
+ * @param archive_handle Handle to an open Archive object
+ * @return The number of free bytes in the archive
+ */
+ResultVal<u64> GetFreeBytesInArchive(ArchiveHandle archive_handle);
+
+/**
* Erases the contents of the physical folder that contains the archive
* identified by the specified id code and path
* @param id_code The id of the archive to format
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index ae52083f9..632620a56 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -497,6 +497,33 @@ static void FormatThisUserSaveData(Service::Interface* self) {
}
/**
+ * FS_User::GetFreeBytes service function
+ * Inputs:
+ * 0: 0x08120080
+ * 1: Archive handle low word
+ * 2: Archive handle high word
+ * Outputs:
+ * 1: Result of function, 0 on success, otherwise error code
+ * 2: Free byte count low word
+ * 3: Free byte count high word
+ */
+static void GetFreeBytes(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[1], cmd_buff[2]);
+ ResultVal<u64> bytes_res = GetFreeBytesInArchive(archive_handle);
+
+ cmd_buff[1] = bytes_res.Code().raw;
+ if (bytes_res.Succeeded()) {
+ cmd_buff[2] = (u32)*bytes_res;
+ cmd_buff[3] = *bytes_res >> 32;
+ } else {
+ cmd_buff[2] = 0;
+ cmd_buff[3] = 0;
+ }
+}
+
+/**
* FS_User::CreateExtSaveData service function
* Inputs:
* 0 : 0x08510242
@@ -681,96 +708,114 @@ static void GetPriority(Service::Interface* self) {
}
const Interface::FunctionInfo FunctionTable[] = {
- {0x000100C6, nullptr, "Dummy1"},
- {0x040100C4, nullptr, "Control"},
- {0x08010002, Initialize, "Initialize"},
- {0x080201C2, OpenFile, "OpenFile"},
- {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
- {0x08040142, DeleteFile, "DeleteFile"},
- {0x08050244, RenameFile, "RenameFile"},
- {0x08060142, DeleteDirectory, "DeleteDirectory"},
- {0x08070142, nullptr, "DeleteDirectoryRecursively"},
- {0x08080202, CreateFile, "CreateFile"},
- {0x08090182, CreateDirectory, "CreateDirectory"},
- {0x080A0244, RenameDirectory, "RenameDirectory"},
- {0x080B0102, OpenDirectory, "OpenDirectory"},
- {0x080C00C2, OpenArchive, "OpenArchive"},
- {0x080D0144, nullptr, "ControlArchive"},
- {0x080E0080, CloseArchive, "CloseArchive"},
- {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
- {0x08100200, nullptr, "CreateSystemSaveData"},
- {0x08110040, nullptr, "DeleteSystemSaveData"},
- {0x08120080, nullptr, "GetFreeBytes"},
- {0x08130000, nullptr, "GetCardType"},
- {0x08140000, nullptr, "GetSdmcArchiveResource"},
- {0x08150000, nullptr, "GetNandArchiveResource"},
- {0x08160000, nullptr, "GetSdmcFatfsErro"},
- {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
- {0x08180000, IsSdmcWriteable, "IsSdmcWritable"},
- {0x08190042, nullptr, "GetSdmcCid"},
- {0x081A0042, nullptr, "GetNandCid"},
- {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
- {0x081C0000, nullptr, "GetNandSpeedInfo"},
- {0x081D0042, nullptr, "GetSdmcLog"},
- {0x081E0042, nullptr, "GetNandLog"},
- {0x081F0000, nullptr, "ClearSdmcLog"},
- {0x08200000, nullptr, "ClearNandLog"},
- {0x08210000, CardSlotIsInserted, "CardSlotIsInserted"},
- {0x08220000, nullptr, "CardSlotPowerOn"},
- {0x08230000, nullptr, "CardSlotPowerOff"},
- {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
- {0x08250040, nullptr, "CardNorDirectCommand"},
- {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
- {0x08270082, nullptr, "CardNorDirectRead"},
- {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
- {0x08290082, nullptr, "CardNorDirectWrite"},
- {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
- {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
- {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
- {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
- {0x082E0040, nullptr, "GetProductInfo"},
- {0x082F0040, nullptr, "GetProgramLaunchInfo"},
- {0x08300182, nullptr, "CreateExtSaveData"},
- {0x08310180, nullptr, "CreateSharedExtSaveData"},
- {0x08320102, nullptr, "ReadExtSaveDataIcon"},
- {0x08330082, nullptr, "EnumerateExtSaveData"},
- {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
- {0x08350080, nullptr, "DeleteExtSaveData"},
- {0x08360080, nullptr, "DeleteSharedExtSaveData"},
- {0x08370040, nullptr, "SetCardSpiBaudRate"},
- {0x08380040, nullptr, "SetCardSpiBusMode"},
- {0x08390000, nullptr, "SendInitializeInfoTo9"},
- {0x083A0100, nullptr, "GetSpecialContentIndex"},
- {0x083B00C2, nullptr, "GetLegacyRomHeader"},
- {0x083C00C2, nullptr, "GetLegacyBannerData"},
- {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
- {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
- {0x083F00C0, nullptr, "GetExtDataBlockSize"},
- {0x08400040, nullptr, "AbnegateAccessRight"},
- {0x08410000, nullptr, "DeleteSdmcRoot"},
- {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
- {0x08430000, nullptr, "InitializeCtrFileSystem"},
- {0x08440000, nullptr, "CreateSeed"},
- {0x084500C2, nullptr, "GetFormatInfo"},
- {0x08460102, nullptr, "GetLegacyRomHeader2"},
- {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
- {0x08480042, nullptr, "GetSdmcCtrRootPath"},
- {0x08490040, nullptr, "GetArchiveResource"},
- {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
- {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
- {0x084C0242, FormatSaveData, "FormatSaveData"},
- {0x084D0102, nullptr, "GetLegacySubBannerData"},
- {0x084E0342, nullptr, "UpdateSha256Context"},
- {0x084F0102, nullptr, "ReadSpecialFile"},
- {0x08500040, nullptr, "GetSpecialFileSize"},
- {0x08510242, CreateExtSaveData, "CreateExtSaveData"},
- {0x08520100, DeleteExtSaveData, "DeleteExtSaveData"},
- {0x08560240, CreateSystemSaveData, "CreateSystemSaveData"},
- {0x08570080, DeleteSystemSaveData, "DeleteSystemSaveData"},
- {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
+ {0x000100C6, nullptr, "Dummy1"},
+ {0x040100C4, nullptr, "Control"},
+ {0x08010002, Initialize, "Initialize"},
+ {0x080201C2, OpenFile, "OpenFile"},
+ {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
+ {0x08040142, DeleteFile, "DeleteFile"},
+ {0x08050244, RenameFile, "RenameFile"},
+ {0x08060142, DeleteDirectory, "DeleteDirectory"},
+ {0x08070142, nullptr, "DeleteDirectoryRecursively"},
+ {0x08080202, CreateFile, "CreateFile"},
+ {0x08090182, CreateDirectory, "CreateDirectory"},
+ {0x080A0244, RenameDirectory, "RenameDirectory"},
+ {0x080B0102, OpenDirectory, "OpenDirectory"},
+ {0x080C00C2, OpenArchive, "OpenArchive"},
+ {0x080D0144, nullptr, "ControlArchive"},
+ {0x080E0080, CloseArchive, "CloseArchive"},
+ {0x080F0180, FormatThisUserSaveData, "FormatThisUserSaveData"},
+ {0x08100200, nullptr, "CreateSystemSaveData"},
+ {0x08110040, nullptr, "DeleteSystemSaveData"},
+ {0x08120080, GetFreeBytes, "GetFreeBytes"},
+ {0x08130000, nullptr, "GetCardType"},
+ {0x08140000, nullptr, "GetSdmcArchiveResource"},
+ {0x08150000, nullptr, "GetNandArchiveResource"},
+ {0x08160000, nullptr, "GetSdmcFatfsError"},
+ {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
+ {0x08180000, IsSdmcWriteable, "IsSdmcWritable"},
+ {0x08190042, nullptr, "GetSdmcCid"},
+ {0x081A0042, nullptr, "GetNandCid"},
+ {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
+ {0x081C0000, nullptr, "GetNandSpeedInfo"},
+ {0x081D0042, nullptr, "GetSdmcLog"},
+ {0x081E0042, nullptr, "GetNandLog"},
+ {0x081F0000, nullptr, "ClearSdmcLog"},
+ {0x08200000, nullptr, "ClearNandLog"},
+ {0x08210000, CardSlotIsInserted, "CardSlotIsInserted"},
+ {0x08220000, nullptr, "CardSlotPowerOn"},
+ {0x08230000, nullptr, "CardSlotPowerOff"},
+ {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
+ {0x08250040, nullptr, "CardNorDirectCommand"},
+ {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
+ {0x08270082, nullptr, "CardNorDirectRead"},
+ {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
+ {0x08290082, nullptr, "CardNorDirectWrite"},
+ {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
+ {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
+ {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
+ {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
+ {0x082E0040, nullptr, "GetProductInfo"},
+ {0x082F0040, nullptr, "GetProgramLaunchInfo"},
+ {0x08300182, nullptr, "CreateExtSaveData"},
+ {0x08310180, nullptr, "CreateSharedExtSaveData"},
+ {0x08320102, nullptr, "ReadExtSaveDataIcon"},
+ {0x08330082, nullptr, "EnumerateExtSaveData"},
+ {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
+ {0x08350080, nullptr, "DeleteExtSaveData"},
+ {0x08360080, nullptr, "DeleteSharedExtSaveData"},
+ {0x08370040, nullptr, "SetCardSpiBaudRate"},
+ {0x08380040, nullptr, "SetCardSpiBusMode"},
+ {0x08390000, nullptr, "SendInitializeInfoTo9"},
+ {0x083A0100, nullptr, "GetSpecialContentIndex"},
+ {0x083B00C2, nullptr, "GetLegacyRomHeader"},
+ {0x083C00C2, nullptr, "GetLegacyBannerData"},
+ {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
+ {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
+ {0x083F00C0, nullptr, "GetExtDataBlockSize"},
+ {0x08400040, nullptr, "AbnegateAccessRight"},
+ {0x08410000, nullptr, "DeleteSdmcRoot"},
+ {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
+ {0x08430000, nullptr, "InitializeCtrFileSystem"},
+ {0x08440000, nullptr, "CreateSeed"},
+ {0x084500C2, nullptr, "GetFormatInfo"},
+ {0x08460102, nullptr, "GetLegacyRomHeader2"},
+ {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
+ {0x08480042, nullptr, "GetSdmcCtrRootPath"},
+ {0x08490040, nullptr, "GetArchiveResource"},
+ {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
+ {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
+ {0x084C0242, FormatSaveData, "FormatSaveData"},
+ {0x084D0102, nullptr, "GetLegacySubBannerData"},
+ {0x084E0342, nullptr, "UpdateSha256Context"},
+ {0x084F0102, nullptr, "ReadSpecialFile"},
+ {0x08500040, nullptr, "GetSpecialFileSize"},
+ {0x08510242, CreateExtSaveData, "CreateExtSaveData"},
+ {0x08520100, DeleteExtSaveData, "DeleteExtSaveData"},
+ {0x08530142, nullptr, "ReadExtSaveDataIcon"},
+ {0x085400C0, nullptr, "GetExtDataBlockSize"},
+ {0x08550102, nullptr, "EnumerateExtSaveData"},
+ {0x08560240, CreateSystemSaveData, "CreateSystemSaveData"},
+ {0x08570080, DeleteSystemSaveData, "DeleteSystemSaveData"},
+ {0x08580000, nullptr, "StartDeviceMoveAsSource"},
+ {0x08590200, nullptr, "StartDeviceMoveAsDestination"},
+ {0x085A00C0, nullptr, "SetArchivePriority"},
+ {0x085B0080, nullptr, "GetArchivePriority"},
+ {0x085C00C0, nullptr, "SetCtrCardLatencyParameter"},
+ {0x085D01C0, nullptr, "SetFsCompatibilityInfo"},
+ {0x085E0040, nullptr, "ResetCardCompatibilityParameter"},
+ {0x085F0040, nullptr, "SwitchCleanupInvalidSaveData"},
+ {0x08600042, nullptr, "EnumerateSystemSaveData"},
{0x08610042, InitializeWithSdkVersion, "InitializeWithSdkVersion"},
- {0x08620040, SetPriority, "SetPriority"},
- {0x08630000, GetPriority, "GetPriority"},
+ {0x08620040, SetPriority, "SetPriority"},
+ {0x08630000, GetPriority, "GetPriority"},
+ {0x08640000, nullptr, "GetNandInfo"},
+ {0x08650140, nullptr, "SetSaveDataSecureValue"},
+ {0x086600C0, nullptr, "GetSaveDataSecureValue"},
+ {0x086700C4, nullptr, "ControlSecureSave"},
+ {0x08680000, nullptr, "GetMediaType"},
+ {0x08690000, nullptr, "GetNandEraseCount"},
+ {0x086A0082, nullptr, "ReadNandReport"}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 481da0c9f..98b11c798 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -275,7 +275,7 @@ static void FlushDataCache(Service::Interface* self) {
u32 size = cmd_buff[2];
u32 process = cmd_buff[4];
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(address), size);
+ VideoCore::g_renderer->rasterizer->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size);
// TODO(purpasmart96): Verify return header on HW
@@ -365,7 +365,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
// GX request DMA - typically used for copying memory from GSP heap to VRAM
case CommandId::REQUEST_DMA:
- VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
+ VideoCore::g_renderer->rasterizer->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
command.dma_request.size);
memcpy(Memory::GetPointer(command.dma_request.dest_address),
@@ -373,7 +373,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
command.dma_request.size);
SignalInterrupt(InterruptId::DMA);
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
+ VideoCore::g_renderer->rasterizer->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
command.dma_request.size);
break;
@@ -467,7 +467,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
if (region.size == 0)
break;
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(
+ VideoCore::g_renderer->rasterizer->InvalidateRegion(
Memory::VirtualToPhysicalAddress(region.address), region.size);
}
break;
diff --git a/src/core/hle/service/gsp_lcd.cpp b/src/core/hle/service/gsp_lcd.cpp
index 9e36732b4..59ee9b73c 100644
--- a/src/core/hle/service/gsp_lcd.cpp
+++ b/src/core/hle/service/gsp_lcd.cpp
@@ -11,14 +11,18 @@
namespace GSP_LCD {
-/*const Interface::FunctionInfo FunctionTable[] = {
-};*/
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000F0000, nullptr, "PowerOnAllBacklights"},
+ {0x00100000, nullptr, "PowerOffAllBacklights"},
+ {0x00110040, nullptr, "PowerOnBacklight"},
+ {0x00120040, nullptr, "PowerOffBacklight"},
+};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
- //Register(FunctionTable);
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp
index fbfb9e885..e103881b1 100644
--- a/src/core/hle/service/hid/hid_user.cpp
+++ b/src/core/hle/service/hid/hid_user.cpp
@@ -11,6 +11,8 @@ namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
{0x000A0000, GetIPCHandles, "GetIPCHandles"},
+ {0x000B0000, nullptr, "StartAnalogStickCalibration"},
+ {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
{0x00110000, EnableAccelerometer, "EnableAccelerometer"},
{0x00120000, DisableAccelerometer, "DisableAccelerometer"},
{0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp
index 0a3aba0a0..43d8a3b85 100644
--- a/src/core/hle/service/http_c.cpp
+++ b/src/core/hle/service/http_c.cpp
@@ -47,10 +47,18 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00220040, nullptr, "GetResponseStatusCode"},
{0x002300C0, nullptr, "GetResponseStatusCodeTimeout"},
{0x00240082, nullptr, "AddTrustedRootCA"},
+ {0x00250080, nullptr, "AddDefaultCert"},
+ {0x00260080, nullptr, "SelectRootCertChain"},
+ {0x002700C4, nullptr, "SetClientCert"},
+ {0x002D0000, nullptr, "CreateRootCertChain"},
+ {0x002E0040, nullptr, "DestroyRootCertChain"},
+ {0x002F0082, nullptr, "RootCertChainAddCert"},
+ {0x00300080, nullptr, "RootCertChainAddDefaultCert"},
{0x00350186, nullptr, "SetDefaultProxy"},
{0x00360000, nullptr, "ClearDNSCache"},
{0x00370080, nullptr, "SetKeepAlive"},
- {0x003800C0, nullptr, "Finalize"},
+ {0x003800C0, nullptr, "SetPostDataTypeSize"},
+ {0x00390000, nullptr, "Finalize"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 25e70d321..1f27e9c1f 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -18,14 +18,14 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00050000, nullptr, "StopSampling"},
{0x00060000, nullptr, "IsSampling"},
{0x00070000, nullptr, "GetEventHandle"},
- {0x00080040, nullptr, "SetControl"},
- {0x00090000, nullptr, "GetControl"},
- {0x000A0040, nullptr, "SetBias"},
- {0x000B0000, nullptr, "GetBias"},
+ {0x00080040, nullptr, "SetGain"},
+ {0x00090000, nullptr, "GetGain"},
+ {0x000A0040, nullptr, "SetPower"},
+ {0x000B0000, nullptr, "GetPower"},
{0x000C0042, nullptr, "size"},
{0x000D0040, nullptr, "SetClamp"},
{0x000E0000, nullptr, "GetClamp"},
- {0x000F0040, nullptr, "unknown_input1"},
+ {0x000F0040, nullptr, "SetAllowShellClosed"},
{0x00100040, nullptr, "unknown_input2"},
};
diff --git a/src/core/hle/service/ndm_u.cpp b/src/core/hle/service/ndm_u.cpp
index df3c97193..ad247fd1f 100644
--- a/src/core/hle/service/ndm_u.cpp
+++ b/src/core/hle/service/ndm_u.cpp
@@ -14,10 +14,26 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00010042, nullptr, "EnterExclusiveState"},
{0x00020002, nullptr, "LeaveExclusiveState"},
{0x00030000, nullptr, "QueryExclusiveMode"},
+ {0x00040002, nullptr, "LockState"},
+ {0x00050002, nullptr, "UnlockState"},
{0x00060040, nullptr, "SuspendDaemons"},
+ {0x00070040, nullptr, "ResumeDaemons"},
{0x00080040, nullptr, "DisableWifiUsage"},
{0x00090000, nullptr, "EnableWifiUsage"},
+ {0x000A0000, nullptr, "GetCurrentState"},
+ {0x000B0000, nullptr, "GetTargetState"},
+ {0x000C0000, nullptr, "<Stubbed>"},
+ {0x000D0040, nullptr, "QueryStatus"},
+ {0x000E0040, nullptr, "GetDaemonDisableCount"},
+ {0x000F0000, nullptr, "GetSchedulerDisableCount"},
+ {0x00100040, nullptr, "SetScanInterval"},
+ {0x00110000, nullptr, "GetScanInterval"},
+ {0x00120040, nullptr, "SetRetryInterval"},
+ {0x00130000, nullptr, "GetRetryInterval"},
{0x00140040, nullptr, "OverrideDefaultDaemons"},
+ {0x00150000, nullptr, "ResetDefaultDaemons"},
+ {0x00160000, nullptr, "GetDefaultDaemons"},
+ {0x00170000, nullptr, "ClearHalfAwakeMacFilter"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/news/news_s.cpp b/src/core/hle/service/news/news_s.cpp
index 2f8c37d9e..5b8db3288 100644
--- a/src/core/hle/service/news/news_s.cpp
+++ b/src/core/hle/service/news/news_s.cpp
@@ -11,6 +11,18 @@ namespace NEWS {
const Interface::FunctionInfo FunctionTable[] = {
{0x000100C6, nullptr, "AddNotification"},
+ {0x00050000, nullptr, "GetTotalNotifications"},
+ {0x00060042, nullptr, "SetNewsDBHeader"},
+ {0x00070082, nullptr, "SetNotificationHeader"},
+ {0x00080082, nullptr, "SetNotificationMessage"},
+ {0x00090082, nullptr, "SetNotificationImage"},
+ {0x000A0042, nullptr, "GetNewsDBHeader"},
+ {0x000B0082, nullptr, "GetNotificationHeader"},
+ {0x000C0082, nullptr, "GetNotificationMessage"},
+ {0x000D0082, nullptr, "GetNotificationImage"},
+ {0x000E0040, nullptr, "SetInfoLEDPattern"},
+ {0x00120082, nullptr, "GetNotificationHeaderOther"},
+ {0x00130000, nullptr, "WriteNewsDBSavedata"},
};
NEWS_S_Interface::NEWS_S_Interface() {
diff --git a/src/core/hle/service/nim/nim_s.cpp b/src/core/hle/service/nim/nim_s.cpp
index 5d8bc059f..1172770ac 100644
--- a/src/core/hle/service/nim/nim_s.cpp
+++ b/src/core/hle/service/nim/nim_s.cpp
@@ -11,6 +11,9 @@ namespace NIM {
const Interface::FunctionInfo FunctionTable[] = {
{0x000A0000, nullptr, "CheckSysupdateAvailableSOAP"},
+ {0x0016020A, nullptr, "ListTitles"},
+ {0x002D0042, nullptr, "DownloadTickets"},
+ {0x00420240, nullptr, "StartDownload"},
};
NIM_S_Interface::NIM_S_Interface() {
diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp
index 6b3ef6ece..99e8e0880 100644
--- a/src/core/hle/service/ns_s.cpp
+++ b/src/core/hle/service/ns_s.cpp
@@ -12,7 +12,16 @@
namespace NS_S {
const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C0, nullptr, "LaunchFIRM"},
{0x000200C0, nullptr, "LaunchTitle"},
+ {0x000500C0, nullptr, "LaunchApplicationFIRM"},
+ {0x00060042, nullptr, "SetFIRMParams4A0"},
+ {0x00070042, nullptr, "CardUpdateInitialize"},
+ {0x000D0140, nullptr, "SetFIRMParams4B0"},
+ {0x000E0000, nullptr, "ShutdownAsync"},
+ {0x00100180, nullptr, "RebootSystem"},
+ {0x00150140, nullptr, "LaunchApplication"},
+ {0x00160000, nullptr, "HardRebootSystem"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp
index 18b22956f..adca64a3a 100644
--- a/src/core/hle/service/nwm_uds.cpp
+++ b/src/core/hle/service/nwm_uds.cpp
@@ -106,14 +106,32 @@ static void Initialize(Service::Interface* self) {
}
const Interface::FunctionInfo FunctionTable[] = {
+ {0x00020000, nullptr, "Scrap"},
{0x00030000, Shutdown, "Shutdown"},
+ {0x00040402, nullptr, "CreateNetwork"},
+ {0x00050040, nullptr, "EjectClient"},
+ {0x00060000, nullptr, "EjectSpectator"},
+ {0x00070080, nullptr, "UpdateNetworkAttribute"},
+ {0x00080000, nullptr, "DestroyNetwork"},
+ {0x000A0000, nullptr, "DisconnectNetwork"},
+ {0x000B0000, nullptr, "GetConnectionStatus"},
+ {0x000D0040, nullptr, "GetNodeInformation"},
{0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
{0x00100042, nullptr, "SetBeaconAdditionalData"},
+ {0x00110040, nullptr, "GetApplicationData"},
+ {0x00120100, nullptr, "Bind"},
+ {0x00130040, nullptr, "Unbind"},
{0x001400C0, nullptr, "RecvBroadcastDataFrame"},
+ {0x00150080, nullptr, "SetMaxSendDelay"},
+ {0x00170182, nullptr, "SendTo"},
+ {0x001A0000, nullptr, "GetChannel"},
{0x001B0302, Initialize, "Initialize"},
{0x001D0044, nullptr, "BeginHostingNetwork"},
{0x001E0084, nullptr, "ConnectToNetwork"},
{0x001F0006, nullptr, "DecryptBeaconData"},
+ {0x00200040, nullptr, "Flush"},
+ {0x00210080, nullptr, "SetProbeResponseParam"},
+ {0x00220402, nullptr, "ScanOnConnection"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
index 7420a62f4..48646ed72 100644
--- a/src/core/hle/service/pm_app.cpp
+++ b/src/core/hle/service/pm_app.cpp
@@ -19,6 +19,10 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00070042, nullptr, "GetFIRMLaunchParams"},
{0x00080100, nullptr, "GetTitleExheaderFlags"},
{0x00090042, nullptr, "SetFIRMLaunchParams"},
+ {0x000A0140, nullptr, "SetResourceLimit"},
+ {0x000B0140, nullptr, "GetResourceLimitMax"},
+ {0x000C0080, nullptr, "UnregisterProcess"},
+ {0x000D0240, nullptr, "LaunchTitleUpdate"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 2c7d49c9f..22c1093ff 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -68,6 +68,18 @@ void GetBatteryChargeState(Service::Interface* self) {
LOG_WARNING(Service_PTM, "(STUBBED) called");
}
+void GetTotalStepCount(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // TODO: This function is only a stub,
+ // it returns 0 as the total step count
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+
+ LOG_WARNING(Service_PTM, "(STUBBED) called");
+}
+
void IsLegacyPowerOff(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
index b690003cb..f2e76441f 100644
--- a/src/core/hle/service/ptm/ptm.h
+++ b/src/core/hle/service/ptm/ptm.h
@@ -72,6 +72,14 @@ void GetBatteryLevel(Interface* self);
void GetBatteryChargeState(Interface* self);
/**
+ * PTM::GetTotalStepCount service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Output of function, * = total step count
+ */
+void GetTotalStepCount(Interface* self);
+
+/**
* PTM::IsLegacyPowerOff service function
* Outputs:
* 1: Result code, 0 on success, otherwise error code
diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp
index 655658f3b..3ecfab05c 100644
--- a/src/core/hle/service/ptm/ptm_sysm.cpp
+++ b/src/core/hle/service/ptm/ptm_sysm.cpp
@@ -39,7 +39,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x08110000, nullptr, "GetShellStatus"},
{0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
{0x08130000, nullptr, "FormatSavedata"},
- {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"}
+ {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"},
+ {0x08180040, nullptr, "ConfigureNew3DSCPU"},
};
PTM_Sysm_Interface::PTM_Sysm_Interface() {
diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp
index 3f5e9c7c1..09dc38c3e 100644
--- a/src/core/hle/service/ptm/ptm_u.cpp
+++ b/src/core/hle/service/ptm/ptm_u.cpp
@@ -23,7 +23,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00090000, nullptr, "GetPedometerState"},
{0x000A0042, nullptr, "GetStepHistoryEntry"},
{0x000B00C2, nullptr, "GetStepHistory"},
- {0x000C0000, nullptr, "GetTotalStepCount"},
+ {0x000C0000, GetTotalStepCount, "GetTotalStepCount"},
{0x000D0040, nullptr, "SetPedometerRecordingMode"},
{0x000E0000, nullptr, "GetPedometerRecordingMode"},
{0x000F0084, nullptr, "GetStepHistoryAll"},
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 633b66fe2..822b093f4 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -552,13 +552,23 @@ static void RecvFrom(Service::Interface* self) {
u32 flags = cmd_buffer[3];
socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]);
- u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
+ struct
+ {
+ u32 output_buffer_descriptor;
+ u32 output_buffer_addr;
+ u32 address_buffer_descriptor;
+ u32 output_src_address_buffer;
+ } buffer_parameters;
+
+ std::memcpy(&buffer_parameters, &cmd_buffer[64], sizeof(buffer_parameters));
+
+ u8* output_buff = Memory::GetPointer(buffer_parameters.output_buffer_addr);
sockaddr src_addr;
socklen_t src_addr_len = sizeof(src_addr);
int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
- if (cmd_buffer[0x1A0 >> 2] != 0) {
- CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2]));
+ if (buffer_parameters.output_src_address_buffer != 0) {
+ CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer));
*ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
}
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
index e634276fc..9ecb72c77 100644
--- a/src/core/hle/service/ssl_c.cpp
+++ b/src/core/hle/service/ssl_c.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <random>
+
#include "core/hle/hle.h"
#include "core/hle/service/ssl_c.h"
@@ -10,11 +12,64 @@
namespace SSL_C {
+// TODO: Implement a proper CSPRNG in the future when actual security is needed
+static std::mt19937 rand_gen;
+
+static void Initialize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // Seed random number generator when the SSL service is initialized
+ std::random_device rand_device;
+ rand_gen.seed(rand_device());
+
+ // Stub, return success
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+}
+
+static void GenerateRandomData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 size = cmd_buff[1];
+ VAddr address = cmd_buff[3];
+ u8* output_buff = Memory::GetPointer(address);
+
+ // Fill the output buffer with random data.
+ u32 data = 0;
+ u32 i = 0;
+ while (i < size) {
+ if ((i % 4) == 0) {
+ // The random number generator returns 4 bytes worth of data, so generate new random data when i == 0 and when i is divisible by 4
+ data = rand_gen();
+ }
+
+ if (size > 4) {
+ // Use up the entire 4 bytes of the random data for as long as possible
+ *(u32*)(output_buff + i) = data;
+ i += 4;
+ } else if (size == 2) {
+ *(u16*)(output_buff + i) = (u16)(data & 0xffff);
+ i += 2;
+ } else {
+ *(u8*)(output_buff + i) = (u8)(data & 0xff);
+ i++;
+ }
+ }
+
+ // Stub, return success
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+}
+
const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010002, Initialize, "Initialize"},
{0x000200C2, nullptr, "CreateContext"},
+ {0x00030000, nullptr, "CreateRootCertChain"},
+ {0x00040040, nullptr, "DestroyRootCertChain"},
{0x00050082, nullptr, "AddTrustedRootCA"},
+ {0x00060080, nullptr, "RootCertChainAddDefaultCert"},
+ {0x00110042, GenerateRandomData, "GenerateRandomData"},
{0x00150082, nullptr, "Read"},
{0x00170082, nullptr, "Write"},
+ {0x00180080, nullptr, "ContextSetRootCertChain"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index 6b1b71fe4..69d0bf4a3 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -267,7 +267,7 @@ static void StartConversion(Service::Interface* self) {
// dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
u32 total_output_size = conversion.input_lines *
(conversion.dst.transfer_unit + conversion.dst.gap);
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(
+ VideoCore::g_renderer->rasterizer->InvalidateRegion(
Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size);
LOG_DEBUG(Service_Y2R, "called");
@@ -375,21 +375,41 @@ static void DriverFinalize(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, SetInputFormat, "SetInputFormat"},
+ {0x00020000, nullptr, "GetInputFormat"},
{0x00030040, SetOutputFormat, "SetOutputFormat"},
+ {0x00040000, nullptr, "GetOutputFormat"},
{0x00050040, SetRotation, "SetRotation"},
+ {0x00060000, nullptr, "GetRotation"},
{0x00070040, SetBlockAlignment, "SetBlockAlignment"},
+ {0x00080000, nullptr, "GetBlockAlignment"},
+ {0x00090040, nullptr, "SetSpacialDithering"},
+ {0x000A0000, nullptr, "GetSpacialDithering"},
+ {0x000B0040, nullptr, "SetTemporalDithering"},
+ {0x000C0000, nullptr, "GetTemporalDithering"},
{0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"},
{0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"},
{0x00100102, SetSendingY, "SetSendingY"},
{0x00110102, SetSendingU, "SetSendingU"},
{0x00120102, SetSendingV, "SetSendingV"},
{0x00130102, SetSendingYUYV, "SetSendingYUYV"},
+ {0x00140000, nullptr, "IsFinishedSendingYuv"},
+ {0x00150000, nullptr, "IsFinishedSendingY"},
+ {0x00160000, nullptr, "IsFinishedSendingU"},
+ {0x00170000, nullptr, "IsFinishedSendingV"},
{0x00180102, SetReceiving, "SetReceiving"},
+ {0x00190000, nullptr, "IsFinishedReceiving"},
{0x001A0040, SetInputLineWidth, "SetInputLineWidth"},
+ {0x001B0000, nullptr, "GetInputLineWidth"},
{0x001C0040, SetInputLines, "SetInputLines"},
+ {0x001D0000, nullptr, "GetInputLines"},
{0x001E0100, SetCoefficient, "SetCoefficient"},
+ {0x001F0000, nullptr, "GetCoefficient"},
{0x00200040, SetStandardCoefficient, "SetStandardCoefficient"},
+ {0x00210040, nullptr, "GetStandardCoefficientParams"},
{0x00220040, SetAlpha, "SetAlpha"},
+ {0x00230000, nullptr, "GetAlpha"},
+ {0x00240200, nullptr, "SetDitheringWeightParams"},
+ {0x00250000, nullptr, "GetDitheringWeightParams"},
{0x00260000, StartConversion, "StartConversion"},
{0x00270000, StopConversion, "StopConversion"},
{0x00280000, IsBusyConversion, "IsBusyConversion"},
@@ -397,6 +417,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x002A0000, PingProcess, "PingProcess"},
{0x002B0000, DriverInitialize, "DriverInitialize"},
{0x002C0000, DriverFinalize, "DriverFinalize"},
+ {0x002D0000, nullptr, "GetPackageParameter"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 45d5f3c5d..e39edcc16 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -778,6 +778,51 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
return RESULT_SUCCESS;
}
+static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {
+ using Kernel::MemoryRegion;
+
+ LOG_TRACE(Kernel_SVC, "called type=%u param=%d", type, param);
+
+ switch ((SystemInfoType)type) {
+ case SystemInfoType::REGION_MEMORY_USAGE:
+ switch ((SystemInfoMemUsageRegion)param) {
+ case SystemInfoMemUsageRegion::ALL:
+ *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::APPLICATION)->used
+ + Kernel::GetMemoryRegion(Kernel::MemoryRegion::SYSTEM)->used
+ + Kernel::GetMemoryRegion(Kernel::MemoryRegion::BASE)->used;
+ break;
+ case SystemInfoMemUsageRegion::APPLICATION:
+ *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::APPLICATION)->used;
+ break;
+ case SystemInfoMemUsageRegion::SYSTEM:
+ *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::SYSTEM)->used;
+ break;
+ case SystemInfoMemUsageRegion::BASE:
+ *out = Kernel::GetMemoryRegion(Kernel::MemoryRegion::BASE)->used;
+ break;
+ default:
+ LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=0 region: param=%d", param);
+ *out = 0;
+ break;
+ }
+ break;
+ case SystemInfoType::KERNEL_ALLOCATED_PAGES:
+ LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=2 param=%d", param);
+ *out = 0;
+ break;
+ case SystemInfoType::KERNEL_SPAWNED_PIDS:
+ *out = 5;
+ break;
+ default:
+ LOG_ERROR(Kernel_SVC, "unknown GetSystemInfo type=%u param=%d", type, param);
+ *out = 0;
+ break;
+ }
+
+ // This function never returns an error, even if invalid parameters were passed.
+ return RESULT_SUCCESS;
+}
+
static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) {
LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u", process_handle, type);
@@ -877,7 +922,7 @@ static const FunctionDef SVC_Table[] = {
{0x27, HLE::Wrap<DuplicateHandle>, "DuplicateHandle"},
{0x28, HLE::Wrap<GetSystemTick>, "GetSystemTick"},
{0x29, nullptr, "GetHandleInfo"},
- {0x2A, nullptr, "GetSystemInfo"},
+ {0x2A, HLE::Wrap<GetSystemInfo>, "GetSystemInfo"},
{0x2B, HLE::Wrap<GetProcessInfo>, "GetProcessInfo"},
{0x2C, nullptr, "GetThreadInfo"},
{0x2D, HLE::Wrap<ConnectToPort>, "ConnectToPort"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index 12de9ffbe..4b9c71e06 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -41,6 +41,35 @@ enum ArbitrationType {
namespace SVC {
+/// Values accepted by svcGetSystemInfo's type parameter.
+enum class SystemInfoType {
+ /**
+ * Reports total used memory for all regions or a specific one, according to the extra
+ * parameter. See `SystemInfoMemUsageRegion`.
+ */
+ REGION_MEMORY_USAGE = 0,
+ /**
+ * Returns the memory usage for certain allocations done internally by the kernel.
+ */
+ KERNEL_ALLOCATED_PAGES = 2,
+ /**
+ * "This returns the total number of processes which were launched directly by the kernel.
+ * For the ARM11 NATIVE_FIRM kernel, this is 5, for processes sm, fs, pm, loader, and pxi."
+ */
+ KERNEL_SPAWNED_PIDS = 26,
+};
+
+/**
+ * Accepted by svcGetSystemInfo param with REGION_MEMORY_USAGE type. Selects a region to query
+ * memory usage of.
+ */
+enum class SystemInfoMemUsageRegion {
+ ALL = 0,
+ APPLICATION = 1,
+ SYSTEM = 2,
+ BASE = 3,
+};
+
void CallSVC(u32 immediate);
} // namespace
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index bc7bde903..4bd3a632d 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -26,7 +26,7 @@
#include "core/tracer/recorder.h"
#include "video_core/command_processor.h"
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
@@ -141,7 +141,7 @@ inline void Write(u32 addr, const T data) {
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1);
}
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress());
+ VideoCore::g_renderer->rasterizer->InvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress());
}
// Reset "trigger" flag and set the "finish" flag
@@ -172,7 +172,7 @@ inline void Write(u32 addr, const T data) {
u32 output_gap = config.texture_copy.output_gap * 16;
size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap);
- VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), contiguous_input_size);
+ VideoCore::g_renderer->rasterizer->FlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size);
u32 remaining_size = config.texture_copy.size;
u32 remaining_input = input_width;
@@ -205,7 +205,7 @@ inline void Write(u32 addr, const T data) {
config.flags);
size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap);
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), contiguous_output_size);
+ VideoCore::g_renderer->rasterizer->InvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size);
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
break;
@@ -232,7 +232,7 @@ inline void Write(u32 addr, const T data) {
u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format);
u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format);
- VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), input_size);
+ VideoCore::g_renderer->rasterizer->FlushRegion(config.GetPhysicalInputAddress(), input_size);
for (u32 y = 0; y < output_height; ++y) {
for (u32 x = 0; x < output_width; ++x) {
@@ -339,7 +339,7 @@ inline void Write(u32 addr, const T data) {
g_regs.display_transfer_config.trigger = 0;
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size);
+ VideoCore::g_renderer->rasterizer->InvalidateRegion(config.GetPhysicalOutputAddress(), output_size);
}
break;
}
diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp
index 111b6a409..8eed6a50a 100644
--- a/src/core/loader/3dsx.cpp
+++ b/src/core/loader/3dsx.cpp
@@ -181,14 +181,14 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
for (unsigned current_inprogress = 0; current_inprogress < remaining && pos < end_pos; current_inprogress++) {
const auto& table = reloc_table[current_inprogress];
- LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)\n", current_segment_reloc_table,
+ LOG_TRACE(Loader, "(t=%d,skip=%u,patch=%u)", current_segment_reloc_table,
(u32)table.skip, (u32)table.patch);
pos += table.skip;
s32 num_patches = table.patch;
while (0 < num_patches && pos < end_pos) {
u32 in_addr = (u8*)pos - program_image.data();
u32 addr = TranslateAddr(*pos, &loadinfo, offsets);
- LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)\n",
+ LOG_TRACE(Loader, "Patching %08X <-- rel(%08X,%d) (%08X)",
base_addr + in_addr, addr, current_segment_reloc_table, *pos);
switch (current_segment_reloc_table) {
case 0:
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 8de95dacf..a7f2715ba 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -71,6 +71,7 @@ enum class ResultStatus {
ErrorNotUsed,
ErrorAlreadyLoaded,
ErrorMemoryAllocationFailed,
+ ErrorEncrypted,
};
static inline u32 MakeMagic(char a, char b, char c, char d) {
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 094d74100..68b3f546e 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -128,9 +128,8 @@ ResultStatus AppLoader_NCCH::LoadExec() {
if (ResultStatus::Success == ReadCode(code)) {
std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
(const char*)exheader_header.codeset_info.name, 8);
- u64 program_id = *reinterpret_cast<u64_le const*>(&ncch_header.program_id[0]);
- SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id);
+ SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, ncch_header.program_id);
codeset->code.offset = 0;
codeset->code.addr = exheader_header.codeset_info.text.address;
@@ -266,6 +265,11 @@ ResultStatus AppLoader_NCCH::Load() {
LOG_DEBUG(Loader, "Thread priority: 0x%X" , priority);
LOG_DEBUG(Loader, "Resource limit category: %d" , resource_limit_category);
+ if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
+ LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
+ return ResultStatus::ErrorEncrypted;
+ }
+
// Read ExeFS...
exefs_offset = ncch_header.exefs_offset * kBlockSize;
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index d875e4cf3..ca6772a78 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -17,31 +17,31 @@
struct NCCH_Header {
u8 signature[0x100];
- u32 magic;
- u32 content_size;
+ u32_le magic;
+ u32_le content_size;
u8 partition_id[8];
- u16 maker_code;
- u16 version;
+ u16_le maker_code;
+ u16_le version;
u8 reserved_0[4];
- u8 program_id[8];
+ u64_le program_id;
u8 reserved_1[0x10];
u8 logo_region_hash[0x20];
u8 product_code[0x10];
u8 extended_header_hash[0x20];
- u32 extended_header_size;
+ u32_le extended_header_size;
u8 reserved_2[4];
u8 flags[8];
- u32 plain_region_offset;
- u32 plain_region_size;
- u32 logo_region_offset;
- u32 logo_region_size;
- u32 exefs_offset;
- u32 exefs_size;
- u32 exefs_hash_region_size;
+ u32_le plain_region_offset;
+ u32_le plain_region_size;
+ u32_le logo_region_offset;
+ u32_le logo_region_size;
+ u32_le exefs_offset;
+ u32_le exefs_size;
+ u32_le exefs_hash_region_size;
u8 reserved_3[4];
- u32 romfs_offset;
- u32 romfs_size;
- u32 romfs_hash_region_size;
+ u32_le romfs_offset;
+ u32_le romfs_size;
+ u32_le romfs_hash_region_size;
u8 reserved_4[4];
u8 exefs_super_block_hash[0x20];
u8 romfs_super_block_hash[0x20];
@@ -109,8 +109,8 @@ struct ExHeader_StorageInfo {
};
struct ExHeader_ARM11_SystemLocalCaps {
- u8 program_id[8];
- u32 core_version;
+ u64_le program_id;
+ u32_le core_version;
u8 reserved_flags[2];
union {
u8 flags0;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index b80795e0c..fc79c3ee9 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -26,9 +26,9 @@ enum class PageType {
};
/**
- * A (reasonably) fast way of allowing switchable and remmapable process address spaces. It loosely
+ * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
* mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
- * fetching requirements when acessing. In the usual case of an access to regular memory, it only
+ * fetching requirements when accessing. In the usual case of an access to regular memory, it only
* requires an indexed fetch and a check for NULL.
*/
struct PageTable {
diff --git a/src/core/settings.h b/src/core/settings.h
index 0b05e5bee..97ddcdff9 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -6,6 +6,7 @@
#include <string>
#include <array>
+#include <common/file_util.h>
namespace Settings {
@@ -60,6 +61,10 @@ struct Values {
float bg_blue;
std::string log_filter;
+
+ // Debugging
+ bool use_gdbstub;
+ u16 gdbstub_port;
} extern values;
}
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 3cd84bf5e..7e9c56538 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -12,6 +12,8 @@
#include "video_core/video_core.h"
+#include "core/gdbstub/gdbstub.h"
+
namespace System {
void Init(EmuWindow* emu_window) {
@@ -22,9 +24,11 @@ void Init(EmuWindow* emu_window) {
Kernel::Init();
HLE::Init();
VideoCore::Init(emu_window);
+ GDBStub::Init();
}
void Shutdown() {
+ GDBStub::Shutdown();
VideoCore::Shutdown();
HLE::Shutdown();
Kernel::Shutdown();
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 8c9d76ab4..c3d7294d5 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,6 +1,7 @@
set(SRCS
renderer_opengl/gl_rasterizer.cpp
renderer_opengl/gl_rasterizer_cache.cpp
+ renderer_opengl/gl_shader_gen.cpp
renderer_opengl/gl_shader_util.cpp
renderer_opengl/gl_state.cpp
renderer_opengl/renderer_opengl.cpp
@@ -10,8 +11,10 @@ set(SRCS
pica.cpp
primitive_assembly.cpp
rasterizer.cpp
+ renderer_base.cpp
shader/shader.cpp
shader/shader_interpreter.cpp
+ swrasterizer.cpp
utils.cpp
video_core.cpp
)
@@ -21,21 +24,22 @@ set(HEADERS
renderer_opengl/gl_rasterizer.h
renderer_opengl/gl_rasterizer_cache.h
renderer_opengl/gl_resource_manager.h
+ renderer_opengl/gl_shader_gen.h
renderer_opengl/gl_shader_util.h
- renderer_opengl/gl_shaders.h
renderer_opengl/gl_state.h
renderer_opengl/pica_to_gl.h
renderer_opengl/renderer_opengl.h
clipper.h
command_processor.h
gpu_debugger.h
- hwrasterizer_base.h
pica.h
primitive_assembly.h
rasterizer.h
+ rasterizer_interface.h
renderer_base.h
shader/shader.h
shader/shader_interpreter.h
+ swrasterizer.h
utils.h
video_core.h
)
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp
index ed99c4f13..5d609da06 100644
--- a/src/video_core/clipper.cpp
+++ b/src/video_core/clipper.cpp
@@ -78,7 +78,7 @@ static void InitScreenCoordinates(OutputVertex& vtx)
vtx.screenpos[2] = viewport.offset_z + vtx.pos.z * inv_w * viewport.zscale;
}
-void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
+void ProcessTriangle(const OutputVertex &v0, const OutputVertex &v1, const OutputVertex &v2) {
using boost::container::static_vector;
// Clipping a planar n-gon against a plane will remove at least 1 vertex and introduces 2 at
diff --git a/src/video_core/clipper.h b/src/video_core/clipper.h
index 6ed01e877..f85d8d4c9 100644
--- a/src/video_core/clipper.h
+++ b/src/video_core/clipper.h
@@ -14,7 +14,7 @@ namespace Clipper {
using Shader::OutputVertex;
-void ProcessTriangle(OutputVertex& v0, OutputVertex& v1, OutputVertex& v2);
+void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2);
} // namespace
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index 47afd8938..35b976c60 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -157,6 +157,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// TODO: What happens if a loader overwrites a previous one's data?
for (unsigned component = 0; component < loader_config.component_count; ++component) {
+ if (component >= 12)
+ LOG_ERROR(HW_GPU, "Overflow in the vertex attribute loader %u trying to load component %u", loader, component);
u32 attribute_index = loader_config.GetComponent(component);
vertex_attribute_sources[attribute_index] = load_address;
vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
@@ -334,19 +336,14 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
}
}
- if (Settings::values.use_hw_renderer) {
- // Send to hardware renderer
- static auto AddHWTriangle = [](const Pica::Shader::OutputVertex& v0,
- const Pica::Shader::OutputVertex& v1,
- const Pica::Shader::OutputVertex& v2) {
- VideoCore::g_renderer->hw_rasterizer->AddTriangle(v0, v1, v2);
- };
+ // Send to renderer
+ using Pica::Shader::OutputVertex;
+ auto AddTriangle = [](
+ const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2) {
+ VideoCore::g_renderer->rasterizer->AddTriangle(v0, v1, v2);
+ };
- primitive_assembler.SubmitVertex(output, AddHWTriangle);
- } else {
- // Send to triangle clipper
- primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle);
- }
+ primitive_assembler.SubmitVertex(output, AddTriangle);
}
for (auto& range : memory_accesses.ranges) {
@@ -354,9 +351,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
range.second, range.first);
}
- if (Settings::values.use_hw_renderer) {
- VideoCore::g_renderer->hw_rasterizer->DrawTriangles();
- }
+ VideoCore::g_renderer->rasterizer->DrawTriangles();
#if PICA_DUMP_GEOMETRY
geometry_dumper.Dump();
@@ -473,7 +468,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
break;
}
- VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id);
+ VideoCore::g_renderer->rasterizer->NotifyPicaRegisterChanged(id);
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::PicaCommandProcessed, reinterpret_cast<void*>(&id));
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index aa1f1484c..4f66dbd65 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -46,10 +46,8 @@ void DebugContext::OnEvent(Event event, void* data) {
{
std::unique_lock<std::mutex> lock(breakpoint_mutex);
- if (Settings::values.use_hw_renderer) {
- // Commit the hardware renderer's framebuffer so it will show on debug widgets
- VideoCore::g_renderer->hw_rasterizer->CommitFramebuffer();
- }
+ // Commit the hardware renderer's framebuffer so it will show on debug widgets
+ VideoCore::g_renderer->rasterizer->FlushFramebuffer();
// TODO: Should stop the CPU thread here once we multithread emulation.
@@ -641,7 +639,7 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
// Initialize write structure
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (png_ptr == nullptr) {
- LOG_ERROR(Debug_GPU, "Could not allocate write struct\n");
+ LOG_ERROR(Debug_GPU, "Could not allocate write struct");
goto finalise;
}
@@ -649,13 +647,13 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
// Initialize info structure
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == nullptr) {
- LOG_ERROR(Debug_GPU, "Could not allocate info struct\n");
+ LOG_ERROR(Debug_GPU, "Could not allocate info struct");
goto finalise;
}
// Setup Exception handling
if (setjmp(png_jmpbuf(png_ptr))) {
- LOG_ERROR(Debug_GPU, "Error during png creation\n");
+ LOG_ERROR(Debug_GPU, "Error during png creation");
goto finalise;
}
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index ff81b409d..2f1b2dec4 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -317,6 +317,7 @@ struct Regs {
};
union {
+ u32 sources_raw;
BitField< 0, 4, Source> color_source1;
BitField< 4, 4, Source> color_source2;
BitField< 8, 4, Source> color_source3;
@@ -326,6 +327,7 @@ struct Regs {
};
union {
+ u32 modifiers_raw;
BitField< 0, 4, ColorModifier> color_modifier1;
BitField< 4, 4, ColorModifier> color_modifier2;
BitField< 8, 4, ColorModifier> color_modifier3;
@@ -335,6 +337,7 @@ struct Regs {
};
union {
+ u32 ops_raw;
BitField< 0, 4, Operation> color_op;
BitField<16, 4, Operation> alpha_op;
};
@@ -348,6 +351,7 @@ struct Regs {
};
union {
+ u32 scales_raw;
BitField< 0, 2, u32> color_scale;
BitField<16, 2, u32> alpha_scale;
};
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp
index 44a8dbfe9..d5a0a96a4 100644
--- a/src/video_core/primitive_assembly.cpp
+++ b/src/video_core/primitive_assembly.cpp
@@ -39,13 +39,12 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl
buffer[buffer_index] = vtx;
- if (topology == Regs::TriangleTopology::Strip) {
- strip_ready |= (buffer_index == 1);
+ strip_ready |= (buffer_index == 1);
+
+ if (topology == Regs::TriangleTopology::Strip)
buffer_index = !buffer_index;
- } else if (topology == Regs::TriangleTopology::Fan) {
+ else if (topology == Regs::TriangleTopology::Fan)
buffer_index = 1;
- strip_ready = true;
- }
break;
default:
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index 7abf60292..ecfdbc9e8 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -462,7 +462,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
}
default:
- LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x\n", (int)mode);
+ LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x", (int)mode);
UNIMPLEMENTED();
return 0;
}
@@ -498,7 +498,8 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
// with some basic arithmetic. Alpha combiners can be configured separately but work
// analogously.
Math::Vec4<u8> combiner_output;
- Math::Vec4<u8> combiner_buffer = {
+ Math::Vec4<u8> combiner_buffer = {0, 0, 0, 0};
+ Math::Vec4<u8> next_combiner_buffer = {
regs.tev_combiner_buffer_color.r, regs.tev_combiner_buffer_color.g,
regs.tev_combiner_buffer_color.b, regs.tev_combiner_buffer_color.a
};
@@ -541,7 +542,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
return combiner_output;
default:
- LOG_ERROR(HW_GPU, "Unknown color combiner source %d\n", (int)source);
+ LOG_ERROR(HW_GPU, "Unknown color combiner source %d", (int)source);
UNIMPLEMENTED();
return {0, 0, 0, 0};
}
@@ -679,7 +680,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
return { (u8)result, (u8)result, (u8)result };
}
default:
- LOG_ERROR(HW_GPU, "Unknown color combiner operation %d\n", (int)op);
+ LOG_ERROR(HW_GPU, "Unknown color combiner operation %d", (int)op);
UNIMPLEMENTED();
return {0, 0, 0};
}
@@ -716,7 +717,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
default:
- LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d\n", (int)op);
+ LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d", (int)op);
UNIMPLEMENTED();
return 0;
}
@@ -747,14 +748,16 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
combiner_output[2] = std::min((unsigned)255, color_output.b() * tev_stage.GetColorMultiplier());
combiner_output[3] = std::min((unsigned)255, alpha_output * tev_stage.GetAlphaMultiplier());
+ combiner_buffer = next_combiner_buffer;
+
if (regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index)) {
- combiner_buffer.r() = combiner_output.r();
- combiner_buffer.g() = combiner_output.g();
- combiner_buffer.b() = combiner_output.b();
+ next_combiner_buffer.r() = combiner_output.r();
+ next_combiner_buffer.g() = combiner_output.g();
+ next_combiner_buffer.b() = combiner_output.b();
}
if (regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index)) {
- combiner_buffer.a() = combiner_output.a();
+ next_combiner_buffer.a() = combiner_output.a();
}
}
diff --git a/src/video_core/hwrasterizer_base.h b/src/video_core/rasterizer_interface.h
index 54b8892fb..008c5827b 100644
--- a/src/video_core/hwrasterizer_base.h
+++ b/src/video_core/rasterizer_interface.h
@@ -12,10 +12,11 @@ struct OutputVertex;
}
}
-class HWRasterizer {
+namespace VideoCore {
+
+class RasterizerInterface {
public:
- virtual ~HWRasterizer() {
- }
+ virtual ~RasterizerInterface() {}
/// Initialize API-specific GPU objects
virtual void InitObjects() = 0;
@@ -32,14 +33,16 @@ public:
virtual void DrawTriangles() = 0;
/// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
- virtual void CommitFramebuffer() = 0;
+ virtual void FlushFramebuffer() = 0;
/// Notify rasterizer that the specified PICA register has been changed
virtual void NotifyPicaRegisterChanged(u32 id) = 0;
- /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
- virtual void NotifyPreRead(PAddr addr, u32 size) = 0;
+ /// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory.
+ virtual void FlushRegion(PAddr addr, u32 size) = 0;
- /// Notify rasterizer that a 3DS memory region has been changed
- virtual void NotifyFlush(PAddr addr, u32 size) = 0;
+ /// Notify rasterizer that any caches of the specified region should be discraded and reloaded from 3DS memory.
+ virtual void InvalidateRegion(PAddr addr, u32 size) = 0;
};
+
+}
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
new file mode 100644
index 000000000..6467ff723
--- /dev/null
+++ b/src/video_core/renderer_base.cpp
@@ -0,0 +1,29 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+
+#include "common/make_unique.h"
+
+#include "core/settings.h"
+
+#include "video_core/renderer_base.h"
+#include "video_core/video_core.h"
+#include "video_core/swrasterizer.h"
+#include "video_core/renderer_opengl/gl_rasterizer.h"
+
+void RendererBase::RefreshRasterizerSetting() {
+ bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled;
+ if (rasterizer == nullptr || opengl_rasterizer_active != hw_renderer_enabled) {
+ opengl_rasterizer_active = hw_renderer_enabled;
+
+ if (hw_renderer_enabled) {
+ rasterizer = Common::make_unique<RasterizerOpenGL>();
+ } else {
+ rasterizer = Common::make_unique<VideoCore::SWRasterizer>();
+ }
+ rasterizer->InitObjects();
+ rasterizer->Reset();
+ }
+}
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 6587bcf27..506bff815 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -8,7 +8,7 @@
#include "common/common_types.h"
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/rasterizer_interface.h"
class EmuWindow;
@@ -54,10 +54,14 @@ public:
return m_current_frame;
}
- std::unique_ptr<HWRasterizer> hw_rasterizer;
+ void RefreshRasterizerSetting();
+
+ std::unique_ptr<VideoCore::RasterizerInterface> rasterizer;
protected:
f32 m_current_fps; ///< Current framerate, should be set by the renderer
int m_current_frame; ///< Current frame, should be set by the renderer
+private:
+ bool opengl_rasterizer_active = false;
};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a613fe136..092351dce 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -8,6 +8,8 @@
#include <glad/glad.h>
#include "common/color.h"
+#include "common/file_util.h"
+#include "common/make_unique.h"
#include "common/math_util.h"
#include "common/microprofile.h"
#include "common/profiler.h"
@@ -19,7 +21,7 @@
#include "video_core/pica.h"
#include "video_core/utils.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
-#include "video_core/renderer_opengl/gl_shaders.h"
+#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
#include "video_core/renderer_opengl/pica_to_gl.h"
@@ -38,69 +40,42 @@ RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr
RasterizerOpenGL::~RasterizerOpenGL() { }
void RasterizerOpenGL::InitObjects() {
- // Create the hardware shader program and get attrib/uniform locations
- shader.Create(GLShaders::g_vertex_shader_hw, GLShaders::g_fragment_shader_hw);
- attrib_position = glGetAttribLocation(shader.handle, "vert_position");
- attrib_color = glGetAttribLocation(shader.handle, "vert_color");
- attrib_texcoords = glGetAttribLocation(shader.handle, "vert_texcoords");
-
- uniform_alphatest_enabled = glGetUniformLocation(shader.handle, "alphatest_enabled");
- uniform_alphatest_func = glGetUniformLocation(shader.handle, "alphatest_func");
- uniform_alphatest_ref = glGetUniformLocation(shader.handle, "alphatest_ref");
-
- uniform_tex = glGetUniformLocation(shader.handle, "tex");
-
- uniform_tev_combiner_buffer_color = glGetUniformLocation(shader.handle, "tev_combiner_buffer_color");
-
- const auto tev_stages = Pica::g_state.regs.GetTevStages();
- for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
- auto& uniform_tev_cfg = uniform_tev_cfgs[tev_stage_index];
-
- std::string tev_ref_str = "tev_cfgs[" + std::to_string(tev_stage_index) + "]";
- uniform_tev_cfg.enabled = glGetUniformLocation(shader.handle, (tev_ref_str + ".enabled").c_str());
- uniform_tev_cfg.color_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_sources").c_str());
- uniform_tev_cfg.alpha_sources = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_sources").c_str());
- uniform_tev_cfg.color_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_modifiers").c_str());
- uniform_tev_cfg.alpha_modifiers = glGetUniformLocation(shader.handle, (tev_ref_str + ".alpha_modifiers").c_str());
- uniform_tev_cfg.color_alpha_op = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_op").c_str());
- uniform_tev_cfg.color_alpha_multiplier = glGetUniformLocation(shader.handle, (tev_ref_str + ".color_alpha_multiplier").c_str());
- uniform_tev_cfg.const_color = glGetUniformLocation(shader.handle, (tev_ref_str + ".const_color").c_str());
- uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str());
- }
-
// Create sampler objects
for (size_t i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create();
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
}
- // Generate VBO and VAO
+ // Generate VBO, VAO and UBO
vertex_buffer.Create();
vertex_array.Create();
+ uniform_buffer.Create();
- // Update OpenGL state
state.draw.vertex_array = vertex_array.handle;
state.draw.vertex_buffer = vertex_buffer.handle;
- state.draw.shader_program = shader.handle;
-
+ state.draw.uniform_buffer = uniform_buffer.handle;
state.Apply();
- // Set the texture samplers to correspond to different texture units
- glUniform1i(uniform_tex, 0);
- glUniform1i(uniform_tex + 1, 1);
- glUniform1i(uniform_tex + 2, 2);
+ // Bind the UBO to binding point 0
+ glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer.handle);
+
+ uniform_block_data.dirty = true;
// Set vertex attributes
- glVertexAttribPointer(attrib_position, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
- glVertexAttribPointer(attrib_color, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
- glVertexAttribPointer(attrib_texcoords, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
- glVertexAttribPointer(attrib_texcoords + 1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
- glVertexAttribPointer(attrib_texcoords + 2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
- glEnableVertexAttribArray(attrib_position);
- glEnableVertexAttribArray(attrib_color);
- glEnableVertexAttribArray(attrib_texcoords);
- glEnableVertexAttribArray(attrib_texcoords + 1);
- glEnableVertexAttribArray(attrib_texcoords + 2);
+ glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION);
+
+ glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, color));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR);
+
+ glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0));
+ glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD1, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1));
+ glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD2, 2, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD0);
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1);
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2);
+
+ SetShader();
// Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
fb_color_texture.texture.Create();
@@ -150,63 +125,17 @@ void RasterizerOpenGL::InitObjects() {
}
void RasterizerOpenGL::Reset() {
- const auto& regs = Pica::g_state.regs;
-
SyncCullMode();
SyncBlendEnabled();
SyncBlendFuncs();
SyncBlendColor();
- SyncAlphaTest();
SyncLogicOp();
SyncStencilTest();
SyncDepthTest();
- // TEV stage 0
- SyncTevSources(0, regs.tev_stage0);
- SyncTevModifiers(0, regs.tev_stage0);
- SyncTevOps(0, regs.tev_stage0);
- SyncTevColor(0, regs.tev_stage0);
- SyncTevMultipliers(0, regs.tev_stage0);
-
- // TEV stage 1
- SyncTevSources(1, regs.tev_stage1);
- SyncTevModifiers(1, regs.tev_stage1);
- SyncTevOps(1, regs.tev_stage1);
- SyncTevColor(1, regs.tev_stage1);
- SyncTevMultipliers(1, regs.tev_stage1);
-
- // TEV stage 2
- SyncTevSources(2, regs.tev_stage2);
- SyncTevModifiers(2, regs.tev_stage2);
- SyncTevOps(2, regs.tev_stage2);
- SyncTevColor(2, regs.tev_stage2);
- SyncTevMultipliers(2, regs.tev_stage2);
-
- // TEV stage 3
- SyncTevSources(3, regs.tev_stage3);
- SyncTevModifiers(3, regs.tev_stage3);
- SyncTevOps(3, regs.tev_stage3);
- SyncTevColor(3, regs.tev_stage3);
- SyncTevMultipliers(3, regs.tev_stage3);
-
- // TEV stage 4
- SyncTevSources(4, regs.tev_stage4);
- SyncTevModifiers(4, regs.tev_stage4);
- SyncTevOps(4, regs.tev_stage4);
- SyncTevColor(4, regs.tev_stage4);
- SyncTevMultipliers(4, regs.tev_stage4);
-
- // TEV stage 5
- SyncTevSources(5, regs.tev_stage5);
- SyncTevModifiers(5, regs.tev_stage5);
- SyncTevOps(5, regs.tev_stage5);
- SyncTevColor(5, regs.tev_stage5);
- SyncTevMultipliers(5, regs.tev_stage5);
+ SetShader();
- SyncCombinerColor();
- SyncCombinerWriteFlags();
-
- res_cache.FullFlush();
+ res_cache.InvalidateAll();
}
void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
@@ -221,6 +150,16 @@ void RasterizerOpenGL::DrawTriangles() {
SyncFramebuffer();
SyncDrawState();
+ if (state.draw.shader_dirty) {
+ SetShader();
+ state.draw.shader_dirty = false;
+ }
+
+ if (uniform_block_data.dirty) {
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW);
+ uniform_block_data.dirty = false;
+ }
+
glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), GL_STREAM_DRAW);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size());
@@ -237,11 +176,11 @@ void RasterizerOpenGL::DrawTriangles() {
u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format)
* regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
- res_cache.NotifyFlush(cur_fb_color_addr, cur_fb_color_size, true);
- res_cache.NotifyFlush(cur_fb_depth_addr, cur_fb_depth_size, true);
+ res_cache.InvalidateInRange(cur_fb_color_addr, cur_fb_color_size, true);
+ res_cache.InvalidateInRange(cur_fb_depth_addr, cur_fb_depth_size, true);
}
-void RasterizerOpenGL::CommitFramebuffer() {
+void RasterizerOpenGL::FlushFramebuffer() {
CommitColorBuffer();
CommitDepthBuffer();
}
@@ -249,9 +188,6 @@ void RasterizerOpenGL::CommitFramebuffer() {
void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
const auto& regs = Pica::g_state.regs;
- if (!Settings::values.use_hw_renderer)
- return;
-
switch(id) {
// Culling
case PICA_REG_INDEX(cull_mode):
@@ -272,6 +208,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
// Alpha test
case PICA_REG_INDEX(output_merger.alpha_test):
SyncAlphaTest();
+ state.draw.shader_dirty = true;
break;
// Stencil test
@@ -290,126 +227,63 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
SyncLogicOp();
break;
- // TEV stage 0
+ // TEV stages
case PICA_REG_INDEX(tev_stage0.color_source1):
- SyncTevSources(0, regs.tev_stage0);
- break;
case PICA_REG_INDEX(tev_stage0.color_modifier1):
- SyncTevModifiers(0, regs.tev_stage0);
- break;
case PICA_REG_INDEX(tev_stage0.color_op):
- SyncTevOps(0, regs.tev_stage0);
- break;
- case PICA_REG_INDEX(tev_stage0.const_r):
- SyncTevColor(0, regs.tev_stage0);
- break;
case PICA_REG_INDEX(tev_stage0.color_scale):
- SyncTevMultipliers(0, regs.tev_stage0);
- break;
-
- // TEV stage 1
case PICA_REG_INDEX(tev_stage1.color_source1):
- SyncTevSources(1, regs.tev_stage1);
- break;
case PICA_REG_INDEX(tev_stage1.color_modifier1):
- SyncTevModifiers(1, regs.tev_stage1);
- break;
case PICA_REG_INDEX(tev_stage1.color_op):
- SyncTevOps(1, regs.tev_stage1);
- break;
- case PICA_REG_INDEX(tev_stage1.const_r):
- SyncTevColor(1, regs.tev_stage1);
- break;
case PICA_REG_INDEX(tev_stage1.color_scale):
- SyncTevMultipliers(1, regs.tev_stage1);
- break;
-
- // TEV stage 2
case PICA_REG_INDEX(tev_stage2.color_source1):
- SyncTevSources(2, regs.tev_stage2);
- break;
case PICA_REG_INDEX(tev_stage2.color_modifier1):
- SyncTevModifiers(2, regs.tev_stage2);
- break;
case PICA_REG_INDEX(tev_stage2.color_op):
- SyncTevOps(2, regs.tev_stage2);
- break;
- case PICA_REG_INDEX(tev_stage2.const_r):
- SyncTevColor(2, regs.tev_stage2);
- break;
case PICA_REG_INDEX(tev_stage2.color_scale):
- SyncTevMultipliers(2, regs.tev_stage2);
- break;
-
- // TEV stage 3
case PICA_REG_INDEX(tev_stage3.color_source1):
- SyncTevSources(3, regs.tev_stage3);
- break;
case PICA_REG_INDEX(tev_stage3.color_modifier1):
- SyncTevModifiers(3, regs.tev_stage3);
- break;
case PICA_REG_INDEX(tev_stage3.color_op):
- SyncTevOps(3, regs.tev_stage3);
- break;
- case PICA_REG_INDEX(tev_stage3.const_r):
- SyncTevColor(3, regs.tev_stage3);
- break;
case PICA_REG_INDEX(tev_stage3.color_scale):
- SyncTevMultipliers(3, regs.tev_stage3);
- break;
-
- // TEV stage 4
case PICA_REG_INDEX(tev_stage4.color_source1):
- SyncTevSources(4, regs.tev_stage4);
- break;
case PICA_REG_INDEX(tev_stage4.color_modifier1):
- SyncTevModifiers(4, regs.tev_stage4);
- break;
case PICA_REG_INDEX(tev_stage4.color_op):
- SyncTevOps(4, regs.tev_stage4);
+ case PICA_REG_INDEX(tev_stage4.color_scale):
+ case PICA_REG_INDEX(tev_stage5.color_source1):
+ case PICA_REG_INDEX(tev_stage5.color_modifier1):
+ case PICA_REG_INDEX(tev_stage5.color_op):
+ case PICA_REG_INDEX(tev_stage5.color_scale):
+ case PICA_REG_INDEX(tev_combiner_buffer_input):
+ state.draw.shader_dirty = true;
break;
- case PICA_REG_INDEX(tev_stage4.const_r):
- SyncTevColor(4, regs.tev_stage4);
+ case PICA_REG_INDEX(tev_stage0.const_r):
+ SyncTevConstColor(0, regs.tev_stage0);
break;
- case PICA_REG_INDEX(tev_stage4.color_scale):
- SyncTevMultipliers(4, regs.tev_stage4);
+ case PICA_REG_INDEX(tev_stage1.const_r):
+ SyncTevConstColor(1, regs.tev_stage1);
break;
-
- // TEV stage 5
- case PICA_REG_INDEX(tev_stage5.color_source1):
- SyncTevSources(5, regs.tev_stage5);
+ case PICA_REG_INDEX(tev_stage2.const_r):
+ SyncTevConstColor(2, regs.tev_stage2);
break;
- case PICA_REG_INDEX(tev_stage5.color_modifier1):
- SyncTevModifiers(5, regs.tev_stage5);
+ case PICA_REG_INDEX(tev_stage3.const_r):
+ SyncTevConstColor(3, regs.tev_stage3);
break;
- case PICA_REG_INDEX(tev_stage5.color_op):
- SyncTevOps(5, regs.tev_stage5);
+ case PICA_REG_INDEX(tev_stage4.const_r):
+ SyncTevConstColor(4, regs.tev_stage4);
break;
case PICA_REG_INDEX(tev_stage5.const_r):
- SyncTevColor(5, regs.tev_stage5);
- break;
- case PICA_REG_INDEX(tev_stage5.color_scale):
- SyncTevMultipliers(5, regs.tev_stage5);
+ SyncTevConstColor(5, regs.tev_stage5);
break;
// TEV combiner buffer color
case PICA_REG_INDEX(tev_combiner_buffer_color):
SyncCombinerColor();
break;
-
- // TEV combiner buffer write flags
- case PICA_REG_INDEX(tev_combiner_buffer_input):
- SyncCombinerWriteFlags();
- break;
}
}
-void RasterizerOpenGL::NotifyPreRead(PAddr addr, u32 size) {
+void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) {
const auto& regs = Pica::g_state.regs;
- if (!Settings::values.use_hw_renderer)
- return;
-
PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
* regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
@@ -426,12 +300,9 @@ void RasterizerOpenGL::NotifyPreRead(PAddr addr, u32 size) {
CommitDepthBuffer();
}
-void RasterizerOpenGL::NotifyFlush(PAddr addr, u32 size) {
+void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) {
const auto& regs = Pica::g_state.regs;
- if (!Settings::values.use_hw_renderer)
- return;
-
PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
* regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
@@ -448,7 +319,7 @@ void RasterizerOpenGL::NotifyFlush(PAddr addr, u32 size) {
ReloadDepthBuffer();
// Notify cache of flush in case the region touches a cached resource
- res_cache.NotifyFlush(addr, size);
+ res_cache.InvalidateInRange(addr, size);
}
void RasterizerOpenGL::SamplerInfo::Create() {
@@ -592,6 +463,47 @@ void RasterizerOpenGL::ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::
state.Apply();
}
+void RasterizerOpenGL::SetShader() {
+ PicaShaderConfig config = PicaShaderConfig::CurrentConfig();
+ std::unique_ptr<PicaShader> shader = Common::make_unique<PicaShader>();
+
+ // Find (or generate) the GLSL shader for the current TEV state
+ auto cached_shader = shader_cache.find(config);
+ if (cached_shader != shader_cache.end()) {
+ current_shader = cached_shader->second.get();
+
+ state.draw.shader_program = current_shader->shader.handle;
+ state.Apply();
+ } else {
+ LOG_DEBUG(Render_OpenGL, "Creating new shader");
+
+ shader->shader.Create(GLShader::GenerateVertexShader().c_str(), GLShader::GenerateFragmentShader(config).c_str());
+
+ state.draw.shader_program = shader->shader.handle;
+ state.Apply();
+
+ // Set the texture samplers to correspond to different texture units
+ GLuint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]");
+ if (uniform_tex != -1) { glUniform1i(uniform_tex, 0); }
+ uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[1]");
+ if (uniform_tex != -1) { glUniform1i(uniform_tex, 1); }
+ uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]");
+ if (uniform_tex != -1) { glUniform1i(uniform_tex, 2); }
+
+ current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
+
+ unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
+ glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
+ }
+
+ // Update uniforms
+ SyncAlphaTest();
+ SyncCombinerColor();
+ auto& tev_stages = Pica::g_state.regs.GetTevStages();
+ for (int index = 0; index < tev_stages.size(); ++index)
+ SyncTevConstColor(index, tev_stages[index]);
+}
+
void RasterizerOpenGL::SyncFramebuffer() {
const auto& regs = Pica::g_state.regs;
@@ -675,12 +587,12 @@ void RasterizerOpenGL::SyncCullMode() {
case Pica::Regs::CullMode::KeepClockWise:
state.cull.enabled = true;
- state.cull.mode = GL_BACK;
+ state.cull.front_face = GL_CW;
break;
case Pica::Regs::CullMode::KeepCounterClockWise:
state.cull.enabled = true;
- state.cull.mode = GL_FRONT;
+ state.cull.front_face = GL_CCW;
break;
default:
@@ -712,9 +624,10 @@ void RasterizerOpenGL::SyncBlendColor() {
void RasterizerOpenGL::SyncAlphaTest() {
const auto& regs = Pica::g_state.regs;
- glUniform1i(uniform_alphatest_enabled, regs.output_merger.alpha_test.enable);
- glUniform1i(uniform_alphatest_func, (GLint)regs.output_merger.alpha_test.func.Value());
- glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f);
+ if (regs.output_merger.alpha_test.ref != uniform_block_data.data.alphatest_ref) {
+ uniform_block_data.data.alphatest_ref = regs.output_merger.alpha_test.ref;
+ uniform_block_data.dirty = true;
+ }
}
void RasterizerOpenGL::SyncLogicOp() {
@@ -744,55 +657,19 @@ void RasterizerOpenGL::SyncDepthTest() {
state.depth.write_mask = regs.output_merger.depth_write_enable ? GL_TRUE : GL_FALSE;
}
-void RasterizerOpenGL::SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- GLint color_srcs[3] = { (GLint)config.color_source1.Value(),
- (GLint)config.color_source2.Value(),
- (GLint)config.color_source3.Value() };
- GLint alpha_srcs[3] = { (GLint)config.alpha_source1.Value(),
- (GLint)config.alpha_source2.Value(),
- (GLint)config.alpha_source3.Value() };
-
- glUniform3iv(uniform_tev_cfgs[stage_index].color_sources, 1, color_srcs);
- glUniform3iv(uniform_tev_cfgs[stage_index].alpha_sources, 1, alpha_srcs);
-}
-
-void RasterizerOpenGL::SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- GLint color_mods[3] = { (GLint)config.color_modifier1.Value(),
- (GLint)config.color_modifier2.Value(),
- (GLint)config.color_modifier3.Value() };
- GLint alpha_mods[3] = { (GLint)config.alpha_modifier1.Value(),
- (GLint)config.alpha_modifier2.Value(),
- (GLint)config.alpha_modifier3.Value() };
-
- glUniform3iv(uniform_tev_cfgs[stage_index].color_modifiers, 1, color_mods);
- glUniform3iv(uniform_tev_cfgs[stage_index].alpha_modifiers, 1, alpha_mods);
-}
-
-void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_op, (GLint)config.color_op.Value(), (GLint)config.alpha_op.Value());
-}
-
-void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- auto const_color = PicaToGL::ColorRGBA8(config.const_color);
- glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data());
-}
-
-void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
- glUniform2i(uniform_tev_cfgs[stage_index].color_alpha_multiplier, config.GetColorMultiplier(), config.GetAlphaMultiplier());
-}
-
void RasterizerOpenGL::SyncCombinerColor() {
auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
- glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data());
+ if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) {
+ uniform_block_data.data.tev_combiner_buffer_color = combiner_color;
+ uniform_block_data.dirty = true;
+ }
}
-void RasterizerOpenGL::SyncCombinerWriteFlags() {
- const auto& regs = Pica::g_state.regs;
- const auto tev_stages = regs.GetTevStages();
- for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
- glUniform2i(uniform_tev_cfgs[tev_stage_index].updates_combiner_buffer_color_alpha,
- regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(tev_stage_index),
- regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(tev_stage_index));
+void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevStageConfig& tev_stage) {
+ auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color);
+ if (const_color != uniform_block_data.data.const_color[stage_index]) {
+ uniform_block_data.data.const_color[stage_index] = const_color;
+ uniform_block_data.dirty = true;
}
}
@@ -806,9 +683,8 @@ void RasterizerOpenGL::SyncDrawState() {
// OpenGL uses different y coordinates, so negate corner offset and flip origin
// TODO: Ensure viewport_corner.x should not be negated or origin flipped
// TODO: Use floating-point viewports for accuracy if supported
- glViewport((GLsizei)static_cast<float>(regs.viewport_corner.x),
- -(GLsizei)static_cast<float>(regs.viewport_corner.y)
- + regs.framebuffer.GetHeight() - viewport_height,
+ glViewport((GLsizei)regs.viewport_corner.x,
+ (GLsizei)regs.viewport_corner.y,
viewport_width, viewport_height);
// Sync bound texture(s), upload if not cached
@@ -824,12 +700,7 @@ void RasterizerOpenGL::SyncDrawState() {
}
}
- // Skip processing TEV stages that simply pass the previous stage results through
- const auto tev_stages = regs.GetTevStages();
- for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size(); ++tev_stage_index) {
- glUniform1i(uniform_tev_cfgs[tev_stage_index].enabled, !IsPassThroughTevStage(tev_stages[tev_stage_index]));
- }
-
+ state.draw.uniform_buffer = uniform_buffer.handle;
state.Apply();
}
@@ -852,7 +723,7 @@ void RasterizerOpenGL::ReloadColorBuffer() {
for (int x = 0; x < fb_color_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_color_texture.width) * bytes_per_pixel;
+ u32 gl_pixel_index = (x + (fb_color_texture.height - 1 - y) * fb_color_texture.width) * bytes_per_pixel;
u8* pixel = color_buffer + dst_offset;
memcpy(&temp_fb_color_buffer[gl_pixel_index], pixel, bytes_per_pixel);
@@ -898,7 +769,7 @@ void RasterizerOpenGL::ReloadDepthBuffer() {
for (int x = 0; x < fb_depth_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_depth_texture.width);
+ u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width);
u8* pixel = depth_buffer + dst_offset;
u32 depth_stencil = *(u32*)pixel;
@@ -910,7 +781,7 @@ void RasterizerOpenGL::ReloadDepthBuffer() {
for (int x = 0; x < fb_depth_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp;
+ u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width) * gl_bpp;
u8* pixel = depth_buffer + dst_offset;
memcpy(&temp_fb_depth_data[gl_pixel_index], pixel, bytes_per_pixel);
@@ -965,7 +836,7 @@ void RasterizerOpenGL::CommitColorBuffer() {
for (int x = 0; x < fb_color_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel;
+ u32 gl_pixel_index = x * bytes_per_pixel + (fb_color_texture.height - 1 - y) * fb_color_texture.width * bytes_per_pixel;
u8* pixel = color_buffer + dst_offset;
memcpy(pixel, &temp_gl_color_buffer[gl_pixel_index], bytes_per_pixel);
@@ -1007,7 +878,7 @@ void RasterizerOpenGL::CommitDepthBuffer() {
for (int x = 0; x < fb_depth_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_depth_texture.width);
+ u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width);
u8* pixel = depth_buffer + dst_offset;
u32 depth_stencil = ((u32*)temp_gl_depth_data)[gl_pixel_index];
@@ -1019,7 +890,7 @@ void RasterizerOpenGL::CommitDepthBuffer() {
for (int x = 0; x < fb_depth_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp;
+ u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width) * gl_bpp;
u8* pixel = depth_buffer + dst_offset;
memcpy(pixel, &temp_gl_depth_data[gl_pixel_index], bytes_per_pixel);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 1fe307846..92b1f812e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -4,60 +4,128 @@
#pragma once
+#include <cstddef>
+#include <cstring>
+#include <memory>
#include <vector>
+#include <unordered_map>
#include "common/common_types.h"
+#include "common/hash.h"
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/pica.h"
+#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/shader/shader_interpreter.h"
-class RasterizerOpenGL : public HWRasterizer {
+/**
+ * This struct contains all state used to generate the GLSL shader program that emulates the current
+ * Pica register configuration. This struct is used as a cache key for generated GLSL shader
+ * programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by
+ * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where
+ * Pica state is not being captured in the shader cache key, thereby resulting in (what should be)
+ * two separate shaders sharing the same key.
+ */
+struct PicaShaderConfig {
+ /// Construct a PicaShaderConfig with the current Pica register configuration.
+ static PicaShaderConfig CurrentConfig() {
+ PicaShaderConfig res;
+ const auto& regs = Pica::g_state.regs;
+
+ res.alpha_test_func = regs.output_merger.alpha_test.enable ?
+ regs.output_merger.alpha_test.func.Value() : Pica::Regs::CompareFunc::Always;
+
+ // Copy relevant TevStageConfig fields only. We're doing this manually (instead of calling
+ // the GetTevStages() function) because BitField explicitly disables copies.
+
+ res.tev_stages[0].sources_raw = regs.tev_stage0.sources_raw;
+ res.tev_stages[1].sources_raw = regs.tev_stage1.sources_raw;
+ res.tev_stages[2].sources_raw = regs.tev_stage2.sources_raw;
+ res.tev_stages[3].sources_raw = regs.tev_stage3.sources_raw;
+ res.tev_stages[4].sources_raw = regs.tev_stage4.sources_raw;
+ res.tev_stages[5].sources_raw = regs.tev_stage5.sources_raw;
+
+ res.tev_stages[0].modifiers_raw = regs.tev_stage0.modifiers_raw;
+ res.tev_stages[1].modifiers_raw = regs.tev_stage1.modifiers_raw;
+ res.tev_stages[2].modifiers_raw = regs.tev_stage2.modifiers_raw;
+ res.tev_stages[3].modifiers_raw = regs.tev_stage3.modifiers_raw;
+ res.tev_stages[4].modifiers_raw = regs.tev_stage4.modifiers_raw;
+ res.tev_stages[5].modifiers_raw = regs.tev_stage5.modifiers_raw;
+
+ res.tev_stages[0].ops_raw = regs.tev_stage0.ops_raw;
+ res.tev_stages[1].ops_raw = regs.tev_stage1.ops_raw;
+ res.tev_stages[2].ops_raw = regs.tev_stage2.ops_raw;
+ res.tev_stages[3].ops_raw = regs.tev_stage3.ops_raw;
+ res.tev_stages[4].ops_raw = regs.tev_stage4.ops_raw;
+ res.tev_stages[5].ops_raw = regs.tev_stage5.ops_raw;
+
+ res.tev_stages[0].scales_raw = regs.tev_stage0.scales_raw;
+ res.tev_stages[1].scales_raw = regs.tev_stage1.scales_raw;
+ res.tev_stages[2].scales_raw = regs.tev_stage2.scales_raw;
+ res.tev_stages[3].scales_raw = regs.tev_stage3.scales_raw;
+ res.tev_stages[4].scales_raw = regs.tev_stage4.scales_raw;
+ res.tev_stages[5].scales_raw = regs.tev_stage5.scales_raw;
+
+ res.combiner_buffer_input =
+ regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
+ regs.tev_combiner_buffer_input.update_mask_a.Value() << 4;
+
+ return res;
+ }
+
+ bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
+ return (stage_index < 4) && (combiner_buffer_input & (1 << stage_index));
+ }
+
+ bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
+ return (stage_index < 4) && ((combiner_buffer_input >> 4) & (1 << stage_index));
+ }
+
+ bool operator ==(const PicaShaderConfig& o) const {
+ return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0;
+ };
+
+ Pica::Regs::CompareFunc alpha_test_func;
+ std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
+ u8 combiner_buffer_input;
+};
+
+namespace std {
+
+template <>
+struct hash<PicaShaderConfig> {
+ size_t operator()(const PicaShaderConfig& k) const {
+ return Common::ComputeHash64(&k, sizeof(PicaShaderConfig));
+ }
+};
+
+} // namespace std
+
+class RasterizerOpenGL : public VideoCore::RasterizerInterface {
public:
RasterizerOpenGL();
~RasterizerOpenGL() override;
- /// Initialize API-specific GPU objects
void InitObjects() override;
-
- /// Reset the rasterizer, such as flushing all caches and updating all state
void Reset() override;
-
- /// Queues the primitive formed by the given vertices for rendering
void AddTriangle(const Pica::Shader::OutputVertex& v0,
const Pica::Shader::OutputVertex& v1,
const Pica::Shader::OutputVertex& v2) override;
-
- /// Draw the current batch of triangles
void DrawTriangles() override;
-
- /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
- void CommitFramebuffer() override;
-
- /// Notify rasterizer that the specified PICA register has been changed
+ void FlushFramebuffer() override;
void NotifyPicaRegisterChanged(u32 id) override;
+ void FlushRegion(PAddr addr, u32 size) override;
+ void InvalidateRegion(PAddr addr, u32 size) override;
- /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
- void NotifyPreRead(PAddr addr, u32 size) override;
-
- /// Notify rasterizer that a 3DS memory region has been changed
- void NotifyFlush(PAddr addr, u32 size) override;
+ /// OpenGL shader generated for a given Pica register state
+ struct PicaShader {
+ /// OpenGL shader resource
+ OGLShader shader;
+ };
private:
- /// Structure used for managing texture environment states
- struct TEVConfigUniforms {
- GLuint enabled;
- GLuint color_sources;
- GLuint alpha_sources;
- GLuint color_modifiers;
- GLuint alpha_modifiers;
- GLuint color_alpha_op;
- GLuint color_alpha_multiplier;
- GLuint const_color;
- GLuint updates_combiner_buffer_color_alpha;
- };
/// Structure used for storing information about color textures
struct TextureInfo {
@@ -123,12 +191,27 @@ private:
GLfloat tex_coord2[2];
};
+ /// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned
+ struct UniformData {
+ // A vec4 color for each of the six tev stages
+ std::array<GLfloat, 4> const_color[6];
+ std::array<GLfloat, 4> tev_combiner_buffer_color;
+ GLint alphatest_ref;
+ INSERT_PADDING_BYTES(12);
+ };
+
+ static_assert(sizeof(UniformData) == 0x80, "The size of the UniformData structure has changed, update the structure in the shader");
+ static_assert(sizeof(UniformData) < 16000, "UniformData structure must be less than 16kb as per the OpenGL spec");
+
/// Reconfigure the OpenGL color texture to use the given format and dimensions
void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height);
/// Reconfigure the OpenGL depth texture to use the given format and dimensions
void ReconfigureDepthTexture(DepthTextureInfo& texture, Pica::Regs::DepthFormat format, u32 width, u32 height);
+ /// Sets the OpenGL shader in accordance with the current PICA register state
+ void SetShader();
+
/// Syncs the state and contents of the OpenGL framebuffer to match the current PICA framebuffer
void SyncFramebuffer();
@@ -156,27 +239,12 @@ private:
/// Syncs the depth test states to match the PICA register
void SyncDepthTest();
- /// Syncs the specified TEV stage's color and alpha sources to match the PICA register
- void SyncTevSources(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
-
- /// Syncs the specified TEV stage's color and alpha modifiers to match the PICA register
- void SyncTevModifiers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
-
- /// Syncs the specified TEV stage's color and alpha combiner operations to match the PICA register
- void SyncTevOps(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
-
- /// Syncs the specified TEV stage's constant color to match the PICA register
- void SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
-
- /// Syncs the specified TEV stage's color and alpha multipliers to match the PICA register
- void SyncTevMultipliers(unsigned stage_index, const Pica::Regs::TevStageConfig& config);
+ /// Syncs the TEV constant color to match the PICA register
+ void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage);
/// Syncs the TEV combiner color buffer to match the PICA register
void SyncCombinerColor();
- /// Syncs the TEV combiner write flags to match the PICA register
- void SyncCombinerWriteFlags();
-
/// Syncs the remaining OpenGL drawing state to match the current PICA state
void SyncDrawState();
@@ -213,21 +281,17 @@ private:
std::array<SamplerInfo, 3> texture_samplers;
TextureInfo fb_color_texture;
DepthTextureInfo fb_depth_texture;
- OGLShader shader;
+
+ std::unordered_map<PicaShaderConfig, std::unique_ptr<PicaShader>> shader_cache;
+ const PicaShader* current_shader = nullptr;
+
+ struct {
+ UniformData data;
+ bool dirty;
+ } uniform_block_data;
+
OGLVertexArray vertex_array;
OGLBuffer vertex_buffer;
+ OGLBuffer uniform_buffer;
OGLFramebuffer framebuffer;
-
- // Hardware vertex shader
- GLuint attrib_position;
- GLuint attrib_color;
- GLuint attrib_texcoords;
-
- // Hardware fragment shader
- GLuint uniform_alphatest_enabled;
- GLuint uniform_alphatest_func;
- GLuint uniform_alphatest_ref;
- GLuint uniform_tex;
- GLuint uniform_tev_combiner_buffer_color;
- TEVConfigUniforms uniform_tev_cfgs[6];
};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 10d4ab0b6..a9ad46fe0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -15,7 +15,7 @@
#include "video_core/renderer_opengl/pica_to_gl.h"
RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
- FullFlush();
+ InvalidateAll();
}
MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -58,8 +58,7 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
}
}
-void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash) {
- // Flush any texture that falls in the flushed region
+void RasterizerCacheOpenGL::InvalidateInRange(PAddr addr, u32 size, bool ignore_hash) {
// TODO: Optimize by also inserting upper bound (addr + size) of each texture into the same map and also narrow using lower_bound
auto cache_upper_bound = texture_cache.upper_bound(addr + size);
@@ -77,6 +76,6 @@ void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash)
}
}
-void RasterizerCacheOpenGL::FullFlush() {
+void RasterizerCacheOpenGL::InvalidateAll() {
texture_cache.clear();
}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 98a48ffbe..b69651427 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -23,11 +23,11 @@ public:
LoadAndBindTexture(state, texture_unit, Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format));
}
- /// Flush any cached resource that touches the flushed region
- void NotifyFlush(PAddr addr, u32 size, bool ignore_hash = false);
+ /// Invalidate any cached resource intersecting the specified region.
+ void InvalidateInRange(PAddr addr, u32 size, bool ignore_hash = false);
- /// Flush all cached OpenGL resources tracked by this cache manager
- void FullFlush();
+ /// Invalidate all cached OpenGL resources tracked by this cache manager
+ void InvalidateAll();
private:
struct CachedTexture {
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 65034d40d..eb128966c 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -71,7 +71,7 @@ public:
/// Creates a new internal OpenGL resource and stores the handle
void Create(const char* vert_shader, const char* frag_shader) {
if (handle != 0) return;
- handle = ShaderUtil::LoadShaders(vert_shader, frag_shader);
+ handle = GLShader::LoadProgram(vert_shader, frag_shader);
}
/// Deletes the internal OpenGL resource
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
new file mode 100644
index 000000000..38de5d469
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -0,0 +1,392 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/pica.h"
+#include "video_core/renderer_opengl/gl_rasterizer.h"
+#include "video_core/renderer_opengl/gl_shader_gen.h"
+
+using Pica::Regs;
+using TevStageConfig = Regs::TevStageConfig;
+
+namespace GLShader {
+
+/// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code)
+static bool IsPassThroughTevStage(const TevStageConfig& stage) {
+ return (stage.color_op == TevStageConfig::Operation::Replace &&
+ stage.alpha_op == TevStageConfig::Operation::Replace &&
+ stage.color_source1 == TevStageConfig::Source::Previous &&
+ stage.alpha_source1 == TevStageConfig::Source::Previous &&
+ stage.color_modifier1 == TevStageConfig::ColorModifier::SourceColor &&
+ stage.alpha_modifier1 == TevStageConfig::AlphaModifier::SourceAlpha &&
+ stage.GetColorMultiplier() == 1 &&
+ stage.GetAlphaMultiplier() == 1);
+}
+
+/// Writes the specified TEV stage source component(s)
+static void AppendSource(std::string& out, TevStageConfig::Source source,
+ const std::string& index_name) {
+ using Source = TevStageConfig::Source;
+ switch (source) {
+ case Source::PrimaryColor:
+ out += "primary_color";
+ break;
+ case Source::PrimaryFragmentColor:
+ // HACK: Until we implement fragment lighting, use primary_color
+ out += "primary_color";
+ break;
+ case Source::SecondaryFragmentColor:
+ // HACK: Until we implement fragment lighting, use zero
+ out += "vec4(0.0)";
+ break;
+ case Source::Texture0:
+ out += "texture(tex[0], texcoord[0])";
+ break;
+ case Source::Texture1:
+ out += "texture(tex[1], texcoord[1])";
+ break;
+ case Source::Texture2:
+ out += "texture(tex[2], texcoord[2])";
+ break;
+ case Source::PreviousBuffer:
+ out += "combiner_buffer";
+ break;
+ case Source::Constant:
+ ((out += "const_color[") += index_name) += ']';
+ break;
+ case Source::Previous:
+ out += "last_tex_env_out";
+ break;
+ default:
+ out += "vec4(0.0)";
+ LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source);
+ break;
+ }
+}
+
+/// Writes the color components to use for the specified TEV stage color modifier
+static void AppendColorModifier(std::string& out, TevStageConfig::ColorModifier modifier,
+ TevStageConfig::Source source, const std::string& index_name) {
+ using ColorModifier = TevStageConfig::ColorModifier;
+ switch (modifier) {
+ case ColorModifier::SourceColor:
+ AppendSource(out, source, index_name);
+ out += ".rgb";
+ break;
+ case ColorModifier::OneMinusSourceColor:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".rgb";
+ break;
+ case ColorModifier::SourceAlpha:
+ AppendSource(out, source, index_name);
+ out += ".aaa";
+ break;
+ case ColorModifier::OneMinusSourceAlpha:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".aaa";
+ break;
+ case ColorModifier::SourceRed:
+ AppendSource(out, source, index_name);
+ out += ".rrr";
+ break;
+ case ColorModifier::OneMinusSourceRed:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".rrr";
+ break;
+ case ColorModifier::SourceGreen:
+ AppendSource(out, source, index_name);
+ out += ".ggg";
+ break;
+ case ColorModifier::OneMinusSourceGreen:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".ggg";
+ break;
+ case ColorModifier::SourceBlue:
+ AppendSource(out, source, index_name);
+ out += ".bbb";
+ break;
+ case ColorModifier::OneMinusSourceBlue:
+ out += "vec3(1.0) - ";
+ AppendSource(out, source, index_name);
+ out += ".bbb";
+ break;
+ default:
+ out += "vec3(0.0)";
+ LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier);
+ break;
+ }
+}
+
+/// Writes the alpha component to use for the specified TEV stage alpha modifier
+static void AppendAlphaModifier(std::string& out, TevStageConfig::AlphaModifier modifier,
+ TevStageConfig::Source source, const std::string& index_name) {
+ using AlphaModifier = TevStageConfig::AlphaModifier;
+ switch (modifier) {
+ case AlphaModifier::SourceAlpha:
+ AppendSource(out, source, index_name);
+ out += ".a";
+ break;
+ case AlphaModifier::OneMinusSourceAlpha:
+ out += "1.0 - ";
+ AppendSource(out, source, index_name);
+ out += ".a";
+ break;
+ case AlphaModifier::SourceRed:
+ AppendSource(out, source, index_name);
+ out += ".r";
+ break;
+ case AlphaModifier::OneMinusSourceRed:
+ out += "1.0 - ";
+ AppendSource(out, source, index_name);
+ out += ".r";
+ break;
+ case AlphaModifier::SourceGreen:
+ AppendSource(out, source, index_name);
+ out += ".g";
+ break;
+ case AlphaModifier::OneMinusSourceGreen:
+ out += "1.0 - ";
+ AppendSource(out, source, index_name);
+ out += ".g";
+ break;
+ case AlphaModifier::SourceBlue:
+ AppendSource(out, source, index_name);
+ out += ".b";
+ break;
+ case AlphaModifier::OneMinusSourceBlue:
+ out += "1.0 - ";
+ AppendSource(out, source, index_name);
+ out += ".b";
+ break;
+ default:
+ out += "0.0";
+ LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier);
+ break;
+ }
+}
+
+/// Writes the combiner function for the color components for the specified TEV stage operation
+static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation,
+ const std::string& variable_name) {
+ out += "clamp(";
+ using Operation = TevStageConfig::Operation;
+ switch (operation) {
+ case Operation::Replace:
+ out += variable_name + "[0]";
+ break;
+ case Operation::Modulate:
+ out += variable_name + "[0] * " + variable_name + "[1]";
+ break;
+ case Operation::Add:
+ out += variable_name + "[0] + " + variable_name + "[1]";
+ break;
+ case Operation::AddSigned:
+ out += variable_name + "[0] + " + variable_name + "[1] - vec3(0.5)";
+ break;
+ case Operation::Lerp:
+ // TODO(bunnei): Verify if HW actually does this per-component, otherwise we can just use builtin lerp
+ out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (vec3(1.0) - " + variable_name + "[2])";
+ break;
+ case Operation::Subtract:
+ out += variable_name + "[0] - " + variable_name + "[1]";
+ break;
+ case Operation::MultiplyThenAdd:
+ out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]";
+ break;
+ case Operation::AddThenMultiply:
+ out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + variable_name + "[2]";
+ break;
+ default:
+ out += "vec3(0.0)";
+ LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation);
+ break;
+ }
+ out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0
+}
+
+/// Writes the combiner function for the alpha component for the specified TEV stage operation
+static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation,
+ const std::string& variable_name) {
+ out += "clamp(";
+ using Operation = TevStageConfig::Operation;
+ switch (operation) {
+ case Operation::Replace:
+ out += variable_name + "[0]";
+ break;
+ case Operation::Modulate:
+ out += variable_name + "[0] * " + variable_name + "[1]";
+ break;
+ case Operation::Add:
+ out += variable_name + "[0] + " + variable_name + "[1]";
+ break;
+ case Operation::AddSigned:
+ out += variable_name + "[0] + " + variable_name + "[1] - 0.5";
+ break;
+ case Operation::Lerp:
+ out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + "[1] * (1.0 - " + variable_name + "[2])";
+ break;
+ case Operation::Subtract:
+ out += variable_name + "[0] - " + variable_name + "[1]";
+ break;
+ case Operation::MultiplyThenAdd:
+ out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]";
+ break;
+ case Operation::AddThenMultiply:
+ out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + "[2]";
+ break;
+ default:
+ out += "0.0";
+ LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation);
+ break;
+ }
+ out += ", 0.0, 1.0)";
+}
+
+/// Writes the if-statement condition used to evaluate alpha testing
+static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) {
+ using CompareFunc = Regs::CompareFunc;
+ switch (func) {
+ case CompareFunc::Never:
+ out += "true";
+ break;
+ case CompareFunc::Always:
+ out += "false";
+ break;
+ case CompareFunc::Equal:
+ case CompareFunc::NotEqual:
+ case CompareFunc::LessThan:
+ case CompareFunc::LessThanOrEqual:
+ case CompareFunc::GreaterThan:
+ case CompareFunc::GreaterThanOrEqual:
+ {
+ static const char* op[] = { "!=", "==", ">=", ">", "<=", "<", };
+ unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal;
+ out += "int(last_tex_env_out.a * 255.0f) " + std::string(op[index]) + " alphatest_ref";
+ break;
+ }
+
+ default:
+ out += "false";
+ LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func);
+ break;
+ }
+}
+
+/// Writes the code to emulate the specified TEV stage
+static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
+ auto& stage = config.tev_stages[index];
+ if (!IsPassThroughTevStage(stage)) {
+ std::string index_name = std::to_string(index);
+
+ out += "vec3 color_results_" + index_name + "[3] = vec3[3](";
+ AppendColorModifier(out, stage.color_modifier1, stage.color_source1, index_name);
+ out += ", ";
+ AppendColorModifier(out, stage.color_modifier2, stage.color_source2, index_name);
+ out += ", ";
+ AppendColorModifier(out, stage.color_modifier3, stage.color_source3, index_name);
+ out += ");\n";
+
+ out += "vec3 color_output_" + index_name + " = ";
+ AppendColorCombiner(out, stage.color_op, "color_results_" + index_name);
+ out += ";\n";
+
+ out += "float alpha_results_" + index_name + "[3] = float[3](";
+ AppendAlphaModifier(out, stage.alpha_modifier1, stage.alpha_source1, index_name);
+ out += ", ";
+ AppendAlphaModifier(out, stage.alpha_modifier2, stage.alpha_source2, index_name);
+ out += ", ";
+ AppendAlphaModifier(out, stage.alpha_modifier3, stage.alpha_source3, index_name);
+ out += ");\n";
+
+ out += "float alpha_output_" + index_name + " = ";
+ AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name);
+ out += ";\n";
+
+ out += "last_tex_env_out = vec4("
+ "clamp(color_output_" + index_name + " * " + std::to_string(stage.GetColorMultiplier()) + ".0, vec3(0.0), vec3(1.0)),"
+ "clamp(alpha_output_" + index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + ".0, 0.0, 1.0));\n";
+ }
+
+ out += "combiner_buffer = next_combiner_buffer;\n";
+
+ if (config.TevStageUpdatesCombinerBufferColor(index))
+ out += "next_combiner_buffer.rgb = last_tex_env_out.rgb;\n";
+
+ if (config.TevStageUpdatesCombinerBufferAlpha(index))
+ out += "next_combiner_buffer.a = last_tex_env_out.a;\n";
+}
+
+std::string GenerateFragmentShader(const PicaShaderConfig& config) {
+ std::string out = R"(
+#version 330 core
+#define NUM_TEV_STAGES 6
+
+in vec4 primary_color;
+in vec2 texcoord[3];
+
+out vec4 color;
+
+layout (std140) uniform shader_data {
+ vec4 const_color[NUM_TEV_STAGES];
+ vec4 tev_combiner_buffer_color;
+ int alphatest_ref;
+};
+
+uniform sampler2D tex[3];
+
+void main() {
+)";
+
+ // Do not do any sort of processing if it's obvious we're not going to pass the alpha test
+ if (config.alpha_test_func == Regs::CompareFunc::Never) {
+ out += "discard; }";
+ return out;
+ }
+
+ out += "vec4 combiner_buffer = vec4(0.0);\n";
+ out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n";
+ out += "vec4 last_tex_env_out = vec4(0.0);\n";
+
+ for (size_t index = 0; index < config.tev_stages.size(); ++index)
+ WriteTevStage(out, config, (unsigned)index);
+
+ if (config.alpha_test_func != Regs::CompareFunc::Always) {
+ out += "if (";
+ AppendAlphaTestCondition(out, config.alpha_test_func);
+ out += ") discard;\n";
+ }
+
+ out += "color = last_tex_env_out;\n}";
+
+ return out;
+}
+
+std::string GenerateVertexShader() {
+ std::string out = "#version 330 core\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n";
+
+ out += R"(
+out vec4 primary_color;
+out vec2 texcoord[3];
+
+void main() {
+ primary_color = vert_color;
+ texcoord[0] = vert_texcoord0;
+ texcoord[1] = vert_texcoord1;
+ texcoord[2] = vert_texcoord2;
+ gl_Position = vec4(vert_position.x, vert_position.y, -vert_position.z, vert_position.w);
+}
+)";
+
+ return out;
+}
+
+} // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h
new file mode 100644
index 000000000..0ca9d2879
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_shader_gen.h
@@ -0,0 +1,27 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+#include "video_core/renderer_opengl/gl_rasterizer.h"
+
+namespace GLShader {
+
+/**
+ * Generates the GLSL vertex shader program source code for the current Pica state
+ * @returns String of the shader source code
+ */
+std::string GenerateVertexShader();
+
+/**
+ * Generates the GLSL fragment shader program source code for the current Pica state
+ * @param config ShaderCacheKey object generated for the current Pica state, used for the shader
+ * configuration (NOTE: Use state in this struct only, not the Pica registers!)
+ * @returns String of the shader source code
+ */
+std::string GenerateFragmentShader(const PicaShaderConfig& config);
+
+} // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 4cf246c06..e3f7a5868 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -8,9 +8,9 @@
#include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
-namespace ShaderUtil {
+namespace GLShader {
-GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
+GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader) {
// Create the shaders
GLuint vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
@@ -65,6 +65,7 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
GLuint program_id = glCreateProgram();
glAttachShader(program_id, vertex_shader_id);
glAttachShader(program_id, fragment_shader_id);
+
glLinkProgram(program_id);
// Check the program
@@ -87,4 +88,4 @@ GLuint LoadShaders(const char* vertex_shader, const char* fragment_shader) {
return program_id;
}
-}
+} // namespace GLShader
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index c9d7cc380..046aae14f 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -6,8 +6,22 @@
#include <glad/glad.h>
-namespace ShaderUtil {
+namespace GLShader {
-GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path);
+enum Attributes {
+ ATTRIBUTE_POSITION,
+ ATTRIBUTE_COLOR,
+ ATTRIBUTE_TEXCOORD0,
+ ATTRIBUTE_TEXCOORD1,
+ ATTRIBUTE_TEXCOORD2,
+};
-}
+/**
+ * Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
+ * @param vertex_shader String of the GLSL vertex shader program
+ * @param fragment_shader String of the GLSL fragment shader program
+ * @returns Handle of the newly created OpenGL shader object
+ */
+GLuint LoadProgram(const char* vertex_shader, const char* fragment_shader);
+
+} // namespace
diff --git a/src/video_core/renderer_opengl/gl_shaders.h b/src/video_core/renderer_opengl/gl_shaders.h
deleted file mode 100644
index a8cb2f595..000000000
--- a/src/video_core/renderer_opengl/gl_shaders.h
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-namespace GLShaders {
-
-const char g_vertex_shader[] = R"(
-#version 150 core
-
-in vec2 vert_position;
-in vec2 vert_tex_coord;
-out vec2 frag_tex_coord;
-
-// This is a truncated 3x3 matrix for 2D transformations:
-// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
-// The third column performs translation.
-// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
-// implicitly be [0, 0, 1]
-uniform mat3x2 modelview_matrix;
-
-void main() {
- // Multiply input position by the rotscale part of the matrix and then manually translate by
- // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
- // to `vec3(vert_position.xy, 1.0)`
- gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
- frag_tex_coord = vert_tex_coord;
-}
-)";
-
-const char g_fragment_shader[] = R"(
-#version 150 core
-
-in vec2 frag_tex_coord;
-out vec4 color;
-
-uniform sampler2D color_texture;
-
-void main() {
- color = texture(color_texture, frag_tex_coord);
-}
-)";
-
-const char g_vertex_shader_hw[] = R"(
-#version 150 core
-
-#define NUM_VTX_ATTR 7
-
-in vec4 vert_position;
-in vec4 vert_color;
-in vec2 vert_texcoords[3];
-
-out vec4 o[NUM_VTX_ATTR];
-
-void main() {
- o[2] = vert_color;
- o[3] = vec4(vert_texcoords[0].xy, vert_texcoords[1].xy);
- o[5] = vec4(0.0, 0.0, vert_texcoords[2].xy);
-
- gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
-}
-)";
-
-// TODO: Create a shader constructor and cache that builds this program with minimal conditionals instead of using tev_cfg uniforms
-const char g_fragment_shader_hw[] = R"(
-#version 150 core
-
-#define NUM_VTX_ATTR 7
-#define NUM_TEV_STAGES 6
-
-#define SOURCE_PRIMARYCOLOR 0x0
-#define SOURCE_PRIMARYFRAGMENTCOLOR 0x1
-#define SOURCE_SECONDARYFRAGMENTCOLOR 0x2
-#define SOURCE_TEXTURE0 0x3
-#define SOURCE_TEXTURE1 0x4
-#define SOURCE_TEXTURE2 0x5
-#define SOURCE_TEXTURE3 0x6
-#define SOURCE_PREVIOUSBUFFER 0xd
-#define SOURCE_CONSTANT 0xe
-#define SOURCE_PREVIOUS 0xf
-
-#define COLORMODIFIER_SOURCECOLOR 0x0
-#define COLORMODIFIER_ONEMINUSSOURCECOLOR 0x1
-#define COLORMODIFIER_SOURCEALPHA 0x2
-#define COLORMODIFIER_ONEMINUSSOURCEALPHA 0x3
-#define COLORMODIFIER_SOURCERED 0x4
-#define COLORMODIFIER_ONEMINUSSOURCERED 0x5
-#define COLORMODIFIER_SOURCEGREEN 0x8
-#define COLORMODIFIER_ONEMINUSSOURCEGREEN 0x9
-#define COLORMODIFIER_SOURCEBLUE 0xc
-#define COLORMODIFIER_ONEMINUSSOURCEBLUE 0xd
-
-#define ALPHAMODIFIER_SOURCEALPHA 0x0
-#define ALPHAMODIFIER_ONEMINUSSOURCEALPHA 0x1
-#define ALPHAMODIFIER_SOURCERED 0x2
-#define ALPHAMODIFIER_ONEMINUSSOURCERED 0x3
-#define ALPHAMODIFIER_SOURCEGREEN 0x4
-#define ALPHAMODIFIER_ONEMINUSSOURCEGREEN 0x5
-#define ALPHAMODIFIER_SOURCEBLUE 0x6
-#define ALPHAMODIFIER_ONEMINUSSOURCEBLUE 0x7
-
-#define OPERATION_REPLACE 0
-#define OPERATION_MODULATE 1
-#define OPERATION_ADD 2
-#define OPERATION_ADDSIGNED 3
-#define OPERATION_LERP 4
-#define OPERATION_SUBTRACT 5
-#define OPERATION_MULTIPLYTHENADD 8
-#define OPERATION_ADDTHENMULTIPLY 9
-
-#define COMPAREFUNC_NEVER 0
-#define COMPAREFUNC_ALWAYS 1
-#define COMPAREFUNC_EQUAL 2
-#define COMPAREFUNC_NOTEQUAL 3
-#define COMPAREFUNC_LESSTHAN 4
-#define COMPAREFUNC_LESSTHANOREQUAL 5
-#define COMPAREFUNC_GREATERTHAN 6
-#define COMPAREFUNC_GREATERTHANOREQUAL 7
-
-in vec4 o[NUM_VTX_ATTR];
-out vec4 color;
-
-uniform bool alphatest_enabled;
-uniform int alphatest_func;
-uniform float alphatest_ref;
-
-uniform sampler2D tex[3];
-
-uniform vec4 tev_combiner_buffer_color;
-
-struct TEVConfig
-{
- bool enabled;
- ivec3 color_sources;
- ivec3 alpha_sources;
- ivec3 color_modifiers;
- ivec3 alpha_modifiers;
- ivec2 color_alpha_op;
- ivec2 color_alpha_multiplier;
- vec4 const_color;
- bvec2 updates_combiner_buffer_color_alpha;
-};
-
-uniform TEVConfig tev_cfgs[NUM_TEV_STAGES];
-
-vec4 g_combiner_buffer;
-vec4 g_last_tex_env_out;
-vec4 g_const_color;
-
-vec4 GetSource(int source) {
- if (source == SOURCE_PRIMARYCOLOR) {
- return o[2];
- } else if (source == SOURCE_PRIMARYFRAGMENTCOLOR) {
- // HACK: Until we implement fragment lighting, use primary_color
- return o[2];
- } else if (source == SOURCE_SECONDARYFRAGMENTCOLOR) {
- // HACK: Until we implement fragment lighting, use zero
- return vec4(0.0, 0.0, 0.0, 0.0);
- } else if (source == SOURCE_TEXTURE0) {
- return texture(tex[0], o[3].xy);
- } else if (source == SOURCE_TEXTURE1) {
- return texture(tex[1], o[3].zw);
- } else if (source == SOURCE_TEXTURE2) {
- // TODO: Unverified
- return texture(tex[2], o[5].zw);
- } else if (source == SOURCE_TEXTURE3) {
- // TODO: no 4th texture?
- } else if (source == SOURCE_PREVIOUSBUFFER) {
- return g_combiner_buffer;
- } else if (source == SOURCE_CONSTANT) {
- return g_const_color;
- } else if (source == SOURCE_PREVIOUS) {
- return g_last_tex_env_out;
- }
-
- return vec4(0.0);
-}
-
-vec3 GetColorModifier(int factor, vec4 color) {
- if (factor == COLORMODIFIER_SOURCECOLOR) {
- return color.rgb;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCECOLOR) {
- return vec3(1.0) - color.rgb;
- } else if (factor == COLORMODIFIER_SOURCEALPHA) {
- return color.aaa;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCEALPHA) {
- return vec3(1.0) - color.aaa;
- } else if (factor == COLORMODIFIER_SOURCERED) {
- return color.rrr;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCERED) {
- return vec3(1.0) - color.rrr;
- } else if (factor == COLORMODIFIER_SOURCEGREEN) {
- return color.ggg;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCEGREEN) {
- return vec3(1.0) - color.ggg;
- } else if (factor == COLORMODIFIER_SOURCEBLUE) {
- return color.bbb;
- } else if (factor == COLORMODIFIER_ONEMINUSSOURCEBLUE) {
- return vec3(1.0) - color.bbb;
- }
-
- return vec3(0.0);
-}
-
-float GetAlphaModifier(int factor, vec4 color) {
- if (factor == ALPHAMODIFIER_SOURCEALPHA) {
- return color.a;
- } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEALPHA) {
- return 1.0 - color.a;
- } else if (factor == ALPHAMODIFIER_SOURCERED) {
- return color.r;
- } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCERED) {
- return 1.0 - color.r;
- } else if (factor == ALPHAMODIFIER_SOURCEGREEN) {
- return color.g;
- } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEGREEN) {
- return 1.0 - color.g;
- } else if (factor == ALPHAMODIFIER_SOURCEBLUE) {
- return color.b;
- } else if (factor == ALPHAMODIFIER_ONEMINUSSOURCEBLUE) {
- return 1.0 - color.b;
- }
-
- return 0.0;
-}
-
-vec3 ColorCombine(int op, vec3 color[3]) {
- if (op == OPERATION_REPLACE) {
- return color[0];
- } else if (op == OPERATION_MODULATE) {
- return color[0] * color[1];
- } else if (op == OPERATION_ADD) {
- return min(color[0] + color[1], 1.0);
- } else if (op == OPERATION_ADDSIGNED) {
- return clamp(color[0] + color[1] - vec3(0.5), 0.0, 1.0);
- } else if (op == OPERATION_LERP) {
- return color[0] * color[2] + color[1] * (vec3(1.0) - color[2]);
- } else if (op == OPERATION_SUBTRACT) {
- return max(color[0] - color[1], 0.0);
- } else if (op == OPERATION_MULTIPLYTHENADD) {
- return min(color[0] * color[1] + color[2], 1.0);
- } else if (op == OPERATION_ADDTHENMULTIPLY) {
- return min(color[0] + color[1], 1.0) * color[2];
- }
-
- return vec3(0.0);
-}
-
-float AlphaCombine(int op, float alpha[3]) {
- if (op == OPERATION_REPLACE) {
- return alpha[0];
- } else if (op == OPERATION_MODULATE) {
- return alpha[0] * alpha[1];
- } else if (op == OPERATION_ADD) {
- return min(alpha[0] + alpha[1], 1.0);
- } else if (op == OPERATION_ADDSIGNED) {
- return clamp(alpha[0] + alpha[1] - 0.5, 0.0, 1.0);
- } else if (op == OPERATION_LERP) {
- return alpha[0] * alpha[2] + alpha[1] * (1.0 - alpha[2]);
- } else if (op == OPERATION_SUBTRACT) {
- return max(alpha[0] - alpha[1], 0.0);
- } else if (op == OPERATION_MULTIPLYTHENADD) {
- return min(alpha[0] * alpha[1] + alpha[2], 1.0);
- } else if (op == OPERATION_ADDTHENMULTIPLY) {
- return min(alpha[0] + alpha[1], 1.0) * alpha[2];
- }
-
- return 0.0;
-}
-
-void main(void) {
- g_combiner_buffer = tev_combiner_buffer_color;
-
- for (int tex_env_idx = 0; tex_env_idx < NUM_TEV_STAGES; ++tex_env_idx) {
- if (tev_cfgs[tex_env_idx].enabled) {
- g_const_color = tev_cfgs[tex_env_idx].const_color;
-
- vec3 color_results[3] = vec3[3](GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.x, GetSource(tev_cfgs[tex_env_idx].color_sources.x)),
- GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.y, GetSource(tev_cfgs[tex_env_idx].color_sources.y)),
- GetColorModifier(tev_cfgs[tex_env_idx].color_modifiers.z, GetSource(tev_cfgs[tex_env_idx].color_sources.z)));
- vec3 color_output = ColorCombine(tev_cfgs[tex_env_idx].color_alpha_op.x, color_results);
-
- float alpha_results[3] = float[3](GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.x, GetSource(tev_cfgs[tex_env_idx].alpha_sources.x)),
- GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.y, GetSource(tev_cfgs[tex_env_idx].alpha_sources.y)),
- GetAlphaModifier(tev_cfgs[tex_env_idx].alpha_modifiers.z, GetSource(tev_cfgs[tex_env_idx].alpha_sources.z)));
- float alpha_output = AlphaCombine(tev_cfgs[tex_env_idx].color_alpha_op.y, alpha_results);
-
- g_last_tex_env_out = vec4(min(color_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.x, 1.0), min(alpha_output * tev_cfgs[tex_env_idx].color_alpha_multiplier.y, 1.0));
- }
-
- if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.x) {
- g_combiner_buffer.rgb = g_last_tex_env_out.rgb;
- }
-
- if (tev_cfgs[tex_env_idx].updates_combiner_buffer_color_alpha.y) {
- g_combiner_buffer.a = g_last_tex_env_out.a;
- }
- }
-
- if (alphatest_enabled) {
- if (alphatest_func == COMPAREFUNC_NEVER) {
- discard;
- } else if (alphatest_func == COMPAREFUNC_ALWAYS) {
-
- } else if (alphatest_func == COMPAREFUNC_EQUAL) {
- if (g_last_tex_env_out.a != alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_NOTEQUAL) {
- if (g_last_tex_env_out.a == alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_LESSTHAN) {
- if (g_last_tex_env_out.a >= alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_LESSTHANOREQUAL) {
- if (g_last_tex_env_out.a > alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_GREATERTHAN) {
- if (g_last_tex_env_out.a <= alphatest_ref) {
- discard;
- }
- } else if (alphatest_func == COMPAREFUNC_GREATERTHANOREQUAL) {
- if (g_last_tex_env_out.a < alphatest_ref) {
- discard;
- }
- }
- }
-
- color = g_last_tex_env_out;
-}
-)";
-
-}
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 77b2816cb..a82372995 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -11,6 +11,7 @@ OpenGLState::OpenGLState() {
// These all match default OpenGL values
cull.enabled = false;
cull.mode = GL_BACK;
+ cull.front_face = GL_CCW;
depth.test_enabled = false;
depth.test_func = GL_LESS;
@@ -67,6 +68,10 @@ void OpenGLState::Apply() {
glCullFace(cull.mode);
}
+ if (cull.front_face != cur_state.cull.front_face) {
+ glFrontFace(cull.front_face);
+ }
+
// Depth test
if (depth.test_enabled != cur_state.depth.test_enabled) {
if (depth.test_enabled) {
@@ -180,6 +185,11 @@ void OpenGLState::Apply() {
glBindBuffer(GL_ARRAY_BUFFER, draw.vertex_buffer);
}
+ // Uniform buffer
+ if (draw.uniform_buffer != cur_state.draw.uniform_buffer) {
+ glBindBuffer(GL_UNIFORM_BUFFER, draw.uniform_buffer);
+ }
+
// Shader program
if (draw.shader_program != cur_state.draw.shader_program) {
glUseProgram(draw.shader_program);
@@ -214,6 +224,9 @@ void OpenGLState::ResetBuffer(GLuint id) {
if (cur_state.draw.vertex_buffer == id) {
cur_state.draw.vertex_buffer = 0;
}
+ if (cur_state.draw.uniform_buffer == id) {
+ cur_state.draw.uniform_buffer = 0;
+ }
}
void OpenGLState::ResetVertexArray(GLuint id) {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 6ecbedbb4..b8ab45bb8 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -11,6 +11,7 @@ public:
struct {
bool enabled; // GL_CULL_FACE
GLenum mode; // GL_CULL_FACE_MODE
+ GLenum front_face; // GL_FRONT_FACE
} cull;
struct {
@@ -64,7 +65,9 @@ public:
GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
+ GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING
GLuint shader_program; // GL_CURRENT_PROGRAM
+ bool shader_dirty;
} draw;
OpenGLState();
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 4202d828c..c34215340 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,9 +21,44 @@
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
-#include "video_core/renderer_opengl/gl_shaders.h"
#include "video_core/renderer_opengl/renderer_opengl.h"
+static const char vertex_shader[] = R"(
+#version 150 core
+
+in vec2 vert_position;
+in vec2 vert_tex_coord;
+out vec2 frag_tex_coord;
+
+// This is a truncated 3x3 matrix for 2D transformations:
+// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
+// The third column performs translation.
+// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
+// implicitly be [0, 0, 1]
+uniform mat3x2 modelview_matrix;
+
+void main() {
+ // Multiply input position by the rotscale part of the matrix and then manually translate by
+ // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
+ // to `vec3(vert_position.xy, 1.0)`
+ gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
+ frag_tex_coord = vert_tex_coord;
+}
+)";
+
+static const char fragment_shader[] = R"(
+#version 150 core
+
+in vec2 frag_tex_coord;
+out vec4 color;
+
+uniform sampler2D color_texture;
+
+void main() {
+ color = texture(color_texture, frag_tex_coord);
+}
+)";
+
/**
* Vertex structure that the drawn screen rectangles are composed of.
*/
@@ -58,7 +93,6 @@ static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const
/// RendererOpenGL constructor
RendererOpenGL::RendererOpenGL() {
- hw_rasterizer.reset(new RasterizerOpenGL());
resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
}
@@ -122,15 +156,7 @@ void RendererOpenGL::SwapBuffers() {
profiler.BeginFrame();
- bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled;
- if (Settings::values.use_hw_renderer != hw_renderer_enabled) {
- // TODO: Save new setting value to config file for next startup
- Settings::values.use_hw_renderer = hw_renderer_enabled;
-
- if (Settings::values.use_hw_renderer) {
- hw_rasterizer->Reset();
- }
- }
+ RefreshRasterizerSetting();
if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
Pica::g_debug_context->recorder->FrameFinished();
@@ -207,7 +233,7 @@ void RendererOpenGL::InitOpenGLObjects() {
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, 0.0f);
// Link shaders and get variable locations
- program_id = ShaderUtil::LoadShaders(GLShaders::g_vertex_shader, GLShaders::g_fragment_shader);
+ program_id = GLShader::LoadProgram(vertex_shader, fragment_shader);
uniform_modelview_matrix = glGetUniformLocation(program_id, "modelview_matrix");
uniform_color_texture = glGetUniformLocation(program_id, "color_texture");
attrib_position = glGetAttribLocation(program_id, "vert_position");
@@ -221,6 +247,7 @@ void RendererOpenGL::InitOpenGLObjects() {
state.draw.vertex_array = vertex_array_handle;
state.draw.vertex_buffer = vertex_buffer_handle;
+ state.draw.uniform_buffer = 0;
state.Apply();
// Attach vertex data to VAO
@@ -250,8 +277,6 @@ void RendererOpenGL::InitOpenGLObjects() {
state.texture_units[0].texture_2d = 0;
state.Apply();
-
- hw_rasterizer->InitObjects();
}
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@@ -440,6 +465,8 @@ void RendererOpenGL::Init() {
LOG_INFO(Render_OpenGL, "GL_VENDOR: %s", glGetString(GL_VENDOR));
LOG_INFO(Render_OpenGL, "GL_RENDERER: %s", glGetString(GL_RENDERER));
InitOpenGLObjects();
+
+ RefreshRasterizerSetting();
}
/// Shutdown the renderer
diff --git a/src/video_core/swrasterizer.cpp b/src/video_core/swrasterizer.cpp
new file mode 100644
index 000000000..03df15b01
--- /dev/null
+++ b/src/video_core/swrasterizer.cpp
@@ -0,0 +1,16 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/clipper.h"
+#include "video_core/swrasterizer.h"
+
+namespace VideoCore {
+
+void SWRasterizer::AddTriangle(const Pica::Shader::OutputVertex& v0,
+ const Pica::Shader::OutputVertex& v1,
+ const Pica::Shader::OutputVertex& v2) {
+ Pica::Clipper::ProcessTriangle(v0, v1, v2);
+}
+
+}
diff --git a/src/video_core/swrasterizer.h b/src/video_core/swrasterizer.h
new file mode 100644
index 000000000..9a9a76d7a
--- /dev/null
+++ b/src/video_core/swrasterizer.h
@@ -0,0 +1,26 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "video_core/rasterizer_interface.h"
+
+namespace VideoCore {
+
+class SWRasterizer : public RasterizerInterface {
+ void InitObjects() override {}
+ void Reset() override {}
+ void AddTriangle(const Pica::Shader::OutputVertex& v0,
+ const Pica::Shader::OutputVertex& v1,
+ const Pica::Shader::OutputVertex& v2) override;
+ void DrawTriangles() override {}
+ void FlushFramebuffer() override {}
+ void NotifyPicaRegisterChanged(u32 id) override {}
+ void FlushRegion(PAddr addr, u32 size) override {}
+ void InvalidateRegion(PAddr addr, u32 size) override {}
+};
+
+}
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index eaddda668..912db91a4 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -2,7 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <memory>
+
#include "common/emu_window.h"
+#include "common/make_unique.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -18,8 +21,8 @@
namespace VideoCore {
-EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
-RendererBase* g_renderer = nullptr; ///< Renderer plugin
+EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
+std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
std::atomic<bool> g_hw_renderer_enabled;
std::atomic<bool> g_shader_jit_enabled;
@@ -29,7 +32,7 @@ void Init(EmuWindow* emu_window) {
Pica::Init();
g_emu_window = emu_window;
- g_renderer = new RendererOpenGL();
+ g_renderer = Common::make_unique<RendererOpenGL>();
g_renderer->SetWindow(g_emu_window);
g_renderer->Init();
@@ -40,7 +43,7 @@ void Init(EmuWindow* emu_window) {
void Shutdown() {
Pica::Shutdown();
- delete g_renderer;
+ g_renderer.reset();
LOG_DEBUG(Render, "shutdown OK");
}
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 2867bf03e..accb0a4eb 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -5,6 +5,7 @@
#pragma once
#include <atomic>
+#include <memory>
class EmuWindow;
class RendererBase;
@@ -29,8 +30,8 @@ static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height
// Video core renderer
// ---------------------
-extern RendererBase* g_renderer; ///< Renderer plugin
-extern EmuWindow* g_emu_window; ///< Emu window
+extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
+extern EmuWindow* g_emu_window; ///< Emu window
// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from qt ui)
extern std::atomic<bool> g_hw_renderer_enabled;