aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.travis-deps.sh2
-rw-r--r--.travis.yml5
-rw-r--r--CMakeLists.txt29
-rw-r--r--README.md1
-rw-r--r--appveyor.yml17
m---------externals/boost0
m---------externals/nihstro0
-rw-r--r--src/citra/CMakeLists.txt16
-rw-r--r--src/citra/citra.rcbin566 -> 282 bytes
-rw-r--r--src/citra_qt/CMakeLists.txt13
-rw-r--r--src/citra_qt/bootmanager.cpp19
-rw-r--r--src/citra_qt/bootmanager.h12
-rw-r--r--src/citra_qt/debugger/callstack.cpp18
-rw-r--r--src/citra_qt/debugger/callstack.h6
-rw-r--r--src/citra_qt/debugger/disassembler.cpp8
-rw-r--r--src/citra_qt/debugger/disassembler.h3
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp2
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp8
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.h4
-rw-r--r--src/citra_qt/debugger/registers.cpp7
-rw-r--r--src/citra_qt/debugger/registers.h3
-rw-r--r--src/citra_qt/main.cpp14
-rw-r--r--src/common/common.h7
-rw-r--r--src/common/log.h2
-rw-r--r--src/common/logging/log.h23
-rw-r--r--src/core/CMakeLists.txt8
-rw-r--r--src/core/arm/arm_interface.h8
-rw-r--r--src/core/arm/dyncom/arm_dyncom.cpp11
-rw-r--r--src/core/arm/dyncom/arm_dyncom.h7
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp465
-rw-r--r--src/core/arm/interpreter/arm_interpreter.cpp6
-rw-r--r--src/core/arm/interpreter/arm_interpreter.h4
-rw-r--r--src/core/arm/interpreter/armsupp.cpp16
-rw-r--r--src/core/arm/skyeye_common/armdefs.h1
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpsingle.cpp64
-rw-r--r--src/core/core.cpp9
-rw-r--r--src/core/core.h20
-rw-r--r--src/core/core_timing.cpp2
-rw-r--r--src/core/hle/function_wrappers.h8
-rw-r--r--src/core/hle/hle.cpp1
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp33
-rw-r--r--src/core/hle/kernel/address_arbiter.h2
-rw-r--r--src/core/hle/kernel/event.cpp16
-rw-r--r--src/core/hle/kernel/kernel.cpp32
-rw-r--r--src/core/hle/kernel/kernel.h26
-rw-r--r--src/core/hle/kernel/mutex.cpp23
-rw-r--r--src/core/hle/kernel/semaphore.cpp10
-rw-r--r--src/core/hle/kernel/shared_memory.cpp4
-rw-r--r--src/core/hle/kernel/thread.cpp438
-rw-r--r--src/core/hle/kernel/thread.h115
-rw-r--r--src/core/hle/kernel/timer.cpp144
-rw-r--r--src/core/hle/kernel/timer.h47
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/apt_s.cpp121
-rw-r--r--src/core/hle/service/apt_s.h30
-rw-r--r--src/core/hle/service/apt_u.cpp32
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp98
-rw-r--r--src/core/hle/service/cfg/cfg_s.h23
-rw-r--r--src/core/hle/service/dsp_dsp.cpp7
-rw-r--r--src/core/hle/service/fs/fs_user.cpp10
-rw-r--r--src/core/hle/service/gsp_gpu.cpp38
-rw-r--r--src/core/hle/service/gsp_gpu.h25
-rw-r--r--src/core/hle/service/hid_user.cpp3
-rw-r--r--src/core/hle/service/ptm_sysm.cpp56
-rw-r--r--src/core/hle/service/ptm_sysm.h23
-rw-r--r--src/core/hle/service/ptm_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp9
-rw-r--r--src/core/hle/service/service.h31
-rw-r--r--src/core/hle/service/soc_u.cpp13
-rw-r--r--src/core/hle/service/srv.cpp4
-rw-r--r--src/core/hle/service/y2r_u.cpp17
-rw-r--r--src/core/hle/svc.cpp122
-rw-r--r--src/core/hle/svc.h15
-rw-r--r--src/core/hw/gpu.cpp131
-rw-r--r--src/core/hw/gpu.h3
-rw-r--r--src/core/hw/hw.cpp1
-rw-r--r--src/core/system.cpp4
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp152
-rw-r--r--src/video_core/pica.h4
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp4
-rw-r--r--src/video_core/vertex_shader.cpp144
82 files changed, 1878 insertions, 989 deletions
diff --git a/.travis-deps.sh b/.travis-deps.sh
index 2a0f6b28..bd09da0d 100644
--- a/.travis-deps.sh
+++ b/.travis-deps.sh
@@ -24,8 +24,6 @@ if [ "$TRAVIS_OS_NAME" = linux -o -z "$TRAVIS_OS_NAME" ]; then
curl http://www.cmake.org/files/v2.8/cmake-2.8.11-Linux-i386.tar.gz \
| sudo tar -xz -C /usr/local --strip-components=1
elif [ "$TRAVIS_OS_NAME" = osx ]; then
- export HOMEBREW_CACHE="$PWD/.homebrew-cache"
- mkdir -p "$HOMEBREW_CACHE"
brew tap homebrew/versions
brew install qt5 glfw3 pkgconfig
fi
diff --git a/.travis.yml b/.travis.yml
index 8c5aceb7..1cb369d5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,11 +4,6 @@ os:
language: cpp
-cache:
- apt: true
- directories:
- - .homebrew-cache
-
before_install:
- sh .travis-deps.sh
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 884520ce..d70c872b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,8 +5,8 @@ cmake_minimum_required(VERSION 2.8.11)
project(citra)
if (NOT MSVC)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes")
- add_definitions(-pthread)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-attributes -pthread")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
else()
# Silence deprecation warnings
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
@@ -17,14 +17,14 @@ else()
# As far as I can tell, there's no way to override the CMake defaults while leaving user
# changes intact, so we'll just clobber everything and say sorry.
message(STATUS "Cache compiler flags ignored, please edit CMakeFiles.txt to change the flags.")
+ # /MP - Multi-threaded compilation
# /MD - Multi-threaded runtime
# /Ox - Full optimization
- # /Oi - Use intrinsic functions
# /Oy- - Don't omit frame pointer
# /GR- - Disable RTTI
# /GS- - No stack buffer overflow checks
# /EHsc - C++-only exception handling semantics
- set(optimization_flags "/MD /Ox /Oi /Oy- /DNDEBUG /GR- /GS- /EHsc")
+ set(optimization_flags "/MP /MD /Ox /Oy- /GR- /GS- /EHsc")
# /Zi - Output debugging information
# /Zo - enahnced debug info for optimized builds
set(CMAKE_C_FLAGS_RELEASE "${optimization_flags} /Zi" CACHE STRING "" FORCE)
@@ -32,7 +32,11 @@ else()
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${optimization_flags} /Zi /Zo" CACHE STRING "" FORCE)
endif()
+
add_definitions(-DSINGLETHREADED)
+# CMake seems to only define _DEBUG on Windows
+set_property(DIRECTORY APPEND PROPERTY
+ COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
find_package(PNG QUIET)
if (PNG_FOUND)
@@ -106,10 +110,23 @@ if (ENABLE_GLFW)
endif()
IF (APPLE)
- # CoreFoundation is required only on OSX
- FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation)
+ FIND_LIBRARY(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
+ FIND_LIBRARY(IOKIT_LIBRARY IOKit) # GLFW dependency
+ FIND_LIBRARY(COREVIDEO_LIBRARY CoreVideo) # GLFW dependency
+ set(PLATFORM_LIBRARIES iconv ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++")
+ELSEIF(MINGW)
+ # GCC does not support codecvt, so use iconv instead
+ set(PLATFORM_LIBRARIES winmm ws2_32 iconv)
+
+ # WSAPoll functionality doesn't exist before WinNT 6.x (Vista and up)
+ add_definitions(-D_WIN32_WINNT=0x0600)
+ELSEIF(WIN32)
+ set(PLATFORM_LIBRARIES winmm ws2_32)
+ELSE()
+ set(PLATFORM_LIBRARIES rt)
ENDIF (APPLE)
option(ENABLE_QT "Enable the Qt frontend" ON)
diff --git a/README.md b/README.md
index b4383606..7715dfe4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
Citra Emulator
==============
[![Travis CI Build Status](https://travis-ci.org/citra-emu/citra.svg)](https://travis-ci.org/citra-emu/citra)
+[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/sdf1o4kh3g1e68m9?svg=true)](https://ci.appveyor.com/project/bunnei/citra)
Citra is an experimental open-source Nintendo 3DS emulator/debugger written in C++. It is written with portability in mind, with builds actively maintained for Windows, Linux and OS X. Citra only emulates a subset of 3DS hardware, and therefore is generally only useful for running/debugging homebrew applications. At this time, Citra is even able to boot several commercial games! None of these run to a playable state, but we are working every day to advance the project forward.
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 00000000..83d3b900
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,17 @@
+# it seems that Qt is only installed by default on unstable os at the moment
+os: unstable
+
+# shallow clone
+clone_depth: 1
+
+environment:
+ QTDIR: C:\Qt\5.4\msvc2013_opengl
+
+install:
+ - git submodule update --init --recursive
+
+before_build:
+ - mkdir build
+ - cd build
+ - cmake ..
+ - cd ..
diff --git a/externals/boost b/externals/boost
-Subproject 97052c28acb141dbf3c5e14114af99045344b69
+Subproject a1afc91d3aaa3da06bdbc13c78613e146665340
diff --git a/externals/nihstro b/externals/nihstro
-Subproject fc71f8684d26ccf277ad68809c8bd7273141fe8
+Subproject 0a8b4d221425f13e24a3cef9b02edc3221bab21
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index bbb3374f..713f4919 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -16,20 +16,6 @@ create_directory_groups(${SRCS} ${HEADERS})
add_executable(citra ${SRCS} ${HEADERS})
target_link_libraries(citra core common video_core)
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih)
-
-if (UNIX)
- target_link_libraries(citra -pthread)
-endif()
-
-if (APPLE)
- target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY})
-elseif (WIN32)
- target_link_libraries(citra winmm wsock32 ws2_32)
- if (MINGW) # GCC does not support codecvt, so use iconv instead
- target_link_libraries(citra iconv)
- endif()
-else() # Unix
- target_link_libraries(citra rt)
-endif()
+target_link_libraries(citra ${PLATFORM_LIBRARIES})
#install(TARGETS citra RUNTIME DESTINATION ${bindir})
diff --git a/src/citra/citra.rc b/src/citra/citra.rc
index c28e7dbe..0165e93d 100644
--- a/src/citra/citra.rc
+++ b/src/citra/citra.rc
Binary files differ
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index a0ba252b..bbc521f8 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -60,17 +60,6 @@ endif()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
target_link_libraries(citra-qt core common video_core qhexedit)
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
-
-if (UNIX)
- target_link_libraries(citra-qt -pthread)
-endif()
-
-if (APPLE)
- target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY})
-elseif (WIN32)
- target_link_libraries(citra-qt winmm wsock32 ws2_32)
-else() # Unix
- target_link_libraries(citra-qt rt)
-endif()
+target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
#install(TARGETS citra-qt RUNTIME DESTINATION ${bindir})
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index 3e24da59..19638010 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -40,18 +40,35 @@ void EmuThread::SetFilename(std::string filename)
void EmuThread::run()
{
stop_run = false;
+
+ // holds whether the cpu was running during the last iteration,
+ // so that the DebugModeLeft signal can be emitted before the
+ // next execution step
+ bool was_active = false;
while (!stop_run)
{
if (cpu_running)
{
+ if (!was_active)
+ emit DebugModeLeft();
+
Core::RunLoop();
+
+ was_active = cpu_running || exec_cpu_step;
+ if (!was_active)
+ emit DebugModeEntered();
}
else if (exec_cpu_step)
{
+ if (!was_active)
+ emit DebugModeLeft();
+
exec_cpu_step = false;
Core::SingleStep();
- emit CPUStepped();
+ emit DebugModeEntered();
yieldCurrentThread();
+
+ was_active = false;
}
}
render_window->moveContext();
diff --git a/src/citra_qt/bootmanager.h b/src/citra_qt/bootmanager.h
index 1c893384..a55db682 100644
--- a/src/citra_qt/bootmanager.h
+++ b/src/citra_qt/bootmanager.h
@@ -81,12 +81,18 @@ private:
signals:
/**
- * Emitted when CPU when we've finished processing a single Gekko instruction
+ * Emitted when the CPU has halted execution
*
- * @warning This will only be emitted when the CPU is not running (SetCpuRunning(false))
* @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
*/
- void CPUStepped();
+ void DebugModeEntered();
+
+ /**
+ * Emitted right before the CPU continues execution
+ *
+ * @warning When connecting to this signal from other threads, make sure to specify either Qt::QueuedConnection (invoke slot within the destination object's message thread) or even Qt::BlockingQueuedConnection (additionally block source thread until slot returns)
+ */
+ void DebugModeLeft();
};
class GRenderWindow : public QWidget, public EmuWindow
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index 274c5ccc..9bb22ca2 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -25,7 +25,7 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent)
ui.treeView->setModel(callstack_model);
}
-void CallstackWidget::OnCPUStepped()
+void CallstackWidget::OnDebugModeEntered()
{
ARM_Disasm* disasm = new ARM_Disasm();
ARM_Interface* app_core = Core::g_app_core;
@@ -33,6 +33,8 @@ void CallstackWidget::OnCPUStepped()
u32 sp = app_core->GetReg(13); //stack pointer
u32 ret_addr, call_addr, func_addr;
+ Clear();
+
int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
{
@@ -71,3 +73,17 @@ void CallstackWidget::OnCPUStepped()
}
}
}
+
+void CallstackWidget::OnDebugModeLeft()
+{
+
+}
+
+void CallstackWidget::Clear()
+{
+ for (int row = 0; row < callstack_model->rowCount(); row++) {
+ for (int column = 0; column < callstack_model->columnCount(); column++) {
+ callstack_model->setItem(row, column, new QStandardItem());
+ }
+ }
+}
diff --git a/src/citra_qt/debugger/callstack.h b/src/citra_qt/debugger/callstack.h
index 4f4f7482..1a9b6dc8 100644
--- a/src/citra_qt/debugger/callstack.h
+++ b/src/citra_qt/debugger/callstack.h
@@ -15,9 +15,13 @@ public:
CallstackWidget(QWidget* parent = 0);
public slots:
- void OnCPUStepped();
+ void OnDebugModeEntered();
+ void OnDebugModeLeft();
private:
Ui::CallStack ui;
QStandardItemModel* callstack_model;
+
+ /// Clears the callstack widget while keeping the column widths the same
+ void Clear();
};
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp
index 8db73752..c61ace92 100644
--- a/src/citra_qt/debugger/disassembler.cpp
+++ b/src/citra_qt/debugger/disassembler.cpp
@@ -13,6 +13,7 @@
#include "core/core.h"
#include "common/break_points.h"
#include "common/symbols.h"
+#include "core/arm/arm_interface.h"
#include "core/arm/skyeye_common/armdefs.h"
#include "core/arm/disassembler/arm_disasm.h"
@@ -234,7 +235,7 @@ void DisassemblerWidget::OnToggleStartStop()
emu_thread.SetCpuRunning(!emu_thread.IsCpuRunning());
}
-void DisassemblerWidget::OnCPUStepped()
+void DisassemblerWidget::OnDebugModeEntered()
{
ARMword next_instr = Core::g_app_core->GetPC();
@@ -251,6 +252,11 @@ void DisassemblerWidget::OnCPUStepped()
disasm_ui.treeView->selectionModel()->setCurrentIndex(model_index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
}
+void DisassemblerWidget::OnDebugModeLeft()
+{
+
+}
+
int DisassemblerWidget::SelectedRow()
{
QModelIndex index = disasm_ui.treeView->selectionModel()->currentIndex();
diff --git a/src/citra_qt/debugger/disassembler.h b/src/citra_qt/debugger/disassembler.h
index 6d3cef10..0deccc24 100644
--- a/src/citra_qt/debugger/disassembler.h
+++ b/src/citra_qt/debugger/disassembler.h
@@ -61,7 +61,8 @@ public slots:
void OnPause();
void OnToggleStartStop();
- void OnCPUStepped();
+ void OnDebugModeEntered();
+ void OnDebugModeLeft();
private:
// returns -1 if no row is selected
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index 4a6159fd..bd420f24 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -76,6 +76,8 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo
format_choice->addItem(tr("IA4"));
format_choice->addItem(tr("UNK10"));
format_choice->addItem(tr("A4"));
+ format_choice->addItem(tr("ETC1"));
+ format_choice->addItem(tr("ETC1A4"));
format_choice->setCurrentIndex(static_cast<int>(info.format));
connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int)));
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
index caa6896f..a9423d6c 100644
--- a/src/citra_qt/debugger/graphics_framebuffer.cpp
+++ b/src/citra_qt/debugger/graphics_framebuffer.cpp
@@ -158,17 +158,17 @@ void GraphicsFramebufferWidget::OnFramebufferAddressChanged(qint64 new_value)
}
}
-void GraphicsFramebufferWidget::OnFramebufferWidthChanged(unsigned int new_value)
+void GraphicsFramebufferWidget::OnFramebufferWidthChanged(int new_value)
{
- if (framebuffer_width != new_value) {
- framebuffer_width = new_value;
+ if (framebuffer_width != static_cast<unsigned>(new_value)) {
+ framebuffer_width = static_cast<unsigned>(new_value);
framebuffer_source_list->setCurrentIndex(static_cast<int>(Source::Custom));
emit Update();
}
}
-void GraphicsFramebufferWidget::OnFramebufferHeightChanged(unsigned int new_value)
+void GraphicsFramebufferWidget::OnFramebufferHeightChanged(int new_value)
{
if (framebuffer_height != new_value) {
framebuffer_height = new_value;
diff --git a/src/citra_qt/debugger/graphics_framebuffer.h b/src/citra_qt/debugger/graphics_framebuffer.h
index 02813525..56215761 100644
--- a/src/citra_qt/debugger/graphics_framebuffer.h
+++ b/src/citra_qt/debugger/graphics_framebuffer.h
@@ -62,8 +62,8 @@ public:
public slots:
void OnFramebufferSourceChanged(int new_value);
void OnFramebufferAddressChanged(qint64 new_value);
- void OnFramebufferWidthChanged(unsigned int new_value);
- void OnFramebufferHeightChanged(unsigned int new_value);
+ void OnFramebufferWidthChanged(int new_value);
+ void OnFramebufferHeightChanged(int new_value);
void OnFramebufferFormatChanged(int new_value);
void OnUpdate();
diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp
index e982dfb3..ab366615 100644
--- a/src/citra_qt/debugger/registers.cpp
+++ b/src/citra_qt/debugger/registers.cpp
@@ -41,7 +41,7 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent)
CSPR->addChild(new QTreeWidgetItem(QStringList("N")));
}
-void RegistersWidget::OnCPUStepped()
+void RegistersWidget::OnDebugModeEntered()
{
ARM_Interface* app_core = Core::g_app_core;
@@ -65,3 +65,8 @@ void RegistersWidget::OnCPUStepped()
CSPR->child(13)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 30) & 0x1)); // Z - Zero
CSPR->child(14)->setText(1, QString("%1").arg((app_core->GetCPSR() >> 31) & 0x1)); // N - Negative/Less than
}
+
+void RegistersWidget::OnDebugModeLeft()
+{
+
+}
diff --git a/src/citra_qt/debugger/registers.h b/src/citra_qt/debugger/registers.h
index ac8429f2..bf895562 100644
--- a/src/citra_qt/debugger/registers.h
+++ b/src/citra_qt/debugger/registers.h
@@ -17,7 +17,8 @@ public:
RegistersWidget(QWidget* parent = NULL);
public slots:
- void OnCPUStepped();
+ void OnDebugModeEntered();
+ void OnDebugModeLeft();
private:
Ui::ARMRegisters cpu_regs_ui;
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index c6671bef..ece593e5 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -124,9 +124,13 @@ GMainWindow::GMainWindow()
connect(ui.action_Hotkeys, SIGNAL(triggered()), this, SLOT(OnOpenHotkeysDialog()));
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views before the CPU continues
- connect(&render_window->GetEmuThread(), SIGNAL(CPUStepped()), disasmWidget, SLOT(OnCPUStepped()), Qt::BlockingQueuedConnection);
- connect(&render_window->GetEmuThread(), SIGNAL(CPUStepped()), registersWidget, SLOT(OnCPUStepped()), Qt::BlockingQueuedConnection);
- connect(&render_window->GetEmuThread(), SIGNAL(CPUStepped()), callstackWidget, SLOT(OnCPUStepped()), Qt::BlockingQueuedConnection);
+ connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), disasmWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
+ connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), registersWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
+ connect(&render_window->GetEmuThread(), SIGNAL(DebugModeEntered()), callstackWidget, SLOT(OnDebugModeEntered()), Qt::BlockingQueuedConnection);
+
+ connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), disasmWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
+ connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), registersWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
+ connect(&render_window->GetEmuThread(), SIGNAL(DebugModeLeft()), callstackWidget, SLOT(OnDebugModeLeft()), Qt::BlockingQueuedConnection);
// Setup hotkeys
RegisterHotkey("Main Window", "Load File", QKeySequence::Open);
@@ -167,8 +171,8 @@ void GMainWindow::BootGame(std::string filename)
}
disasmWidget->Init();
- registersWidget->OnCPUStepped();
- callstackWidget->OnCPUStepped();
+ registersWidget->OnDebugModeEntered();
+ callstackWidget->OnDebugModeEntered();
render_window->GetEmuThread().SetFilename(filename);
render_window->GetEmuThread().start();
diff --git a/src/common/common.h b/src/common/common.h
index bf48ae66..3246c779 100644
--- a/src/common/common.h
+++ b/src/common/common.h
@@ -11,13 +11,6 @@
#include <cstdio>
#include <cstring>
-// Force enable logging in the right modes. For some reason, something had changed
-// so that debugfast no longer logged.
-#if defined(_DEBUG) || defined(DEBUGFAST)
-#undef LOGGING
-#define LOGGING 1
-#endif
-
#define STACKALIGN
// An inheritable class to disallow the copy constructor and operator= functions
diff --git a/src/common/log.h b/src/common/log.h
index 667f2fbb..b397cf14 100644
--- a/src/common/log.h
+++ b/src/common/log.h
@@ -14,7 +14,7 @@
#endif
#endif
-#if _DEBUG
+#ifdef _DEBUG
#define _dbg_assert_(_t_, _a_) \
if (!(_a_)) {\
LOG_CRITICAL(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index bda3d633..3d94bf0d 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -74,17 +74,6 @@ enum class Class : ClassType {
};
/**
- * Level below which messages are simply discarded without buffering regardless of the display
- * settings.
- */
-const Level MINIMUM_LEVEL =
-#ifdef _DEBUG
- Level::Trace;
-#else
- Level::Debug;
-#endif
-
-/**
* Logs a message to the global logger. This proxy exists to avoid exposing the details of the
* Logger class, including the ConcurrentRingBuffer template, to all files that desire to log
* messages, reducing unecessary recompilations.
@@ -103,13 +92,15 @@ void LogMessage(Class log_class, Level log_level,
} // namespace Log
#define LOG_GENERIC(log_class, log_level, ...) \
- do { \
- if (::Log::Level::log_level >= ::Log::MINIMUM_LEVEL) \
- ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \
- __FILE__, __LINE__, __func__, __VA_ARGS__); \
- } while (0)
+ ::Log::LogMessage(::Log::Class::log_class, ::Log::Level::log_level, \
+ __FILE__, __LINE__, __func__, __VA_ARGS__)
+#ifdef _DEBUG
#define LOG_TRACE( log_class, ...) LOG_GENERIC(log_class, Trace, __VA_ARGS__)
+#else
+#define LOG_TRACE( log_class, ...) (void(0))
+#endif
+
#define LOG_DEBUG( log_class, ...) LOG_GENERIC(log_class, Debug, __VA_ARGS__)
#define LOG_INFO( log_class, ...) LOG_GENERIC(log_class, Info, __VA_ARGS__)
#define LOG_WARNING( log_class, ...) LOG_GENERIC(log_class, Warning, __VA_ARGS__)
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index d248b454..0fc8bf31 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -31,17 +31,20 @@ set(SRCS
hle/kernel/mutex.cpp
hle/kernel/semaphore.cpp
hle/kernel/shared_memory.cpp
+ hle/kernel/timer.cpp
hle/kernel/thread.cpp
hle/service/ac_u.cpp
hle/service/act_u.cpp
hle/service/am_app.cpp
hle/service/am_net.cpp
hle/service/apt_a.cpp
+ hle/service/apt_s.cpp
hle/service/apt_u.cpp
hle/service/boss_u.cpp
hle/service/cecd_u.cpp
hle/service/cfg/cfg.cpp
hle/service/cfg/cfg_i.cpp
+ hle/service/cfg/cfg_s.cpp
hle/service/cfg/cfg_u.cpp
hle/service/csnd_snd.cpp
hle/service/dsp_dsp.cpp
@@ -62,6 +65,7 @@ set(SRCS
hle/service/nwm_uds.cpp
hle/service/pm_app.cpp
hle/service/ptm_u.cpp
+ hle/service/ptm_sysm.cpp
hle/service/service.cpp
hle/service/soc_u.cpp
hle/service/srv.cpp
@@ -124,17 +128,20 @@ set(HEADERS
hle/kernel/semaphore.h
hle/kernel/session.h
hle/kernel/shared_memory.h
+ hle/kernel/timer.h
hle/kernel/thread.h
hle/service/ac_u.h
hle/service/act_u.h
hle/service/am_app.h
hle/service/am_net.h
hle/service/apt_a.h
+ hle/service/apt_s.h
hle/service/apt_u.h
hle/service/boss_u.h
hle/service/cecd_u.h
hle/service/cfg/cfg.h
hle/service/cfg/cfg_i.h
+ hle/service/cfg/cfg_s.h
hle/service/cfg/cfg_u.h
hle/service/csnd_snd.h
hle/service/dsp_dsp.h
@@ -155,6 +162,7 @@ set(HEADERS
hle/service/nwm_uds.h
hle/service/pm_app.h
hle/service/ptm_u.h
+ hle/service/ptm_sysm.h
hle/service/service.h
hle/service/soc_u.h
hle/service/srv.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index d3bd4a9a..e612f743 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -7,7 +7,9 @@
#include "common/common.h"
#include "common/common_types.h"
-#include "core/hle/svc.h"
+namespace Core {
+ struct ThreadContext;
+}
/// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable {
@@ -87,13 +89,13 @@ public:
* Saves the current CPU context
* @param ctx Thread context to save
*/
- virtual void SaveContext(ThreadContext& ctx) = 0;
+ virtual void SaveContext(Core::ThreadContext& ctx) = 0;
/**
* Loads a CPU context
* @param ctx Thread context to load
*/
- virtual void LoadContext(const ThreadContext& ctx) = 0;
+ virtual void LoadContext(const Core::ThreadContext& ctx) = 0;
/// Prepare core for thread reschedule (if needed to correctly handle state)
virtual void PrepareReschedule() = 0;
diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp
index c779e3fd..9c4cc90f 100644
--- a/src/core/arm/dyncom/arm_dyncom.cpp
+++ b/src/core/arm/dyncom/arm_dyncom.cpp
@@ -9,13 +9,14 @@
#include "core/arm/dyncom/arm_dyncom.h"
#include "core/arm/dyncom/arm_dyncom_interpreter.h"
+#include "core/core.h"
#include "core/core_timing.h"
const static cpu_config_t s_arm11_cpu_info = {
"armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
};
-ARM_DynCom::ARM_DynCom() : ticks(0) {
+ARM_DynCom::ARM_DynCom() {
state = std::unique_ptr<ARMul_State>(new ARMul_State);
ARMul_EmulateInit();
@@ -74,11 +75,11 @@ void ARM_DynCom::SetCPSR(u32 cpsr) {
}
u64 ARM_DynCom::GetTicks() const {
- return ticks;
+ // TODO(Subv): Remove ARM_DynCom::GetTicks() and use CoreTiming::GetTicks() directly once ARMemu is gone
+ return CoreTiming::GetTicks();
}
void ARM_DynCom::AddTicks(u64 ticks) {
- this->ticks += ticks;
down_count -= ticks;
if (down_count < 0)
CoreTiming::Advance();
@@ -94,7 +95,7 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) {
AddTicks(ticks_executed);
}
-void ARM_DynCom::SaveContext(ThreadContext& ctx) {
+void ARM_DynCom::SaveContext(Core::ThreadContext& ctx) {
memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
@@ -110,7 +111,7 @@ void ARM_DynCom::SaveContext(ThreadContext& ctx) {
ctx.mode = state->NextInstr;
}
-void ARM_DynCom::LoadContext(const ThreadContext& ctx) {
+void ARM_DynCom::LoadContext(const Core::ThreadContext& ctx) {
memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h
index 7284dcd0..f16fb070 100644
--- a/src/core/arm/dyncom/arm_dyncom.h
+++ b/src/core/arm/dyncom/arm_dyncom.h
@@ -71,13 +71,13 @@ public:
* Saves the current CPU context
* @param ctx Thread context to save
*/
- void SaveContext(ThreadContext& ctx) override;
+ void SaveContext(Core::ThreadContext& ctx) override;
/**
* Loads a CPU context
* @param ctx Thread context to load
*/
- void LoadContext(const ThreadContext& ctx) override;
+ void LoadContext(const Core::ThreadContext& ctx) override;
/// Prepare core for thread reschedule (if needed to correctly handle state)
void PrepareReschedule() override;
@@ -89,8 +89,5 @@ public:
void ExecuteInstructions(int num_instructions) override;
private:
-
std::unique_ptr<ARMul_State> state;
- u64 ticks;
-
};
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 7c710ccd..d0347566 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -176,13 +176,11 @@ unsigned int DPO(ArithmeticShiftRightByImmediate)(arm_processor *cpu, unsigned i
unsigned int shifter_operand;
int shift_imm = BITS(sht_oper, 7, 11);
if (shift_imm == 0) {
- if (BIT(rm, 31)) {
+ if (BIT(rm, 31) == 0)
shifter_operand = 0;
- cpu->shifter_carry_out = BIT(rm, 31);
- } else {
+ else
shifter_operand = 0xFFFFFFFF;
- cpu->shifter_carry_out = BIT(rm, 31);
- }
+ cpu->shifter_carry_out = BIT(rm, 31);
} else {
shifter_operand = static_cast<int>(rm) >> shift_imm;
cpu->shifter_carry_out = BIT(rm, shift_imm - 1);
@@ -1728,25 +1726,21 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(ldrb)(unsigned int inst, int index)
}
ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index)
{
- arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
- ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_base->cond = BITS(inst, 28, 31);
- inst_base->idx = index;
- inst_base->br = NON_BRANCH;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
inst_cream->inst = inst;
- if (I_BIT == 0) {
+ if (BITS(inst, 25, 27) == 2) {
inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
} else {
DEBUG_MSG;
}
- #if 0
- inst_cream->get_addr = get_calc_addr_op(inst);
- if(inst == 0x54f13001) {
- DEBUG_LOG(ARM11, "get_calc_addr_op:%llx\n", inst_cream->get_addr);
- }
- #endif
if (BITS(inst, 12, 15) == 15) {
inst_base->br = INDIRECT_BRANCH;
@@ -1846,17 +1840,24 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index)
}
ARM_INST_PTR INTERPRETER_TRANSLATE(ldrt)(unsigned int inst, int index)
{
- arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
- ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_base->cond = BITS(inst, 28, 31);
- inst_base->idx = index;
- inst_base->br = NON_BRANCH;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
inst_cream->inst = inst;
- if (I_BIT == 0) {
+ if (BITS(inst, 25, 27) == 2) {
inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
} else {
+ // Reaching this would indicate the thumb version
+ // of this instruction, however the 3DS CPU doesn't
+ // support this variant (the 3DS CPU is only ARMv6K,
+ // while this variant is added in ARMv6T2).
+ // So it's sufficient for citra to not implement this.
DEBUG_MSG;
}
@@ -2714,17 +2715,19 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(strb)(unsigned int inst, int index)
}
ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index)
{
- arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
- ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_base->cond = BITS(inst, 28, 31);
- inst_base->idx = index;
- inst_base->br = NON_BRANCH;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
inst_cream->inst = inst;
-// inst_cream->get_addr = get_calc_addr_op(inst);
- if (I_BIT == 0) {
+
+ if (BITS(inst, 25, 27) == 2) {
inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
} else {
DEBUG_MSG;
}
@@ -2796,17 +2799,24 @@ ARM_INST_PTR INTERPRETER_TRANSLATE(strh)(unsigned int inst, int index)
}
ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index)
{
- arm_inst *inst_base = (arm_inst *)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
- ldst_inst *inst_cream = (ldst_inst *)inst_base->component;
+ arm_inst* inst_base = (arm_inst*)AllocBuffer(sizeof(arm_inst) + sizeof(ldst_inst));
+ ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_base->cond = BITS(inst, 28, 31);
- inst_base->idx = index;
- inst_base->br = NON_BRANCH;
+ inst_base->idx = index;
+ inst_base->br = NON_BRANCH;
inst_cream->inst = inst;
- if (I_BIT == 0) {
+ if (BITS(inst, 25, 27) == 2) {
inst_cream->get_addr = LnSWoUB(ImmediatePostIndexed);
+ } else if (BITS(inst, 25, 27) == 3) {
+ inst_cream->get_addr = LnSWoUB(ScaledRegisterPostIndexed);
} else {
+ // Reaching this would indicate the thumb version
+ // of this instruction, however the 3DS CPU doesn't
+ // support this variant (the 3DS CPU is only ARMv6K,
+ // while this variant is added in ARMv6T2).
+ // So it's sufficient for citra to not implement this.
DEBUG_MSG;
}
@@ -3905,21 +3915,9 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
#endif
- #define UPDATE_NFLAG(dst) (cpu->NFlag = BIT(dst, 31) ? 1 : 0)
- #define UPDATE_ZFLAG(dst) (cpu->ZFlag = dst ? 0 : 1)
-
- #define UPDATE_CFLAG(dst, lop, rop) (cpu->CFlag = ((dst < lop) || (dst < rop)))
- #define UPDATE_CFLAG_CARRY_FROM_ADD(lop, rop, flag) (cpu->CFlag = (((uint64_t) lop + (uint64_t) rop + (uint64_t) flag) > 0xffffffff) )
- #define UPDATE_CFLAG_NOT_BORROW_FROM_FLAG(lop, rop, flag) (cpu->CFlag = ((uint64_t) lop >= ((uint64_t) rop + (uint64_t) flag)))
- #define UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop) (cpu->CFlag = (lop >= rop))
- #define UPDATE_CFLAG_WITH_NOT(dst, lop, rop) (cpu->CFlag = !(dst < lop))
- #define UPDATE_CFLAG_WITH_SC (cpu->CFlag = cpu->shifter_carry_out)
-
- #define UPDATE_VFLAG(dst, lop, rop) (cpu->VFlag = (((lop < 0) && (rop < 0) && (dst >= 0)) || \
- ((lop >= 0) && (rop) >= 0 && (dst < 0))))
- #define UPDATE_VFLAG_WITH_NOT(dst, lop, rop) (cpu->VFlag = !(((lop < 0) && (rop < 0) && (dst >= 0)) || \
- ((lop >= 0) && (rop) >= 0 && (dst < 0))))
- #define UPDATE_VFLAG_OVERFLOW_FROM(dst, lop, rop) (cpu->VFlag = (((lop ^ rop) & (lop ^ dst)) >> 31))
+ #define UPDATE_NFLAG(dst) (cpu->NFlag = BIT(dst, 31) ? 1 : 0)
+ #define UPDATE_ZFLAG(dst) (cpu->ZFlag = dst ? 0 : 1)
+ #define UPDATE_CFLAG_WITH_SC (cpu->CFlag = cpu->shifter_carry_out)
#define SAVE_NZCVT cpu->Cpsr = (cpu->Cpsr & 0x0fffffdf) | \
(cpu->NFlag << 31) | \
@@ -3967,16 +3965,12 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
&&INIT_INST_LENGTH,&&END
};
#endif
- arm_inst * inst_base;
- unsigned int lop, rop, dst;
+ arm_inst* inst_base;
unsigned int addr;
unsigned int phys_addr;
- unsigned int last_pc = 0;
unsigned int num_instrs = 0;
- static unsigned int last_physical_base = 0, last_logical_base = 0;
int ptr;
- bool single_step = (cpu->NumInstrsToExecute == 1);
LOAD_NZCVT;
DISPATCH:
@@ -4003,12 +3997,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
ADC_INST:
{
- adc_inst *inst_cream = (adc_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- unsigned int sht_op = SHIFTER_OPERAND;
- rop = SHIFTER_OPERAND + cpu->CFlag;
- RD = dst = lop + rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ adc_inst* const inst_cream = (adc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(RN, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4016,10 +4011,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_CARRY_FROM_ADD(lop, sht_op, cpu->CFlag);
- UPDATE_VFLAG((int)dst, (int)lop, (int)rop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(adc_inst));
@@ -4033,14 +4028,17 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
ADD_INST:
{
- add_inst *inst_cream = (add_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- if (inst_cream->Rn == 15) {
- lop += 2 * GET_INST_SIZE(cpu);
- }
- rop = SHIFTER_OPERAND;
- RD = dst = lop + rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ add_inst* const inst_cream = (add_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 2 * GET_INST_SIZE(cpu);
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(rn_val, SHIFTER_OPERAND, 0, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4048,10 +4046,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG(dst, lop, rop);
- UPDATE_VFLAG((int)dst, (int)lop, (int)rop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(add_inst));
@@ -4067,9 +4065,9 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
and_inst *inst_cream = (and_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- rop = SHIFTER_OPERAND;
- RD = dst = lop & rop;
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop & rop;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4077,8 +4075,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -4110,12 +4108,12 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
bic_inst *inst_cream = (bic_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
+ u32 lop = RN;
if (inst_cream->Rn == 15) {
lop += 2 * GET_INST_SIZE(cpu);
}
- rop = SHIFTER_OPERAND;
- RD = dst = lop & (~rop);
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop & (~rop);
if ((inst_cream->S) && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4123,8 +4121,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -4230,15 +4228,17 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
CMN_INST:
{
- cmn_inst *inst_cream = (cmn_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- rop = SHIFTER_OPERAND;
- dst = lop + rop;
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG(dst, lop, rop);
- UPDATE_VFLAG((int)dst, (int)lop, (int)rop);
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ cmn_inst* const inst_cream = (cmn_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ u32 result = AddWithCarry(RN, SHIFTER_OPERAND, 0, &carry, &overflow);
+
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(cmn_inst));
@@ -4247,19 +4247,21 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
CMP_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- cmp_inst *inst_cream = (cmp_inst *)inst_base->component;
- lop = RN;
- if (inst_cream->Rn == 15) {
- lop += 2 * GET_INST_SIZE(cpu);
- }
- rop = SHIFTER_OPERAND;
- dst = lop - rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ cmp_inst* const inst_cream = (cmp_inst*)inst_base->component;
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop);
- UPDATE_VFLAG_OVERFLOW_FROM(dst, lop, rop);
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 2 * GET_INST_SIZE(cpu);
+
+ bool carry;
+ bool overflow;
+ u32 result = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow);
+
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
INC_PC(sizeof(cmp_inst));
@@ -4317,12 +4319,12 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
eor_inst *inst_cream = (eor_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
+ u32 lop = RN;
if (inst_cream->Rn == 15) {
lop += 2 * GET_INST_SIZE(cpu);
}
- rop = SHIFTER_OPERAND;
- RD = dst = lop ^ rop;
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop ^ rop;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4330,8 +4332,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -4848,10 +4850,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOG_ERROR(Core_ARM11, "invalid operands for MLA");
CITRA_IGNORE_EXIT(-1);
}
- RD = dst = static_cast<uint32_t>((rm * rs + rn) & 0xffffffff);
+ RD = static_cast<uint32_t>((rm * rs + rn) & 0xffffffff);
if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mla_inst));
@@ -4867,7 +4869,7 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
mov_inst *inst_cream = (mov_inst *)inst_base->component;
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- RD = dst = SHIFTER_OPERAND;
+ RD = SHIFTER_OPERAND;
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -4875,8 +4877,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -4964,39 +4966,41 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
MSR_INST:
{
- msr_inst *inst_cream = (msr_inst *)inst_base->component;
- const uint32_t UnallocMask = 0x06f0fc00, UserMask = 0xf80f0200, PrivMask = 0x000001df, StateMask = 0x01000020;
- unsigned int inst = inst_cream->inst;
- unsigned int operand;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ msr_inst *inst_cream = (msr_inst *)inst_base->component;
+ const uint32_t UnallocMask = 0x06f0fc00, UserMask = 0xf80f0200, PrivMask = 0x000001df, StateMask = 0x01000020;
+ unsigned int inst = inst_cream->inst;
+ unsigned int operand;
- if (BIT(inst, 25)) {
- int rot_imm = BITS(inst, 8, 11) * 2;
- operand = ROTATE_RIGHT_32(BITS(inst, 0, 7), rot_imm);
- } else {
- operand = cpu->Reg[BITS(inst, 0, 3)];
- }
- uint32_t byte_mask = (BIT(inst, 16) ? 0xff : 0) | (BIT(inst, 17) ? 0xff00 : 0)
- | (BIT(inst, 18) ? 0xff0000 : 0) | (BIT(inst, 19) ? 0xff000000 : 0);
- uint32_t mask;
- if (!inst_cream->R) {
- if (InAPrivilegedMode(cpu)) {
- if ((operand & StateMask) != 0) {
- /// UNPREDICTABLE
- DEBUG_MSG;
- } else
- mask = byte_mask & (UserMask | PrivMask);
+ if (BIT(inst, 25)) {
+ int rot_imm = BITS(inst, 8, 11) * 2;
+ operand = ROTATE_RIGHT_32(BITS(inst, 0, 7), rot_imm);
} else {
- mask = byte_mask & UserMask;
+ operand = cpu->Reg[BITS(inst, 0, 3)];
}
- SAVE_NZCVT;
+ uint32_t byte_mask = (BIT(inst, 16) ? 0xff : 0) | (BIT(inst, 17) ? 0xff00 : 0)
+ | (BIT(inst, 18) ? 0xff0000 : 0) | (BIT(inst, 19) ? 0xff000000 : 0);
+ uint32_t mask;
+ if (!inst_cream->R) {
+ if (InAPrivilegedMode(cpu)) {
+ if ((operand & StateMask) != 0) {
+ /// UNPREDICTABLE
+ DEBUG_MSG;
+ } else
+ mask = byte_mask & (UserMask | PrivMask);
+ } else {
+ mask = byte_mask & UserMask;
+ }
+ SAVE_NZCVT;
- cpu->Cpsr = (cpu->Cpsr & ~mask) | (operand & mask);
- switch_mode(cpu, cpu->Cpsr & 0x1f);
- LOAD_NZCVT;
- } else {
- if (CurrentModeHasSPSR) {
- mask = byte_mask & (UserMask | PrivMask | StateMask);
- cpu->Spsr_copy = (cpu->Spsr_copy & ~mask) | (operand & mask);
+ cpu->Cpsr = (cpu->Cpsr & ~mask) | (operand & mask);
+ switch_mode(cpu, cpu->Cpsr & 0x1f);
+ LOAD_NZCVT;
+ } else {
+ if (CurrentModeHasSPSR) {
+ mask = byte_mask & (UserMask | PrivMask | StateMask);
+ cpu->Spsr_copy = (cpu->Spsr_copy & ~mask) | (operand & mask);
+ }
}
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -5010,10 +5014,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
uint64_t rm = RM;
uint64_t rs = RS;
- RD = dst = static_cast<uint32_t>((rm * rs) & 0xffffffff);
+ RD = static_cast<uint32_t>((rm * rs) & 0xffffffff);
if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(mul_inst));
@@ -5027,9 +5031,11 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
MVN_INST:
{
- mvn_inst *inst_cream = (mvn_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- RD = dst = ~SHIFTER_OPERAND;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ mvn_inst* const inst_cream = (mvn_inst*)inst_base->component;
+
+ RD = ~SHIFTER_OPERAND;
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5037,8 +5043,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -5053,11 +5059,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
ORR_INST:
{
- orr_inst *inst_cream = (orr_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- rop = SHIFTER_OPERAND;
- RD = dst = lop | rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ orr_inst* const inst_cream = (orr_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
+ RD = lop | rop;
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5065,8 +5073,8 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
UPDATE_CFLAG_WITH_SC;
}
if (inst_cream->Rd == 15) {
@@ -5286,14 +5294,17 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
RFE_INST:
RSB_INST:
{
- rsb_inst *inst_cream = (rsb_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- rop = RN;
- lop = SHIFTER_OPERAND;
- if (inst_cream->Rn == 15) {
- rop += 2 * GET_INST_SIZE(cpu);;
- }
- RD = dst = lop - rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ rsb_inst* const inst_cream = (rsb_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 2 * GET_INST_SIZE(cpu);
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(~rn_val, SHIFTER_OPERAND, 1, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5301,10 +5312,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop);
- UPDATE_VFLAG_OVERFLOW_FROM(dst, lop, rop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsb_inst));
@@ -5318,11 +5329,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
RSC_INST:
{
- rsc_inst *inst_cream = (rsc_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- rop = SHIFTER_OPERAND;
- RD = dst = rop - lop - !cpu->CFlag;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ rsc_inst* const inst_cream = (rsc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(~RN, SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5330,10 +5343,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_NOT_BORROW_FROM_FLAG(rop, lop, !cpu->CFlag);
- UPDATE_VFLAG_OVERFLOW_FROM((int)dst, (int)rop, (int)lop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(rsc_inst));
@@ -5456,11 +5469,13 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
SBC_INST:
{
- sbc_inst *inst_cream = (sbc_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = SHIFTER_OPERAND + !cpu->CFlag;
- rop = RN;
- RD = dst = rop - lop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sbc_inst* const inst_cream = (sbc_inst*)inst_base->component;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(RN, ~SHIFTER_OPERAND, cpu->CFlag, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -5468,15 +5483,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
-
- if(rop >= !cpu->CFlag)
- UPDATE_CFLAG_NOT_BORROW_FROM(rop - !cpu->CFlag, SHIFTER_OPERAND);
- else
- UPDATE_CFLAG_NOT_BORROW_FROM(rop, !cpu->CFlag);
-
- UPDATE_VFLAG_OVERFLOW_FROM(dst, rop, lop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sbc_inst));
@@ -6254,14 +6264,17 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
SUB_INST:
{
- sub_inst *inst_cream = (sub_inst *)inst_base->component;
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- lop = RN;
- if (inst_cream->Rn == 15) {
- lop += 8;
- }
- rop = SHIFTER_OPERAND;
- RD = dst = lop - rop;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ sub_inst* const inst_cream = (sub_inst*)inst_base->component;
+
+ u32 rn_val = RN;
+ if (inst_cream->Rn == 15)
+ rn_val += 8;
+
+ bool carry;
+ bool overflow;
+ RD = AddWithCarry(rn_val, ~SHIFTER_OPERAND, 1, &carry, &overflow);
+
if (inst_cream->S && (inst_cream->Rd == 15)) {
if (CurrentModeHasSPSR) {
cpu->Cpsr = cpu->Spsr_copy;
@@ -6269,10 +6282,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
LOAD_NZCVT;
}
} else if (inst_cream->S) {
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
- UPDATE_CFLAG_NOT_BORROW_FROM(lop, rop);
- UPDATE_VFLAG_OVERFLOW_FROM(dst, lop, rop);
+ UPDATE_NFLAG(RD);
+ UPDATE_ZFLAG(RD);
+ cpu->CFlag = carry;
+ cpu->VFlag = overflow;
}
if (inst_cream->Rd == 15) {
INC_PC(sizeof(sub_inst));
@@ -6400,18 +6413,19 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
TEQ_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- teq_inst *inst_cream = (teq_inst *)inst_base->component;
- lop = RN;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ teq_inst* const inst_cream = (teq_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
if (inst_cream->Rn == 15)
lop += GET_INST_SIZE(cpu) * 2;
- rop = SHIFTER_OPERAND;
- dst = lop ^ rop;
+ u32 result = lop ^ rop;
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
UPDATE_CFLAG_WITH_SC;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -6421,18 +6435,19 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
}
TST_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
- tst_inst *inst_cream = (tst_inst *)inst_base->component;
- lop = RN;
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
+ tst_inst* const inst_cream = (tst_inst*)inst_base->component;
+
+ u32 lop = RN;
+ u32 rop = SHIFTER_OPERAND;
if (inst_cream->Rn == 15)
lop += GET_INST_SIZE(cpu) * 2;
- rop = SHIFTER_OPERAND;
- dst = lop & rop;
+ u32 result = lop & rop;
- UPDATE_NFLAG(dst);
- UPDATE_ZFLAG(dst);
+ UPDATE_NFLAG(result);
+ UPDATE_ZFLAG(result);
UPDATE_CFLAG_WITH_SC;
}
cpu->Reg[15] += GET_INST_SIZE(cpu);
@@ -6696,10 +6711,10 @@ unsigned InterpreterMainLoop(ARMul_State* state) {
{
if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
umaal_inst* const inst_cream = (umaal_inst*)inst_base->component;
- const u32 rm = RM;
- const u32 rn = RN;
- const u32 rd_lo = RDLO;
- const u32 rd_hi = RDHI;
+ const u64 rm = RM;
+ const u64 rn = RN;
+ const u64 rd_lo = RDLO;
+ const u64 rd_hi = RDHI;
const u64 result = (rm * rn) + rd_lo + rd_hi;
RDLO = (result & 0xFFFFFFFF);
diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp
index 80ebc359..c76d371a 100644
--- a/src/core/arm/interpreter/arm_interpreter.cpp
+++ b/src/core/arm/interpreter/arm_interpreter.cpp
@@ -4,6 +4,8 @@
#include "core/arm/interpreter/arm_interpreter.h"
+#include "core/core.h"
+
const static cpu_config_t arm11_cpu_info = {
"armv6", "arm11", 0x0007b000, 0x0007f000, NONCACHE
};
@@ -75,7 +77,7 @@ void ARM_Interpreter::ExecuteInstructions(int num_instructions) {
ARMul_Emulate32(state);
}
-void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
+void ARM_Interpreter::SaveContext(Core::ThreadContext& ctx) {
memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers));
memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers));
@@ -91,7 +93,7 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) {
ctx.mode = state->NextInstr;
}
-void ARM_Interpreter::LoadContext(const ThreadContext& ctx) {
+void ARM_Interpreter::LoadContext(const Core::ThreadContext& ctx) {
memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers));
memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers));
diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h
index 019dad5d..e5ecc69c 100644
--- a/src/core/arm/interpreter/arm_interpreter.h
+++ b/src/core/arm/interpreter/arm_interpreter.h
@@ -70,13 +70,13 @@ public:
* Saves the current CPU context
* @param ctx Thread context to save
*/
- void SaveContext(ThreadContext& ctx) override;
+ void SaveContext(Core::ThreadContext& ctx) override;
/**
* Loads a CPU context
* @param ctx Thread context to load
*/
- void LoadContext(const ThreadContext& ctx) override;
+ void LoadContext(const Core::ThreadContext& ctx) override;
/// Prepare core for thread reschedule (if needed to correctly handle state)
void PrepareReschedule() override;
diff --git a/src/core/arm/interpreter/armsupp.cpp b/src/core/arm/interpreter/armsupp.cpp
index 68ac2a0c..e2626eef 100644
--- a/src/core/arm/interpreter/armsupp.cpp
+++ b/src/core/arm/interpreter/armsupp.cpp
@@ -418,6 +418,22 @@ ARMul_NegZero (ARMul_State * state, ARMword result)
}
}
+// Add with carry, indicates if a carry-out or signed overflow occurred.
+u32 AddWithCarry(u32 left, u32 right, u32 carry_in, bool* carry_out_occurred, bool* overflow_occurred)
+{
+ u64 unsigned_sum = (u64)left + (u64)right + (u64)carry_in;
+ s64 signed_sum = (s64)(s32)left + (s64)(s32)right + (s64)carry_in;
+ u64 result = (unsigned_sum & 0xFFFFFFFF);
+
+ if (carry_out_occurred)
+ *carry_out_occurred = (result != unsigned_sum);
+
+ if (overflow_occurred)
+ *overflow_occurred = ((s64)(s32)result != signed_sum);
+
+ return (u32)result;
+}
+
// Compute whether an addition of A and B, giving RESULT, overflowed.
bool AddOverflow(ARMword a, ARMword b, ARMword result)
{
diff --git a/src/core/arm/skyeye_common/armdefs.h b/src/core/arm/skyeye_common/armdefs.h
index 1b2cef45..560b51a9 100644
--- a/src/core/arm/skyeye_common/armdefs.h
+++ b/src/core/arm/skyeye_common/armdefs.h
@@ -795,6 +795,7 @@ extern void ARMul_FixSPSR(ARMul_State*, ARMword, ARMword);
extern void ARMul_ConsolePrint(ARMul_State*, const char*, ...);
extern void ARMul_SelectProcessor(ARMul_State*, unsigned);
+extern u32 AddWithCarry(u32, u32, u32, bool*, bool*);
extern bool ARMul_AddOverflowQ(ARMword, ARMword);
extern u8 ARMul_SignedSaturatedAdd8(u8, u8);
diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
index 08d0d719..77b52860 100644
--- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp
@@ -959,70 +959,34 @@ vfp_single_multiply(struct vfp_single *vsd, struct vfp_single *vsn, struct vfp_s
static u32
vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr, u32 negate, const char *func)
{
-
- {
- struct vfp_single vsd, vsp, vsn, vsm;
- u32 exceptions;
- s32 v;
-
-
-
- v = vfp_get_float(state, sn);
- pr_debug("VFP: s%u = %08x\n", sn, v);
- vfp_single_unpack(&vsn, v);
- if (vsn.exponent == 0 && vsn.significand)
- vfp_single_normalise_denormal(&vsn);
-
- vfp_single_unpack(&vsm, m);
- if (vsm.exponent == 0 && vsm.significand)
- vfp_single_normalise_denormal(&vsm);
-
- exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
-
- if (negate & NEG_MULTIPLY)
- vsp.sign = vfp_sign_negate(vsp.sign);
-
- v = vfp_get_float(state, sd);
- pr_debug("VFP: s%u = %08x\n", sd, v);
- vfp_single_unpack(&vsn, v);
- if (negate & NEG_SUBTRACT)
- vsn.sign = vfp_sign_negate(vsn.sign);
-
- exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
-
- return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func);
- }
-
- struct vfp_double vsd, vsp, vsn, vsm;
+ vfp_single vsd, vsp, vsn, vsm;
u32 exceptions;
s32 v;
- s64 vd;
- s64 md;
v = vfp_get_float(state, sn);
- vd = vfp_single_to_doubleintern(state, v, fpscr);
- vfp_double_unpack(&vsn, vd);
+ pr_debug("VFP: s%u = %08x\n", sn, v);
+ vfp_single_unpack(&vsn, v);
+ if (vsn.exponent == 0 && vsn.significand)
+ vfp_single_normalise_denormal(&vsn);
+
+ vfp_single_unpack(&vsm, m);
+ if (vsm.exponent == 0 && vsm.significand)
+ vfp_single_normalise_denormal(&vsm);
- md = vfp_single_to_doubleintern(state, m, fpscr);
- vfp_double_unpack(&vsm, md);
+ exceptions = vfp_single_multiply(&vsp, &vsn, &vsm, fpscr);
- exceptions = vfp_double_multiply(&vsp, &vsn, &vsm, fpscr);
if (negate & NEG_MULTIPLY)
vsp.sign = vfp_sign_negate(vsp.sign);
v = vfp_get_float(state, sd);
- vd = vfp_single_to_doubleintern(state, v, fpscr);
- vfp_double_unpack(&vsn, vd);
-
+ pr_debug("VFP: s%u = %08x\n", sd, v);
+ vfp_single_unpack(&vsn, v);
if (negate & NEG_SUBTRACT)
vsn.sign = vfp_sign_negate(vsn.sign);
- exceptions |= vfp_double_add(&vsd, &vsn, &vsp, fpscr);
-
- s64 debug = vfp_double_pack(&vsd);
-
- return vfp_double_fcvtsinterncutting(state, sd, &vsd, fpscr);
+ exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr);
+ return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func);
}
/*
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 98f8a7df..e9e5c35c 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -8,6 +8,7 @@
#include "core/core_timing.h"
#include "core/settings.h"
+#include "core/arm/arm_interface.h"
#include "core/arm/disassembler/arm_disasm.h"
#include "core/arm/interpreter/arm_interpreter.h"
#include "core/arm/dyncom/arm_dyncom.h"
@@ -17,8 +18,6 @@
namespace Core {
-static u64 last_ticks = 0; ///< Last CPU ticks
-static ARM_Disasm* disasm = nullptr; ///< ARM disassembler
ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
@@ -26,7 +25,7 @@ ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
void RunLoop(int tight_loop) {
// If the current thread is an idle thread, then don't execute instructions,
// instead advance to the next event and try to yield to the next thread
- if (Kernel::IsIdleThread(Kernel::GetCurrentThreadHandle())) {
+ if (Kernel::GetCurrentThread()->IsIdle()) {
LOG_TRACE(Core_ARM11, "Idling");
CoreTiming::Idle();
CoreTiming::Advance();
@@ -60,7 +59,6 @@ void Stop() {
int Init() {
LOG_DEBUG(Core, "initialized OK");
- disasm = new ARM_Disasm();
g_sys_core = new ARM_Interpreter();
switch (Settings::values.cpu_core) {
@@ -73,13 +71,10 @@ int Init() {
break;
}
- last_ticks = Core::g_app_core->GetTicks();
-
return 0;
}
void Shutdown() {
- delete disasm;
delete g_app_core;
delete g_sys_core;
diff --git a/src/core/core.h b/src/core/core.h
index ecd58a73..2f5e8bc6 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -4,8 +4,9 @@
#pragma once
-#include "core/arm/arm_interface.h"
-#include "core/arm/skyeye_common/armdefs.h"
+#include "common/common_types.h"
+
+class ARM_Interface;
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -16,6 +17,21 @@ enum CPUCore {
CPU_OldInterpreter,
};
+struct ThreadContext {
+ u32 cpu_registers[13];
+ u32 sp;
+ u32 lr;
+ u32 pc;
+ u32 cpsr;
+ u32 fpu_registers[32];
+ u32 fpscr;
+ u32 fpexc;
+
+ // These are not part of native ThreadContext, but needed by emu
+ u32 reg_15;
+ u32 mode;
+};
+
extern ARM_Interface* g_app_core; ///< ARM11 application core
extern ARM_Interface* g_sys_core; ///< ARM11 system (OS) core
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 83319968..ec9d52a0 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -9,6 +9,8 @@
#include "common/chunk_file.h"
#include "common/log.h"
+
+#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 0f822f84..a2f51b41 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -5,6 +5,8 @@
#pragma once
#include "common/common_types.h"
+
+#include "core/arm/arm_interface.h"
#include "core/mem_map.h"
#include "core/hle/hle.h"
@@ -135,6 +137,12 @@ template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() {
FuncReturn(retval);
}
+template<s32 func(u32, s64, s64)> void Wrap() {
+ s64 param1 = ((u64)PARAM(3) << 32) | PARAM(2);
+ s64 param2 = ((u64)PARAM(4) << 32) | PARAM(1);
+ FuncReturn(func(PARAM(0), param1, param2));
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp
index f76048d1..11570c8b 100644
--- a/src/core/hle/hle.cpp
+++ b/src/core/hle/hle.cpp
@@ -4,6 +4,7 @@
#include <vector>
+#include "core/arm/arm_interface.h"
#include "core/mem_map.h"
#include "core/hle/hle.h"
#include "core/hle/shared_page.h"
diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h
index 59b770f0..3f6f9a4b 100644
--- a/src/core/hle/hle.h
+++ b/src/core/hle/hle.h
@@ -4,6 +4,8 @@
#pragma once
+#include <string>
+
#include "common/common_types.h"
#include "core/core.h"
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 736bbc36..b7434aaf 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -29,35 +29,56 @@ public:
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Arbitrate an address
-ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) {
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds) {
+ Object* object = Kernel::g_handle_table.GetGeneric(handle).get();
+ if (object == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
switch (type) {
// Signal thread(s) waiting for arbitrate address...
case ArbitrationType::Signal:
// Negative value means resume all threads
if (value < 0) {
- ArbitrateAllThreads(handle, address);
+ ArbitrateAllThreads(object, address);
} else {
// Resume first N threads
for(int i = 0; i < value; i++)
- ArbitrateHighestPriorityThread(handle, address);
+ ArbitrateHighestPriorityThread(object, address);
}
break;
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
if ((s32)Memory::Read32(address) <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ HLE::Reschedule(__func__);
+ }
+ break;
+ case ArbitrationType::WaitIfLessThanWithTimeout:
+ if ((s32)Memory::Read32(address) <= value) {
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds);
HLE::Reschedule(__func__);
}
break;
-
case ArbitrationType::DecrementAndWaitIfLessThan:
{
s32 memory_value = Memory::Read32(address) - 1;
Memory::Write32(address, memory_value);
if (memory_value <= value) {
- Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address);
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ HLE::Reschedule(__func__);
+ }
+ break;
+ }
+ case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout:
+ {
+ s32 memory_value = Memory::Read32(address) - 1;
+ Memory::Write32(address, memory_value);
+ if (memory_value <= value) {
+ Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address);
+ Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds);
HLE::Reschedule(__func__);
}
break;
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index 030e7ad7..3ffd465a 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -28,7 +28,7 @@ enum class ArbitrationType : u32 {
};
/// Arbitrate an address
-ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value);
+ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds);
/// Create an address arbiter
Handle CreateAddressArbiter(const std::string& name = "Unknown");
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index e43c3ee4..271190db 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -33,11 +33,11 @@ public:
ResultVal<bool> WaitSynchronization() override {
bool wait = locked;
if (locked) {
- Handle thread = GetCurrentThreadHandle();
+ Handle thread = GetCurrentThread()->GetHandle();
if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
waiting_threads.push_back(thread);
}
- Kernel::WaitCurrentThread(WAITTYPE_EVENT, GetHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_EVENT, this);
}
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
locked = true;
@@ -53,7 +53,7 @@ public:
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
- Event* evt = g_handle_table.Get<Event>(handle);
+ Event* evt = g_handle_table.Get<Event>(handle).get();
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
evt->permanent_locked = permanent_locked;
@@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SetEventLocked(const Handle handle, const bool locked) {
- Event* evt = g_handle_table.Get<Event>(handle);
+ Event* evt = g_handle_table.Get<Event>(handle).get();
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
@@ -82,13 +82,15 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode SignalEvent(const Handle handle) {
- Event* evt = g_handle_table.Get<Event>(handle);
+ Event* evt = g_handle_table.Get<Event>(handle).get();
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
// Resume threads waiting for event to signal
bool event_caught = false;
for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
- ResumeThreadFromWait( evt->waiting_threads[i]);
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(evt->waiting_threads[i]).get();
+ if (thread != nullptr)
+ thread->ResumeFromWait();
// If any thread is signalled awake by this event, assume the event was "caught" and reset
// the event. This will result in the next thread waiting on the event to block. Otherwise,
@@ -110,7 +112,7 @@ ResultCode SignalEvent(const Handle handle) {
* @return Result of operation, 0 on success, otherwise error code
*/
ResultCode ClearEvent(Handle handle) {
- Event* evt = g_handle_table.Get<Event>(handle);
+ Event* evt = g_handle_table.Get<Event>(handle).get();
if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!evt->permanent_locked) {
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index ae2c11a1..d3684896 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -6,13 +6,15 @@
#include "common/common.h"
+#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/timer.h"
namespace Kernel {
-Handle g_main_thread = 0;
+SharedPtr<Thread> g_main_thread = nullptr;
HandleTable g_handle_table;
u64 g_program_id = 0;
@@ -21,7 +23,7 @@ HandleTable::HandleTable() {
Clear();
}
-ResultVal<Handle> HandleTable::Create(Object* obj) {
+ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
_dbg_assert_(Kernel, obj != nullptr);
u16 slot = next_free_slot;
@@ -37,22 +39,23 @@ ResultVal<Handle> HandleTable::Create(Object* obj) {
// CTR-OS doesn't use generation 0, so skip straight to 1.
if (next_generation >= (1 << 15)) next_generation = 1;
+ Handle handle = generation | (slot << 15);
+ if (obj->handle == INVALID_HANDLE)
+ obj->handle = handle;
+
generations[slot] = generation;
- intrusive_ptr_add_ref(obj);
- objects[slot] = obj;
+ objects[slot] = std::move(obj);
- Handle handle = generation | (slot << 15);
- obj->handle = handle;
return MakeResult<Handle>(handle);
}
ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
- Object* object = GetGeneric(handle);
+ SharedPtr<Object> object = GetGeneric(handle);
if (object == nullptr) {
LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
return ERR_INVALID_HANDLE;
}
- return Create(object);
+ return Create(std::move(object));
}
ResultCode HandleTable::Close(Handle handle) {
@@ -62,7 +65,6 @@ ResultCode HandleTable::Close(Handle handle) {
size_t slot = GetSlot(handle);
u16 generation = GetGeneration(handle);
- intrusive_ptr_release(objects[slot]);
objects[slot] = nullptr;
generations[generation] = next_free_slot;
@@ -77,10 +79,9 @@ bool HandleTable::IsValid(Handle handle) const {
return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
}
-Object* HandleTable::GetGeneric(Handle handle) const {
+SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
- // TODO(yuriks) Directly return the pointer once this is possible.
- handle = GetCurrentThreadHandle();
+ return GetCurrentThread();
} else if (handle == CurrentProcess) {
LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess);
return nullptr;
@@ -95,8 +96,6 @@ Object* HandleTable::GetGeneric(Handle handle) const {
void HandleTable::Clear() {
for (size_t i = 0; i < MAX_COUNT; ++i) {
generations[i] = i + 1;
- if (objects[i] != nullptr)
- intrusive_ptr_release(objects[i]);
objects[i] = nullptr;
}
next_free_slot = 0;
@@ -105,12 +104,13 @@ void HandleTable::Clear() {
/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
+ Kernel::TimersInit();
}
/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
-
+ Kernel::TimersShutdown();
g_handle_table.Clear(); // Free all kernel objects
}
@@ -123,7 +123,7 @@ bool LoadExec(u32 entry_point) {
Core::g_app_core->SetPC(entry_point);
// 0x30 is the typical main thread priority I've seen used so far
- g_main_thread = Kernel::SetupMainThread(0x30);
+ g_main_thread = Kernel::SetupMainThread(0x30, Kernel::DEFAULT_STACK_SIZE);
// Setup the idle thread
Kernel::SetupIdleThread();
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7f86fd07..5e5217b7 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -4,6 +4,8 @@
#pragma once
+#include <boost/intrusive_ptr.hpp>
+
#include <array>
#include <string>
#include "common/common.h"
@@ -16,6 +18,8 @@ const Handle INVALID_HANDLE = 0;
namespace Kernel {
+class Thread;
+
// TODO: Verify code
const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
ErrorSummary::OutOfResource, ErrorLevel::Temporary);
@@ -39,6 +43,7 @@ enum class HandleType : u32 {
Process = 8,
AddressArbiter = 9,
Semaphore = 10,
+ Timer = 11
};
enum {
@@ -49,7 +54,7 @@ class HandleTable;
class Object : NonCopyable {
friend class HandleTable;
- u32 handle;
+ u32 handle = INVALID_HANDLE;
public:
virtual ~Object() {}
Handle GetHandle() const { return handle; }
@@ -73,7 +78,7 @@ private:
unsigned int ref_count = 0;
};
-// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting
+// Special functions used by boost::instrusive_ptr to do automatic ref-counting
inline void intrusive_ptr_add_ref(Object* object) {
++object->ref_count;
}
@@ -84,6 +89,9 @@ inline void intrusive_ptr_release(Object* object) {
}
}
+template <typename T>
+using SharedPtr = boost::intrusive_ptr<T>;
+
/**
* This class allows the creation of Handles, which are references to objects that can be tested
* for validity and looked up. Here they are used to pass references to kernel objects to/from the
@@ -116,7 +124,7 @@ public:
* @return The created Handle or one of the following errors:
* - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
*/
- ResultVal<Handle> Create(Object* obj);
+ ResultVal<Handle> Create(SharedPtr<Object> obj);
/**
* Returns a new handle that points to the same object as the passed in handle.
@@ -140,7 +148,7 @@ public:
* Looks up a handle.
* @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid.
*/
- Object* GetGeneric(Handle handle) const;
+ SharedPtr<Object> GetGeneric(Handle handle) const;
/**
* Looks up a handle while verifying its type.
@@ -148,10 +156,10 @@ public:
* type differs from the handle type `T::HANDLE_TYPE`.
*/
template <class T>
- T* Get(Handle handle) const {
- Object* object = GetGeneric(handle);
+ SharedPtr<T> Get(Handle handle) const {
+ SharedPtr<Object> object = GetGeneric(handle);
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
- return static_cast<T*>(object);
+ return boost::static_pointer_cast<T>(std::move(object));
}
return nullptr;
}
@@ -170,7 +178,7 @@ private:
static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; }
/// Stores the Object referenced by the handle or null if the slot is empty.
- std::array<Object*, MAX_COUNT> objects;
+ std::array<SharedPtr<Object>, MAX_COUNT> objects;
/**
* The value of `next_generation` when the handle was created, used to check for validity. For
@@ -189,7 +197,7 @@ private:
};
extern HandleTable g_handle_table;
-extern Handle g_main_thread;
+extern SharedPtr<Thread> g_main_thread;
/// The ID code of the currently running game
/// TODO(Subv): This variable should not be here,
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 3dfeffc9..853a5dd7 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -40,14 +40,21 @@ static MutexMap g_mutex_held_locks;
* @param mutex Mutex that is to be acquired
* @param thread Thread that will acquired
*/
-void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) {
+void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) {
g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
mutex->lock_thread = thread;
}
-bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
- MutexAcquireLock(mutex, thread);
- Kernel::ResumeThreadFromWait(thread);
+bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) {
+ MutexAcquireLock(mutex, thread_handle);
+
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(thread_handle).get();
+ if (thread == nullptr) {
+ LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle);
+ return false;
+ }
+
+ thread->ResumeFromWait();
return true;
}
@@ -87,7 +94,7 @@ void ReleaseThreadMutexes(Handle thread) {
// Release every mutex that the thread holds, and resume execution on the waiting threads
for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
- Mutex* mutex = g_handle_table.Get<Mutex>(iter->second);
+ Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get();
ResumeWaitingThread(mutex);
}
@@ -115,7 +122,7 @@ bool ReleaseMutex(Mutex* mutex) {
* @param handle Handle to mutex to release
*/
ResultCode ReleaseMutex(Handle handle) {
- Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle);
+ Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle).get();
if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (!ReleaseMutex(mutex)) {
@@ -168,8 +175,8 @@ Handle CreateMutex(bool initial_locked, const std::string& name) {
ResultVal<bool> Mutex::WaitSynchronization() {
bool wait = locked;
if (locked) {
- waiting_threads.push_back(GetCurrentThreadHandle());
- Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle());
+ waiting_threads.push_back(GetCurrentThread()->GetHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this);
} else {
// Lock the mutex when the first thread accesses it
locked = true;
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index 6bc8066a..88ec9a10 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -37,8 +37,8 @@ public:
bool wait = !IsAvailable();
if (wait) {
- Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle());
- waiting_threads.push(GetCurrentThreadHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_SEMA, this);
+ waiting_threads.push(GetCurrentThread()->GetHandle());
} else {
--available_count;
}
@@ -70,7 +70,7 @@ ResultCode CreateSemaphore(Handle* handle, s32 initial_count,
}
ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
- Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle);
+ Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle).get();
if (semaphore == nullptr)
return InvalidHandle(ErrorModule::Kernel);
@@ -84,7 +84,9 @@ ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) {
// Notify some of the threads that the semaphore has been released
// stop once the semaphore is full again or there are no more waiting threads
while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) {
- Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front());
+ Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get();
+ if (thread != nullptr)
+ thread->ResumeFromWait();
semaphore->waiting_threads.pop();
--semaphore->available_count;
}
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index cea1f6fa..5368e472 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -61,7 +61,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
- SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
+ SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get();
if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
shared_memory->base_address = address;
@@ -72,7 +72,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions
}
ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) {
- SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
+ SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get();
if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel);
if (0 != shared_memory->base_address)
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 58fb62e8..bc86a7c5 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -10,6 +10,7 @@
#include "common/common.h"
#include "common/thread_queue_list.h"
+#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/hle.h"
@@ -21,68 +22,25 @@
namespace Kernel {
-class Thread : public Kernel::Object {
-public:
-
- std::string GetName() const override { return name; }
- std::string GetTypeName() const override { return "Thread"; }
-
- static const HandleType HANDLE_TYPE = HandleType::Thread;
- HandleType GetHandleType() const override { return HANDLE_TYPE; }
-
- inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
- inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
- inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
- inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
- inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
- inline bool IsIdle() const { return idle; }
-
- ResultVal<bool> WaitSynchronization() override {
- const bool wait = status != THREADSTATUS_DORMANT;
- if (wait) {
- Handle thread = GetCurrentThreadHandle();
- if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
- waiting_threads.push_back(thread);
- }
- WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
+ResultVal<bool> Thread::WaitSynchronization() {
+ const bool wait = status != THREADSTATUS_DORMANT;
+ if (wait) {
+ Thread* thread = GetCurrentThread();
+ if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
+ waiting_threads.push_back(thread);
}
-
- return MakeResult<bool>(wait);
+ WaitCurrentThread(WAITTYPE_THREADEND, this);
}
- ThreadContext context;
-
- u32 thread_id;
-
- u32 status;
- u32 entry_point;
- u32 stack_top;
- u32 stack_size;
-
- s32 initial_priority;
- s32 current_priority;
-
- s32 processor_id;
-
- WaitType wait_type;
- Handle wait_handle;
- VAddr wait_address;
-
- std::vector<Handle> waiting_threads;
-
- std::string name;
-
- /// Whether this thread is intended to never actually be executed, i.e. always idle
- bool idle = false;
-};
+ return MakeResult<bool>(wait);
+}
// Lists all thread ids that aren't deleted/etc.
-static std::vector<Handle> thread_queue;
+static std::vector<SharedPtr<Thread>> thread_list;
// Lists only ready thread ids.
-static Common::ThreadQueueList<Handle, THREADPRIO_LOWEST+1> thread_ready_queue;
+static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue;
-static Handle current_thread_handle;
static Thread* current_thread;
static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup
@@ -92,30 +50,9 @@ Thread* GetCurrentThread() {
return current_thread;
}
-/// Gets the current thread handle
-Handle GetCurrentThreadHandle() {
- return GetCurrentThread()->GetHandle();
-}
-
-/// Sets the current thread
-inline void SetCurrentThread(Thread* t) {
- current_thread = t;
- current_thread_handle = t->GetHandle();
-}
-
-/// Saves the current CPU context
-void SaveContext(ThreadContext& ctx) {
- Core::g_app_core->SaveContext(ctx);
-}
-
-/// Loads a CPU context
-void LoadContext(ThreadContext& ctx) {
- Core::g_app_core->LoadContext(ctx);
-}
-
/// Resets a thread
-void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
- memset(&t->context, 0, sizeof(ThreadContext));
+static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
+ memset(&t->context, 0, sizeof(Core::ThreadContext));
t->context.cpu_registers[0] = arg;
t->context.pc = t->context.reg_15 = t->entry_point;
@@ -131,22 +68,21 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
t->current_priority = t->initial_priority;
}
t->wait_type = WAITTYPE_NONE;
- t->wait_handle = 0;
+ t->wait_object = nullptr;
t->wait_address = 0;
}
/// Change a thread to "ready" state
-void ChangeReadyState(Thread* t, bool ready) {
- Handle handle = t->GetHandle();
+static void ChangeReadyState(Thread* t, bool ready) {
if (t->IsReady()) {
if (!ready) {
- thread_ready_queue.remove(t->current_priority, handle);
+ thread_ready_queue.remove(t->current_priority, t);
}
} else if (ready) {
if (t->IsRunning()) {
- thread_ready_queue.push_front(t->current_priority, handle);
+ thread_ready_queue.push_front(t->current_priority, t);
} else {
- thread_ready_queue.push_back(t->current_priority, handle);
+ thread_ready_queue.push_back(t->current_priority, t);
}
t->status = THREADSTATUS_READY;
}
@@ -158,43 +94,36 @@ static bool CheckWaitType(const Thread* thread, WaitType type) {
}
/// Check if a thread is blocking on a specified wait type with a specified handle
-static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) {
- return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle);
+static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) {
+ return CheckWaitType(thread, type) && wait_object == thread->wait_object;
}
/// Check if a thread is blocking on a specified wait type with a specified handle and address
-static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
- return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address);
+static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) {
+ return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address);
}
/// Stops the current thread
-ResultCode StopThread(Handle handle, const char* reason) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
- if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
-
+void Thread::Stop(const char* reason) {
// Release all the mutexes that this thread holds
- ReleaseThreadMutexes(handle);
-
- ChangeReadyState(thread, false);
- thread->status = THREADSTATUS_DORMANT;
- for (Handle waiting_handle : thread->waiting_threads) {
- Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle);
+ ReleaseThreadMutexes(GetHandle());
- if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle))
- ResumeThreadFromWait(waiting_handle);
+ ChangeReadyState(this, false);
+ status = THREADSTATUS_DORMANT;
+ for (auto& waiting_thread : waiting_threads) {
+ if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this))
+ waiting_thread->ResumeFromWait();
}
- thread->waiting_threads.clear();
+ waiting_threads.clear();
// Stopped threads are never waiting.
- thread->wait_type = WAITTYPE_NONE;
- thread->wait_handle = 0;
- thread->wait_address = 0;
-
- return RESULT_SUCCESS;
+ wait_type = WAITTYPE_NONE;
+ wait_object = nullptr;
+ wait_address = 0;
}
/// Changes a threads state
-void ChangeThreadState(Thread* t, ThreadStatus new_status) {
+static void ChangeThreadState(Thread* t, ThreadStatus new_status) {
if (!t || t->status == new_status) {
return;
}
@@ -209,46 +138,44 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
}
/// Arbitrate the highest priority thread that is waiting
-Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
- Handle highest_priority_thread = 0;
+Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) {
+ Thread* highest_priority_thread = nullptr;
s32 priority = THREADPRIO_LOWEST;
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (Handle handle : thread_queue) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
-
- if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
+ for (auto& thread : thread_list) {
+ if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address))
continue;
if (thread == nullptr)
continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up.
if(thread->current_priority <= priority) {
- highest_priority_thread = handle;
+ highest_priority_thread = thread.get();
priority = thread->current_priority;
}
}
+
// If a thread was arbitrated, resume it
- if (0 != highest_priority_thread)
- ResumeThreadFromWait(highest_priority_thread);
+ if (nullptr != highest_priority_thread) {
+ highest_priority_thread->ResumeFromWait();
+ }
return highest_priority_thread;
}
/// Arbitrate all threads currently waiting
-void ArbitrateAllThreads(u32 arbiter, u32 address) {
+void ArbitrateAllThreads(Object* arbiter, u32 address) {
// Iterate through threads, find highest priority thread that is waiting to be arbitrated...
- for (Handle handle : thread_queue) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
-
- if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
- ResumeThreadFromWait(handle);
+ for (auto& thread : thread_list) {
+ if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address))
+ thread->ResumeFromWait();
}
}
/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
-void CallThread(Thread* t) {
+static void CallThread(Thread* t) {
// Stop waiting
if (t->wait_type != WAITTYPE_NONE) {
t->wait_type = WAITTYPE_NONE;
@@ -257,12 +184,12 @@ void CallThread(Thread* t) {
}
/// Switches CPU context to that of the specified thread
-void SwitchContext(Thread* t) {
+static void SwitchContext(Thread* t) {
Thread* cur = GetCurrentThread();
// Save context for current thread
if (cur) {
- SaveContext(cur->context);
+ Core::g_app_core->SaveContext(cur->context);
if (cur->IsRunning()) {
ChangeReadyState(cur, true);
@@ -270,19 +197,19 @@ void SwitchContext(Thread* t) {
}
// Load context of new thread
if (t) {
- SetCurrentThread(t);
+ current_thread = t;
ChangeReadyState(t, false);
t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
t->wait_type = WAITTYPE_NONE;
- LoadContext(t->context);
+ Core::g_app_core->LoadContext(t->context);
} else {
- SetCurrentThread(nullptr);
+ current_thread = nullptr;
}
}
/// Gets the next thread that is ready to be run by priority
-Thread* NextThread() {
- Handle next;
+static Thread* NextThread() {
+ Thread* next;
Thread* cur = GetCurrentThread();
if (cur && cur->IsRunning()) {
@@ -293,63 +220,111 @@ Thread* NextThread() {
if (next == 0) {
return nullptr;
}
- return Kernel::g_handle_table.Get<Thread>(next);
+ return next;
}
-void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
+void WaitCurrentThread(WaitType wait_type, Object* wait_object) {
Thread* thread = GetCurrentThread();
thread->wait_type = wait_type;
- thread->wait_handle = wait_handle;
+ thread->wait_object = wait_object;
ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
}
-void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) {
- WaitCurrentThread(wait_type, wait_handle);
+void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) {
+ WaitCurrentThread(wait_type, wait_object);
GetCurrentThread()->wait_address = wait_address;
}
+/// Event type for the thread wake up event
+static int ThreadWakeupEventType = -1;
+
+/// Callback that will wake up the thread it was scheduled for
+static void ThreadWakeupCallback(u64 parameter, int cycles_late) {
+ Handle handle = static_cast<Handle>(parameter);
+ SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle);
+ if (thread == nullptr) {
+ LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+ return;
+ }
+
+ thread->ResumeFromWait();
+}
+
+
+void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) {
+ // Don't schedule a wakeup if the thread wants to wait forever
+ if (nanoseconds == -1)
+ return;
+ _dbg_assert_(Kernel, thread != nullptr);
+
+ u64 microseconds = nanoseconds / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle());
+}
+
/// Resumes a thread from waiting by marking it as "ready"
-void ResumeThreadFromWait(Handle handle) {
- Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
- if (thread) {
- thread->status &= ~THREADSTATUS_WAIT;
- thread->wait_handle = 0;
- thread->wait_type = WAITTYPE_NONE;
- if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
- ChangeReadyState(thread, true);
- }
+void Thread::ResumeFromWait() {
+ // Cancel any outstanding wakeup events
+ CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle());
+
+ status &= ~THREADSTATUS_WAIT;
+ wait_object = nullptr;
+ wait_type = WAITTYPE_NONE;
+ if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
+ ChangeReadyState(this, true);
}
}
/// Prints the thread queue for debugging purposes
-void DebugThreadQueue() {
+static void DebugThreadQueue() {
Thread* thread = GetCurrentThread();
if (!thread) {
return;
}
- LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
- for (u32 i = 0; i < thread_queue.size(); i++) {
- Handle handle = thread_queue[i];
- s32 priority = thread_ready_queue.contains(handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle());
+ for (auto& t : thread_list) {
+ s32 priority = thread_ready_queue.contains(t.get());
if (priority != -1) {
- LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle);
+ LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle());
}
}
}
-/// Creates a new thread
-Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
- s32 processor_id, u32 stack_top, int stack_size) {
+ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
+ u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size) {
+ if (stack_size < 0x200) {
+ LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name.c_str(), stack_size);
+ // TODO: Verify error
+ return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ }
- _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
- "priority=%d, outside of allowable range!", priority)
+ if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
+ s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
+ LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
+ name.c_str(), priority, new_priority);
+ // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
+ // validity of this
+ priority = new_priority;
+ }
+
+ if (!Memory::GetPointer(entry_point)) {
+ LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
+ // TODO: Verify error
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ }
- Thread* thread = new Thread;
+ SharedPtr<Thread> thread(new Thread);
- // TOOD(yuriks): Fix error reporting
- handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE);
+ // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for
+ // the time being. Create a handle here, it will be copied to the handle field in
+ // the object and use by the rest of the code. This should be removed when other
+ // code doesn't rely on the handle anymore.
+ ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread);
+ if (handle.Failed())
+ return handle.Code();
- thread_queue.push_back(handle);
+ thread_list.push_back(thread);
thread_ready_queue.prepare(priority);
thread->thread_id = next_thread_id++;
@@ -360,69 +335,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
thread->initial_priority = thread->current_priority = priority;
thread->processor_id = processor_id;
thread->wait_type = WAITTYPE_NONE;
- thread->wait_handle = 0;
+ thread->wait_object = nullptr;
thread->wait_address = 0;
- thread->name = name;
-
- return thread;
-}
-
-/// Creates a new thread - wrapper for external user
-Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
- u32 stack_top, int stack_size) {
-
- if (name == nullptr) {
- LOG_ERROR(Kernel_SVC, "nullptr name");
- return -1;
- }
- if ((u32)stack_size < 0x200) {
- LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name,
- stack_size);
- return -1;
- }
- if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
- s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
- LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d",
- name, priority, new_priority);
- // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
- // validity of this
- priority = new_priority;
- }
- if (!Memory::GetPointer(entry_point)) {
- LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point);
- return -1;
- }
- Handle handle;
- Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
- stack_size);
+ thread->name = std::move(name);
- ResetThread(thread, arg, 0);
- CallThread(thread);
+ ResetThread(thread.get(), arg, 0);
+ CallThread(thread.get());
- return handle;
-}
-
-/// Get the priority of the thread specified by handle
-ResultVal<u32> GetThreadPriority(const Handle handle) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
- if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel);
-
- return MakeResult<u32>(thread->current_priority);
+ return MakeResult<SharedPtr<Thread>>(std::move(thread));
}
/// Set the priority of the thread specified by handle
-ResultCode SetThreadPriority(Handle handle, s32 priority) {
- Thread* thread = nullptr;
- if (!handle) {
- thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
- } else {
- thread = g_handle_table.Get<Thread>(handle);
- if (thread == nullptr) {
- return InvalidHandle(ErrorModule::Kernel);
- }
- }
- _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
-
+void Thread::SetPriority(s32 priority) {
// If priority is invalid, clamp to valid range
if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
@@ -433,38 +357,39 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) {
}
// Change thread priority
- s32 old = thread->current_priority;
- thread_ready_queue.remove(old, handle);
- thread->current_priority = priority;
- thread_ready_queue.prepare(thread->current_priority);
+ s32 old = current_priority;
+ thread_ready_queue.remove(old, this);
+ current_priority = priority;
+ thread_ready_queue.prepare(current_priority);
// Change thread status to "ready" and push to ready queue
- if (thread->IsRunning()) {
- thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
+ if (IsRunning()) {
+ status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
}
- if (thread->IsReady()) {
- thread_ready_queue.push_back(thread->current_priority, handle);
+ if (IsReady()) {
+ thread_ready_queue.push_back(current_priority, this);
}
-
- return RESULT_SUCCESS;
}
Handle SetupIdleThread() {
- Handle handle;
- Thread* thread = CreateThread(handle, "idle", 0, THREADPRIO_LOWEST, THREADPROCESSORID_0, 0, 0);
+ // We need to pass a few valid values to get around parameter checking in Thread::Create.
+ auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0,
+ THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE);
+ _dbg_assert_(Kernel, thread_res.Succeeded());
+ SharedPtr<Thread> thread = std::move(*thread_res);
+
thread->idle = true;
- CallThread(thread);
- return handle;
+ CallThread(thread.get());
+ return thread->GetHandle();
}
-Handle SetupMainThread(s32 priority, int stack_size) {
- Handle handle;
-
+SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) {
// Initialize new "main" thread
- Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
- THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
-
- ResetThread(thread, 0, 0);
+ auto thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0,
+ THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
+ // TODO(yuriks): Propagate error
+ _dbg_assert_(Kernel, thread_res.Succeeded());
+ SharedPtr<Thread> thread = std::move(*thread_res);
// If running another thread already, set it to "ready" state
Thread* cur = GetCurrentThread();
@@ -473,11 +398,11 @@ Handle SetupMainThread(s32 priority, int stack_size) {
}
// Run new "main" thread
- SetCurrentThread(thread);
+ current_thread = thread.get();
thread->status = THREADSTATUS_RUNNING;
- LoadContext(thread->context);
+ Core::g_app_core->LoadContext(thread->context);
- return handle;
+ return thread;
}
@@ -493,46 +418,19 @@ void Reschedule() {
} else {
LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
- for (Handle handle : thread_queue) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
+ for (auto& thread : thread_list) {
LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X",
- thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
+ thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type,
+ (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE));
}
}
-
- // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
- // to sleep. So, we'll just immediately set it to "ready" again after an attempted context
- // switch has occurred. This results in the current thread yielding on a sleep once, and then it
- // will immediately be placed back in the queue for execution.
-
- if (CheckWaitType(prev, WAITTYPE_SLEEP))
- ResumeThreadFromWait(prev->GetHandle());
-}
-
-bool IsIdleThread(Handle handle) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
- if (!thread) {
- LOG_ERROR(Kernel, "Thread not found %u", handle);
- return false;
- }
- return thread->IsIdle();
-}
-
-ResultCode GetThreadId(u32* thread_id, Handle handle) {
- Thread* thread = g_handle_table.Get<Thread>(handle);
- if (thread == nullptr)
- return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS,
- ErrorSummary::WrongArgument, ErrorLevel::Permanent);
-
- *thread_id = thread->thread_id;
-
- return RESULT_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ThreadingInit() {
next_thread_id = INITIAL_THREAD_ID;
+ ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
}
void ThreadingShutdown() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index dfe92d16..284dec40 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -4,8 +4,12 @@
#pragma once
+#include <string>
+#include <vector>
+
#include "common/common_types.h"
+#include "core/core.h"
#include "core/mem_map.h"
#include "core/hle/kernel/kernel.h"
@@ -43,66 +47,107 @@ enum WaitType {
WAITTYPE_MUTEX,
WAITTYPE_SYNCH,
WAITTYPE_ARB,
+ WAITTYPE_TIMER,
};
namespace Kernel {
-/// Creates a new thread - wrapper for external user
-Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
- u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE);
+class Thread : public Kernel::Object {
+public:
+ static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority,
+ u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size);
-/// Sets up the primary application thread
-Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
+ std::string GetName() const override { return name; }
+ std::string GetTypeName() const override { return "Thread"; }
-/// Reschedules to the next available thread (call after current thread is suspended)
-void Reschedule();
+ static const HandleType HANDLE_TYPE = HandleType::Thread;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
-/// Stops the current thread
-ResultCode StopThread(Handle thread, const char* reason);
+ inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
+ inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
+ inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
+ inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
+ inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
+ inline bool IsIdle() const { return idle; }
-/**
- * Retrieves the ID of the specified thread handle
- * @param thread_id Will contain the output thread id
- * @param handle Handle to the thread we want
- * @return Whether the function was successful or not
- */
-ResultCode GetThreadId(u32* thread_id, Handle handle);
+ ResultVal<bool> WaitSynchronization() override;
+
+ s32 GetPriority() const { return current_priority; }
+ void SetPriority(s32 priority);
+
+ u32 GetThreadId() const { return thread_id; }
+
+ void Stop(const char* reason);
+ /// Resumes a thread from waiting by marking it as "ready".
+ void ResumeFromWait();
+
+ Core::ThreadContext context;
+
+ u32 thread_id;
+
+ u32 status;
+ u32 entry_point;
+ u32 stack_top;
+ u32 stack_size;
+
+ s32 initial_priority;
+ s32 current_priority;
+
+ s32 processor_id;
+
+ WaitType wait_type;
+ Object* wait_object;
+ VAddr wait_address;
-/// Resumes a thread from waiting by marking it as "ready"
-void ResumeThreadFromWait(Handle handle);
+ std::vector<SharedPtr<Thread>> waiting_threads;
+
+ std::string name;
+
+ /// Whether this thread is intended to never actually be executed, i.e. always idle
+ bool idle = false;
+
+private:
+ Thread() = default;
+};
+
+/// Sets up the primary application thread
+SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size);
+
+/// Reschedules to the next available thread (call after current thread is suspended)
+void Reschedule();
/// Arbitrate the highest priority thread that is waiting
-Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address);
+Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address);
/// Arbitrate all threads currently waiting...
-void ArbitrateAllThreads(u32 arbiter, u32 address);
+void ArbitrateAllThreads(Object* arbiter, u32 address);
-/// Gets the current thread handle
-Handle GetCurrentThreadHandle();
+/// Gets the current thread
+Thread* GetCurrentThread();
/**
* Puts the current thread in the wait state for the given type
* @param wait_type Type of wait
- * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
+ * @param wait_object Kernel object that we are waiting on, defaults to current thread
+ */
+void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread());
+
+/**
+ * Schedules an event to wake up the specified thread after the specified delay.
+ * @param handle The thread handle.
+ * @param nanoseconds The time this thread will be allowed to sleep for.
*/
-void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
+void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds);
/**
* Puts the current thread in the wait state for the given type
* @param wait_type Type of wait
- * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread
+ * @param wait_object Kernel object that we are waiting on
* @param wait_address Arbitration address used to resume from wait
*/
-void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address);
+void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address);
-/// Put current thread in a wait state - on WaitSynchronization
-void WaitThread_Synchronization();
-/// Get the priority of the thread specified by handle
-ResultVal<u32> GetThreadPriority(const Handle handle);
-
-/// Set the priority of the thread specified by handle
-ResultCode SetThreadPriority(Handle handle, s32 priority);
/**
* Sets up the idle thread, this is a thread that is intended to never execute instructions,
@@ -111,10 +156,6 @@ ResultCode SetThreadPriority(Handle handle, s32 priority);
* @returns The handle of the idle thread
*/
Handle SetupIdleThread();
-
-/// Whether the current thread is an idle thread
-bool IsIdleThread(Handle thread);
-
/// Initialize threading
void ThreadingInit();
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
new file mode 100644
index 00000000..3b0452d4
--- /dev/null
+++ b/src/core/hle/kernel/timer.cpp
@@ -0,0 +1,144 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <set>
+
+#include "common/common.h"
+
+#include "core/core_timing.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/timer.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Timer : public Object {
+public:
+ std::string GetTypeName() const override { return "Timer"; }
+ std::string GetName() const override { return name; }
+
+ static const HandleType HANDLE_TYPE = HandleType::Timer;
+ HandleType GetHandleType() const override { return HANDLE_TYPE; }
+
+ ResetType reset_type; ///< The ResetType of this timer
+
+ bool signaled; ///< Whether the timer has been signaled or not
+ std::set<Handle> waiting_threads; ///< Threads that are waiting for the timer
+ std::string name; ///< Name of timer (optional)
+
+ u64 initial_delay; ///< The delay until the timer fires for the first time
+ u64 interval_delay; ///< The delay until the timer fires after the first time
+
+ ResultVal<bool> WaitSynchronization() override {
+ bool wait = !signaled;
+ if (wait) {
+ waiting_threads.insert(GetCurrentThread()->GetHandle());
+ Kernel::WaitCurrentThread(WAITTYPE_TIMER, this);
+ }
+ return MakeResult<bool>(wait);
+ }
+};
+
+/**
+ * Creates a timer.
+ * @param handle Reference to handle for the newly created timer
+ * @param reset_type ResetType describing how to create timer
+ * @param name Optional name of timer
+ * @return Newly created Timer object
+ */
+Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) {
+ Timer* timer = new Timer;
+
+ handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE);
+
+ timer->reset_type = reset_type;
+ timer->signaled = false;
+ timer->name = name;
+ timer->initial_delay = 0;
+ timer->interval_delay = 0;
+ return timer;
+}
+
+ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) {
+ CreateTimer(*handle, reset_type, name);
+ return RESULT_SUCCESS;
+}
+
+ResultCode ClearTimer(Handle handle) {
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ timer->signaled = false;
+ return RESULT_SUCCESS;
+}
+
+/// The event type of the generic timer callback event
+static int TimerCallbackEventType = -1;
+
+/// The timer callback event, called when a timer is fired
+static void TimerCallback(u64 timer_handle, int cycles_late) {
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(timer_handle);
+
+ if (timer == nullptr) {
+ LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle);
+ return;
+ }
+
+ LOG_TRACE(Kernel, "Timer %u fired", timer_handle);
+
+ timer->signaled = true;
+
+ // Resume all waiting threads
+ for (Handle thread_handle : timer->waiting_threads) {
+ if (SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(thread_handle))
+ thread->ResumeFromWait();
+ }
+
+ timer->waiting_threads.clear();
+
+ 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;
+ CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late,
+ TimerCallbackEventType, timer_handle);
+ }
+}
+
+ResultCode SetTimer(Handle handle, s64 initial, s64 interval) {
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ timer->initial_delay = initial;
+ timer->interval_delay = interval;
+
+ u64 initial_microseconds = initial / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle);
+ return RESULT_SUCCESS;
+}
+
+ResultCode CancelTimer(Handle handle) {
+ SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
+
+ if (timer == nullptr)
+ return InvalidHandle(ErrorModule::Kernel);
+
+ CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle);
+ return RESULT_SUCCESS;
+}
+
+void TimersInit() {
+ TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback);
+}
+
+void TimersShutdown() {
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
new file mode 100644
index 00000000..f8aa66b6
--- /dev/null
+++ b/src/core/hle/kernel/timer.h
@@ -0,0 +1,47 @@
+// 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 "core/hle/kernel/kernel.h"
+#include "core/hle/svc.h"
+
+namespace Kernel {
+
+/**
+ * Cancels a timer
+ * @param handle Handle of the timer to cancel
+ */
+ResultCode CancelTimer(Handle handle);
+
+/**
+ * Starts a timer with the specified initial delay and interval
+ * @param handle Handle of the timer to start
+ * @param initial Delay until the timer is first fired
+ * @param interval Delay until the timer is fired after the first time
+ */
+ResultCode SetTimer(Handle handle, s64 initial, s64 interval);
+
+/**
+ * Clears a timer
+ * @param handle Handle of the timer to clear
+ */
+ResultCode ClearTimer(Handle handle);
+
+/**
+ * Creates a timer
+ * @param Handle to newly created Timer object
+ * @param reset_type ResetType describing how to create the timer
+ * @param name Optional name of timer
+ * @return ResultCode of the error
+ */
+ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown");
+
+/// Initializes the required variables for timers
+void TimersInit();
+/// Tears down the timer variables
+void TimersShutdown();
+} // namespace
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 0e9c213e..82dcf5bb 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -369,14 +369,14 @@ private:
StorageType storage;
ResultCode result_code;
-#if _DEBUG
+#ifdef _DEBUG
// The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the
// need to cast `storage` to a pointer or pay attention to `result_code`.
const T* debug_ptr;
#endif
void UpdateDebugPtr() {
-#if _DEBUG
+#ifdef _DEBUG
debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
#endif
}
diff --git a/src/core/hle/service/apt_s.cpp b/src/core/hle/service/apt_s.cpp
new file mode 100644
index 00000000..f4599e19
--- /dev/null
+++ b/src/core/hle/service/apt_s.cpp
@@ -0,0 +1,121 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+
+#include "common/common.h"
+#include "common/file_util.h"
+
+#include "core/hle/hle.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/mutex.h"
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/service/apt_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace APT_S
+
+namespace APT_U {
+ extern void GetLockHandle(Service::Interface* self);
+ extern void Initialize(Service::Interface* self);
+ extern void Enable(Service::Interface* self);
+ extern void InquireNotification(Service::Interface* self);
+ extern void GetSharedFont(Service::Interface* self);
+ extern void AppletUtility(Service::Interface* self);
+ extern void GlanceParameter(Service::Interface* self);
+ extern void ReceiveParameter(Service::Interface* self);
+}
+
+namespace APT_S {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, APT_U::GetLockHandle, "GetLockHandle"},
+ {0x00020080, APT_U::Initialize, "Initialize"},
+ {0x00030040, APT_U::Enable, "Enable"},
+ {0x00040040, nullptr, "Finalize"},
+ {0x00050040, nullptr, "GetAppletManInfo"},
+ {0x00060040, nullptr, "GetAppletInfo"},
+ {0x00070000, nullptr, "GetLastSignaledAppletId"},
+ {0x00080000, nullptr, "CountRegisteredApplet"},
+ {0x00090040, nullptr, "IsRegistered"},
+ {0x000A0040, nullptr, "GetAttribute"},
+ {0x000B0040, APT_U::InquireNotification, "InquireNotification"},
+ {0x000C0104, nullptr, "SendParameter"},
+ {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter"},
+ {0x000E0080, APT_U::GlanceParameter, "GlanceParameter"},
+ {0x000F0100, nullptr, "CancelParameter"},
+ {0x001000C2, nullptr, "DebugFunc"},
+ {0x001100C0, nullptr, "MapProgramIdForDebug"},
+ {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"},
+ {0x00130000, nullptr, "GetPreparationState"},
+ {0x00140040, nullptr, "SetPreparationState"},
+ {0x00150140, nullptr, "PrepareToStartApplication"},
+ {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
+ {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00190040, nullptr, "PrepareToStartSystemApplet"},
+ {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
+ {0x001B00C4, nullptr, "StartApplication"},
+ {0x001C0000, nullptr, "WakeupApplication"},
+ {0x001D0000, nullptr, "CancelApplication"},
+ {0x001E0084, nullptr, "StartLibraryApplet"},
+ {0x001F0084, nullptr, "StartSystemApplet"},
+ {0x00200044, nullptr, "StartNewestHomeMenu"},
+ {0x00210000, nullptr, "OrderToCloseApplication"},
+ {0x00220040, nullptr, "PrepareToCloseApplication"},
+ {0x00230040, nullptr, "PrepareToJumpToApplication"},
+ {0x00240044, nullptr, "JumpToApplication"},
+ {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"},
+ {0x00260000, nullptr, "PrepareToCloseSystemApplet"},
+ {0x00270044, nullptr, "CloseApplication"},
+ {0x00280044, nullptr, "CloseLibraryApplet"},
+ {0x00290044, nullptr, "CloseSystemApplet"},
+ {0x002A0000, nullptr, "OrderToCloseSystemApplet"},
+ {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"},
+ {0x002C0044, nullptr, "JumpToHomeMenu"},
+ {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"},
+ {0x002E0044, nullptr, "LeaveHomeMenu"},
+ {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"},
+ {0x00300044, nullptr, "LeaveResidentApplet"},
+ {0x00310100, nullptr, "PrepareToDoApplicationJump"},
+ {0x00320084, nullptr, "DoApplicationJump"},
+ {0x00330000, nullptr, "GetProgramIdOnApplicationJump"},
+ {0x00340084, nullptr, "SendDeliverArg"},
+ {0x00350080, nullptr, "ReceiveDeliverArg"},
+ {0x00360040, nullptr, "LoadSysMenuArg"},
+ {0x00370042, nullptr, "StoreSysMenuArg"},
+ {0x00380040, nullptr, "PreloadResidentApplet"},
+ {0x00390040, nullptr, "PrepareToStartResidentApplet"},
+ {0x003A0044, nullptr, "StartResidentApplet"},
+ {0x003B0040, nullptr, "CancelLibraryApplet"},
+ {0x003C0042, nullptr, "SendDspSleep"},
+ {0x003D0042, nullptr, "SendDspWakeUp"},
+ {0x003E0080, nullptr, "ReplySleepQuery"},
+ {0x003F0040, nullptr, "ReplySleepNotificationComplete"},
+ {0x00400042, nullptr, "SendCaptureBufferInfo"},
+ {0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
+ {0x00420080, nullptr, "SleepSystem"},
+ {0x00430040, nullptr, "NotifyToWait"},
+ {0x00440000, APT_U::GetSharedFont, "GetSharedFont"},
+ {0x00450040, nullptr, "GetWirelessRebootInfo"},
+ {0x00460104, nullptr, "Wrap"},
+ {0x00470104, nullptr, "Unwrap"},
+ {0x00480100, nullptr, "GetProgramInfo"},
+ {0x00490180, nullptr, "Reboot"},
+ {0x004A0040, nullptr, "GetCaptureInfo"},
+ {0x004B00C2, APT_U::AppletUtility, "AppletUtility"},
+ {0x004C0000, nullptr, "SetFatalErrDispMode"},
+ {0x004D0080, nullptr, "GetAppletProgramInfo"},
+ {0x004E0000, nullptr, "HardwareResetAsync"},
+ {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
+ {0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/apt_s.h b/src/core/hle/service/apt_s.h
new file mode 100644
index 00000000..f097c974
--- /dev/null
+++ b/src/core/hle/service/apt_s.h
@@ -0,0 +1,30 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace APT_S
+
+namespace APT_S {
+
+// Application and title launching service. These services handle signaling for home/power button as
+// well. Only one session for either APT service can be open at a time, normally processes close the
+// service handle immediately once finished using the service. The commands for APT:U and APT:S are
+// exactly the same, however certain commands are only accessible with APT:S(NS module will call
+// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services.
+
+/// Interface to "APT:S" service
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "APT:S";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp
index d8b261ba..69a7bcf9 100644
--- a/src/core/hle/service/apt_u.cpp
+++ b/src/core/hle/service/apt_u.cpp
@@ -10,7 +10,7 @@
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/shared_memory.h"
-#include "apt_u.h"
+#include "core/hle/service/apt_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace APT_U
@@ -25,10 +25,12 @@ namespace APT_U {
// correctly mapping it in Citra, however we still do not understand how the mapping is determined.
static const VAddr SHARED_FONT_VADDR = 0x18000000;
-// Handle to shared memory region designated to for shared system font
+/// Handle to shared memory region designated to for shared system font
static Handle shared_font_mem = 0;
static Handle lock_handle = 0;
+static Handle notification_event_handle = 0; ///< APT notification event handle
+static Handle pause_event_handle = 0; ///< APT pause event handle
static std::vector<u8> shared_font;
/// Signals used by APT functions
@@ -42,18 +44,28 @@ enum class SignalType : u32 {
void Initialize(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle
- cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle
+ notification_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Notification");
+ pause_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause");
- Kernel::SetEventLocked(cmd_buff[3], true);
- Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event
+ cmd_buff[3] = notification_event_handle;
+ cmd_buff[4] = pause_event_handle;
+
+ Kernel::SetEventLocked(notification_event_handle, true);
+ Kernel::SetEventLocked(pause_event_handle, false); // Fire start event
_assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock");
Kernel::ReleaseMutex(lock_handle);
cmd_buff[1] = 0; // No error
+}
- LOG_DEBUG(Service_APT, "called");
+void NotifyToWait(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ u32 app_id = cmd_buff[1];
+ // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further.
+ Kernel::SignalEvent(pause_event_handle);
+ LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id);
+ cmd_buff[1] = 0;
}
void GetLockHandle(Service::Interface* self) {
@@ -86,7 +98,7 @@ void Enable(Service::Interface* self) {
void InquireNotification(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 app_id = cmd_buff[2];
+ u32 app_id = cmd_buff[1];
cmd_buff[1] = 0; // No error
cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
@@ -194,8 +206,6 @@ void AppletUtility(Service::Interface* self) {
* 4 : Handle to shared font memory
*/
void GetSharedFont(Service::Interface* self) {
- LOG_TRACE(Kernel_SVC, "called");
-
u32* cmd_buff = Kernel::GetCommandBuffer();
if (!shared_font.empty()) {
@@ -281,7 +291,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00400042, nullptr, "SendCaptureBufferInfo"},
{0x00410040, nullptr, "ReceiveCaptureBufferInfo"},
{0x00420080, nullptr, "SleepSystem"},
- {0x00430040, nullptr, "NotifyToWait"},
+ {0x00430040, NotifyToWait, "NotifyToWait"},
{0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
{0x00460104, nullptr, "Wrap"},
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp
new file mode 100644
index 00000000..cf4e8215
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg_s.cpp
@@ -0,0 +1,98 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/cfg/cfg.h"
+#include "core/hle/service/cfg/cfg_s.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_S
+
+namespace CFG_S {
+
+/**
+ * CFG_S::GetConfigInfoBlk2 service function
+ * Inputs:
+ * 0 : 0x00010082
+ * 1 : Size
+ * 2 : Block ID
+ * 3 : Descriptor for the output buffer
+ * 4 : Output buffer pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void GetConfigInfoBlk2(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 size = cmd_buffer[1];
+ u32 block_id = cmd_buffer[2];
+ u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
+
+ if (data_pointer == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw;
+}
+
+/**
+ * CFG_S::GetConfigInfoBlk8 service function
+ * Inputs:
+ * 0 : 0x04010082
+ * 1 : Size
+ * 2 : Block ID
+ * 3 : Descriptor for the output buffer
+ * 4 : Output buffer pointer
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void GetConfigInfoBlk8(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ u32 size = cmd_buffer[1];
+ u32 block_id = cmd_buffer[2];
+ u8* data_pointer = Memory::GetPointer(cmd_buffer[4]);
+
+ if (data_pointer == nullptr) {
+ cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
+ return;
+ }
+
+ cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw;
+}
+
+/**
+ * CFG_S::UpdateConfigNANDSavegame service function
+ * Inputs:
+ * 0 : 0x04030000
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+static void UpdateConfigNANDSavegame(Service::Interface* self) {
+ u32* cmd_buffer = Kernel::GetCommandBuffer();
+ cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw;
+}
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"},
+ {0x00020000, nullptr, "SecureInfoGetRegion"},
+ {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"},
+ {0x04020082, nullptr, "SetConfigInfoBlk4"},
+ {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"},
+ {0x04040042, nullptr, "GetLocalFriendCodeSeedData"},
+ {0x04050000, nullptr, "GetLocalFriendCodeSeed"},
+ {0x04060000, nullptr, "SecureInfoGetRegion"},
+ {0x04070000, nullptr, "SecureInfoGetByte101"},
+ {0x04080042, nullptr, "SecureInfoGetSerialNo"},
+ {0x04090000, nullptr, "UpdateConfigBlk00040003"},
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h
new file mode 100644
index 00000000..d8b67137
--- /dev/null
+++ b/src/core/hle/service/cfg/cfg_s.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace CFG_S
+
+namespace CFG_S {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "cfg:s";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index d4affdfb..d5e39ea4 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -23,11 +23,8 @@ void SignalInterrupt() {
// that check the DSP interrupt signal event to run. We should figure out the different types of
// DSP interrupts, and trigger them at the appropriate times.
- if (interrupt_event == 0) {
- LOG_WARNING(Service_DSP, "cannot signal interrupt until DSP event has been created!");
- return;
- }
- Kernel::SignalEvent(interrupt_event);
+ if (interrupt_event != 0)
+ Kernel::SignalEvent(interrupt_event);
}
/**
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index 7eb32146..56f3117f 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -27,8 +27,6 @@ static void Initialize(Service::Interface* self) {
// TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per
// http://3dbrew.org/wiki/FS:Initialize#Request
cmd_buff[1] = RESULT_SUCCESS.raw;
-
- LOG_DEBUG(Service_FS, "called");
}
/**
@@ -104,8 +102,8 @@ static void OpenFileDirectly(Service::Interface* self) {
FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
FileSys::Path file_path(filename_type, filename_size, filename_ptr);
- LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d",
- archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes);
+ LOG_DEBUG(Service_FS, "archive_id=0x%08X archive_path=%s file_path=%s, mode=%u attributes=%d",
+ archive_id, archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes);
ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path);
if (archive_handle.Failed()) {
@@ -367,7 +365,7 @@ static void OpenArchive(Service::Interface* self) {
u32 archivename_ptr = cmd_buff[5];
FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr);
- LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str());
+ LOG_DEBUG(Service_FS, "archive_id=0x%08X archive_path=%s", archive_id, archive_path.DebugStr().c_str());
ResultVal<ArchiveHandle> handle = OpenArchive(archive_id, archive_path);
cmd_buff[1] = handle.Code().raw;
@@ -408,8 +406,6 @@ static void IsSdmcDetected(Service::Interface* self) {
cmd_buff[1] = 0;
cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0;
-
- LOG_DEBUG(Service_FS, "called");
}
/**
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 26a43217..4ca2b9bd 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -210,14 +210,27 @@ void SignalInterrupt(InterruptId interrupt_id) {
}
for (int thread_id = 0; thread_id < 0x4; ++thread_id) {
InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(thread_id);
- interrupt_relay_queue->number_interrupts = interrupt_relay_queue->number_interrupts + 1;
-
u8 next = interrupt_relay_queue->index;
next += interrupt_relay_queue->number_interrupts;
next = next % 0x34; // 0x34 is the number of interrupt slots
+ interrupt_relay_queue->number_interrupts += 1;
+
interrupt_relay_queue->slot[next] = interrupt_id;
interrupt_relay_queue->error_code = 0x0; // No error
+
+ // Update framebuffer information if requested
+ // TODO(yuriks): Confirm where this code should be called. It is definitely updated without
+ // executing any GSP commands, only waiting on the event.
+ for (int screen_id = 0; screen_id < 2; ++screen_id) {
+ FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
+
+ if (info->is_dirty) {
+ SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
+ }
+
+ info->is_dirty = false;
+ }
}
Kernel::SignalEvent(g_interrupt_event);
}
@@ -269,8 +282,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2);
WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2);
-
- SignalInterrupt(InterruptId::PSC0);
break;
}
@@ -283,22 +294,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags);
WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1);
-
- // TODO(bunnei): Determine if these interrupts should be signalled here.
- SignalInterrupt(InterruptId::PSC1);
- SignalInterrupt(InterruptId::PPF);
-
- // Update framebuffer information if requested
- for (int screen_id = 0; screen_id < 2; ++screen_id) {
- FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
-
- if (info->is_dirty) {
- SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
- info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1;
- }
-
- info->is_dirty = false;
- }
break;
}
@@ -331,9 +326,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
/// This triggers handling of the GX command written to the command buffer in shared memory.
static void TriggerCmdReqQueue(Service::Interface* self) {
-
- LOG_TRACE(Service_GSP, "called");
-
// Iterate through each thread's command queue...
for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) {
CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id);
diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h
index 932b6170..65abb194 100644
--- a/src/core/hle/service/gsp_gpu.h
+++ b/src/core/hle/service/gsp_gpu.h
@@ -45,21 +45,16 @@ enum class CommandId : u32 {
/// GSP thread interrupt relay queue
struct InterruptRelayQueue {
- union {
- u32 hex;
-
- // Index of last interrupt in the queue
- BitField<0,8,u32> index;
-
- // Number of interrupts remaining to be processed by the userland code
- BitField<8,8,u32> number_interrupts;
-
- // Error code - zero on success, otherwise an error has occurred
- BitField<16,8,u32> error_code;
- };
-
- u32 unk0;
- u32 unk1;
+ // Index of last interrupt in the queue
+ u8 index;
+ // Number of interrupts remaining to be processed by the userland code
+ u8 number_interrupts;
+ // Error code - zero on success, otherwise an error has occurred
+ u8 error_code;
+ u8 padding1;
+
+ u32 missed_PDC0;
+ u32 missed_PDC1;
InterruptId slot[0x34]; ///< Interrupt ID slots
};
diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp
index 99b0ea5a..1403b1de 100644
--- a/src/core/hle/service/hid_user.cpp
+++ b/src/core/hle/service/hid_user.cpp
@@ -4,6 +4,7 @@
#include "common/log.h"
+#include "core/arm/arm_interface.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
@@ -162,8 +163,6 @@ static void GetIPCHandles(Service::Interface* self) {
cmd_buff[6] = event_accelerometer;
cmd_buff[7] = event_gyroscope;
cmd_buff[8] = event_debug_pad;
-
- LOG_TRACE(Service_HID, "called");
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hle/service/ptm_sysm.cpp b/src/core/hle/service/ptm_sysm.cpp
new file mode 100644
index 00000000..4b5f86a4
--- /dev/null
+++ b/src/core/hle/service/ptm_sysm.cpp
@@ -0,0 +1,56 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/log.h"
+#include "common/make_unique.h"
+#include "core/file_sys/archive_extsavedata.h"
+#include "core/hle/hle.h"
+#include "core/hle/service/ptm_sysm.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PTM_SYSM
+
+namespace PTM_SYSM {
+
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x040100C0, nullptr, "SetRtcAlarmEx"},
+ {0x04020042, nullptr, "ReplySleepQuery"},
+ {0x04030042, nullptr, "NotifySleepPreparationComplete"},
+ {0x04040102, nullptr, "SetWakeupTrigger"},
+ {0x04050000, nullptr, "GetAwakeReason"},
+ {0x04060000, nullptr, "RequestSleep"},
+ {0x040700C0, nullptr, "ShutdownAsync"},
+ {0x04080000, nullptr, "Awake"},
+ {0x04090080, nullptr, "RebootAsync"},
+ {0x040A0000, nullptr, "CheckNew3DS"},
+ {0x08010640, nullptr, "SetInfoLEDPattern"},
+ {0x08020040, nullptr, "SetInfoLEDPatternHeader"},
+ {0x08030000, nullptr, "GetInfoLEDStatus"},
+ {0x08040040, nullptr, "SetBatteryEmptyLEDPattern"},
+ {0x08050000, nullptr, "ClearStepHistory"},
+ {0x080600C2, nullptr, "SetStepHistory"},
+ {0x08070082, nullptr, "GetPlayHistory"},
+ {0x08080000, nullptr, "GetPlayHistoryStart"},
+ {0x08090000, nullptr, "GetPlayHistoryLength"},
+ {0x080A0000, nullptr, "ClearPlayHistory"},
+ {0x080B0080, nullptr, "CalcPlayHistoryStart"},
+ {0x080C0080, nullptr, "SetUserTime"},
+ {0x080D0000, nullptr, "InvalidateSystemTime"},
+ {0x080E0140, nullptr, "NotifyPlayEvent"},
+ {0x080F0000, nullptr, "IsLegacyPowerOff"},
+ {0x08100000, nullptr, "ClearLegacyPowerOff"},
+ {0x08110000, nullptr, "GetShellStatus"},
+ {0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
+ {0x08130000, nullptr, "FormatSavedata"},
+ {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"}
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Interface class
+
+Interface::Interface() {
+ Register(FunctionTable, ARRAY_SIZE(FunctionTable));
+}
+
+} // namespace
diff --git a/src/core/hle/service/ptm_sysm.h b/src/core/hle/service/ptm_sysm.h
new file mode 100644
index 00000000..0f267b21
--- /dev/null
+++ b/src/core/hle/service/ptm_sysm.h
@@ -0,0 +1,23 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// Namespace PTM_SYSM
+
+namespace PTM_SYSM {
+
+class Interface : public Service::Interface {
+public:
+ Interface();
+
+ std::string GetPortName() const override {
+ return "ptm:sysm";
+ }
+};
+
+} // namespace
diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp
index fd79cd8a..753180ad 100644
--- a/src/core/hle/service/ptm_u.cpp
+++ b/src/core/hle/service/ptm_u.cpp
@@ -76,8 +76,6 @@ static void GetShellState(Service::Interface* self) {
cmd_buff[1] = 0;
cmd_buff[2] = shell_open ? 1 : 0;
-
- LOG_TRACE(Service_PTM, "PTM_U::GetShellState called");
}
/**
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 0c559728..446ed516 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -11,10 +11,12 @@
#include "core/hle/service/am_app.h"
#include "core/hle/service/am_net.h"
#include "core/hle/service/apt_a.h"
+#include "core/hle/service/apt_s.h"
#include "core/hle/service/apt_u.h"
#include "core/hle/service/boss_u.h"
#include "core/hle/service/cecd_u.h"
#include "core/hle/service/cfg/cfg_i.h"
+#include "core/hle/service/cfg/cfg_s.h"
#include "core/hle/service/cfg/cfg_u.h"
#include "core/hle/service/csnd_snd.h"
#include "core/hle/service/dsp_dsp.h"
@@ -34,6 +36,7 @@
#include "core/hle/service/nwm_uds.h"
#include "core/hle/service/pm_app.h"
#include "core/hle/service/ptm_u.h"
+#include "core/hle/service/ptm_sysm.h"
#include "core/hle/service/soc_u.h"
#include "core/hle/service/srv.h"
#include "core/hle/service/ssl_c.h"
@@ -59,7 +62,8 @@ void Manager::DeleteService(const std::string& port_name) {
}
Interface* Manager::FetchFromHandle(Handle handle) {
- return Kernel::g_handle_table.Get<Interface>(handle);
+ // TODO(yuriks): This function is very suspicious and should probably be exterminated.
+ return Kernel::g_handle_table.Get<Interface>(handle).get();
}
Interface* Manager::FetchFromPortName(const std::string& port_name) {
@@ -84,10 +88,12 @@ void Init() {
g_manager->AddService(new AM_APP::Interface);
g_manager->AddService(new AM_NET::Interface);
g_manager->AddService(new APT_A::Interface);
+ g_manager->AddService(new APT_S::Interface);
g_manager->AddService(new APT_U::Interface);
g_manager->AddService(new BOSS_U::Interface);
g_manager->AddService(new CECD_U::Interface);
g_manager->AddService(new CFG_I::Interface);
+ g_manager->AddService(new CFG_S::Interface);
g_manager->AddService(new CFG_U::Interface);
g_manager->AddService(new CSND_SND::Interface);
g_manager->AddService(new DSP_DSP::Interface);
@@ -107,6 +113,7 @@ void Init() {
g_manager->AddService(new NWM_UDS::Interface);
g_manager->AddService(new PM_APP::Interface);
g_manager->AddService(new PTM_U::Interface);
+ g_manager->AddService(new PTM_SYSM::Interface);
g_manager->AddService(new SOC_U::Interface);
g_manager->AddService(new SSL_C::Interface);
g_manager->AddService(new Y2R_U::Interface);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 41ba1e55..e75d5008 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -33,6 +33,22 @@ class Interface : public Kernel::Session {
// processes.
friend class Manager;
+
+ /**
+ * Creates a function string for logging, complete with the name (or header code, depending
+ * on what's passed in) the port name, and all the cmd_buff arguments.
+ */
+ std::string MakeFunctionString(const std::string& name, const std::string& port_name, const u32* cmd_buff) {
+ // Number of params == bits 0-5 + bits 6-11
+ int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
+
+ std::string function_string = Common::StringFromFormat("function '%s': port=%s", name.c_str(), port_name.c_str());
+ for (int i = 1; i <= num_params; ++i) {
+ function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
+ }
+ return function_string;
+ }
+
public:
std::string GetName() const override { return GetPortName(); }
@@ -72,21 +88,14 @@ public:
auto itr = m_functions.find(cmd_buff[0]);
if (itr == m_functions.end() || itr->second.func == nullptr) {
- // Number of params == bits 0-5 + bits 6-11
- int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
-
- std::string error = "unknown/unimplemented function '%s': port=%s";
- for (int i = 1; i <= num_params; ++i) {
- error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]);
- }
-
- std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
-
- LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str());
+ std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name;
+ LOG_ERROR(Service, "%s %s", "unknown/unimplemented", MakeFunctionString(function_name, GetPortName(), cmd_buff).c_str());
// TODO(bunnei): Hack - ignore error
cmd_buff[1] = 0;
return MakeResult<bool>(false);
+ } else {
+ LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName(), cmd_buff).c_str());
}
itr->second.func(this);
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index f502c6af..bb8ee86b 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -7,6 +7,19 @@
#if EMU_PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#include <ws2tcpip.h>
+
+// MinGW does not define several errno constants
+#ifndef _MSC_VER
+#define EBADMSG 104
+#define ENODATA 120
+#define ENOMSG 122
+#define ENOSR 124
+#define ENOSTR 125
+#define ETIME 137
+#define EIDRM 2001
+#define ENOLINK 2002
+#endif // _MSC_VER
+
#else
#include <sys/socket.h>
#include <netinet/in.h>
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 912b52ad..ac5f30a2 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -14,16 +14,12 @@ namespace SRV {
static Handle g_event_handle = 0;
static void Initialize(Service::Interface* self) {
- LOG_DEBUG(Service_SRV, "called");
-
u32* cmd_buff = Kernel::GetCommandBuffer();
cmd_buff[1] = 0; // No error
}
static void GetProcSemaphore(Service::Interface* self) {
- LOG_TRACE(Service_SRV, "called");
-
u32* cmd_buff = Kernel::GetCommandBuffer();
// TODO(bunnei): Change to a semaphore once these have been implemented
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index f9e3619d..b3d873ef 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -12,6 +12,21 @@
namespace Y2R_U {
+/**
+ * Y2R_U::IsBusyConversion service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Whether the current conversion is of type busy conversion (?)
+ */
+static void IsBusyConversion(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;;
+ cmd_buff[2] = 0;
+
+ LOG_WARNING(Service, "(STUBBED) called");
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, nullptr, "SetInputFormat"},
{0x00030040, nullptr, "SetOutputFormat"},
@@ -29,7 +44,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00220040, nullptr, "SetAlpha"},
{0x00260000, nullptr, "StartConversion"},
{0x00270000, nullptr, "StopConversion"},
- {0x00280000, nullptr, "IsBusyConversion"},
+ {0x00280000, IsBusyConversion, "IsBusyConversion"},
{0x002A0000, nullptr, "PingProcess"},
{0x002B0000, nullptr, "DriverInitialize"},
{0x002C0000, nullptr, "DriverFinalize"}
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index c25409a9..a487f757 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -7,6 +7,7 @@
#include "common/string_util.h"
#include "common/symbols.h"
+#include "core/arm/arm_interface.h"
#include "core/mem_map.h"
#include "core/hle/kernel/address_arbiter.h"
@@ -15,6 +16,7 @@
#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/timer.h"
#include "core/hle/function_wrappers.h"
#include "core/hle/result.h"
@@ -23,6 +25,8 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace SVC
+using Kernel::SharedPtr;
+
namespace SVC {
enum ControlMemoryOperation {
@@ -92,7 +96,7 @@ static Result ConnectToPort(Handle* out, const char* port_name) {
/// Synchronize to an OS service
static Result SendSyncRequest(Handle handle) {
- Kernel::Session* session = Kernel::g_handle_table.Get<Kernel::Session>(handle);
+ SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle);
if (session == nullptr) {
return InvalidHandle(ErrorModule::Kernel).raw;
}
@@ -116,20 +120,19 @@ static Result CloseHandle(Handle handle) {
/// Wait for a handle to synchronize, timeout after the specified nanoseconds
static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
- // TODO(bunnei): Do something with nano_seconds, currently ignoring this
- bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
-
- Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handle);
+ SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handle);
if (object == nullptr)
return InvalidHandle(ErrorModule::Kernel).raw;
- LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(),
- object->GetName().c_str(), nano_seconds);
+ LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle,
+ object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds);
ResultVal<bool> wait = object->WaitSynchronization();
// Check for next thread to schedule
if (wait.Succeeded() && *wait) {
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds);
HLE::Reschedule(__func__);
}
@@ -139,6 +142,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all,
s64 nano_seconds) {
+
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
bool unlock_all = true;
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
@@ -148,12 +152,12 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
// Iterate through each handle, synchronize kernel object
for (s32 i = 0; i < handle_count; i++) {
- Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handles[i]);
+ SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]);
if (object == nullptr)
return InvalidHandle(ErrorModule::Kernel).raw;
- LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(),
- object->GetName().c_str());
+ LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i],
+ object->GetTypeName().c_str(), object->GetName().c_str());
// TODO(yuriks): Verify how the real function behaves when an error happens here
ResultVal<bool> wait_result = object->WaitSynchronization();
@@ -180,7 +184,6 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count,
/// Create an address arbiter (to allocate access to shared resources)
static Result CreateAddressArbiter(u32* arbiter) {
- LOG_TRACE(Kernel_SVC, "called");
Handle handle = Kernel::CreateAddressArbiter();
*arbiter = handle;
return 0;
@@ -191,7 +194,7 @@ static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value,
LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter,
address, type, value);
return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type),
- address, value).raw;
+ address, value, nanoseconds).raw;
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
@@ -220,6 +223,8 @@ static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit,
/// Creates a new thread
static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) {
+ using Kernel::Thread;
+
std::string name;
if (Symbols::HasSymbol(entry_point)) {
TSymbol symbol = Symbols::GetSymbol(entry_point);
@@ -228,41 +233,53 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top
name = Common::StringFromFormat("unknown-%08x", entry_point);
}
- Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id,
- stack_top);
+ ResultVal<SharedPtr<Thread>> thread_res = Kernel::Thread::Create(
+ name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE);
+ if (thread_res.Failed())
+ return thread_res.Code().raw;
+ SharedPtr<Thread> thread = std::move(*thread_res);
- Core::g_app_core->SetReg(1, thread);
+ // TODO(yuriks): Create new handle instead of using built-in
+ Core::g_app_core->SetReg(1, thread->GetHandle());
LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, "
"threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point,
- name.c_str(), arg, stack_top, priority, processor_id, thread);
+ name.c_str(), arg, stack_top, priority, processor_id, thread->GetHandle());
+
+ if (THREADPROCESSORID_1 == processor_id) {
+ LOG_WARNING(Kernel_SVC,
+ "thread designated for system CPU core (UNIMPLEMENTED) will be run with app core scheduling");
+ }
return 0;
}
/// Called when a thread exits
-static u32 ExitThread() {
- Handle thread = Kernel::GetCurrentThreadHandle();
-
- LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
+static void ExitThread() {
+ LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC());
- Kernel::StopThread(thread, __func__);
+ Kernel::GetCurrentThread()->Stop(__func__);
HLE::Reschedule(__func__);
- return 0;
}
/// Gets the priority for the specified thread
static Result GetThreadPriority(s32* priority, Handle handle) {
- ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle);
- if (priority_result.Succeeded()) {
- *priority = *priority_result;
- }
- return priority_result.Code().raw;
+ const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
+ if (thread == nullptr)
+ return InvalidHandle(ErrorModule::Kernel).raw;
+
+ *priority = thread->GetPriority();
+ return RESULT_SUCCESS.raw;
}
/// Sets the priority for the specified thread
static Result SetThreadPriority(Handle handle, s32 priority) {
- return Kernel::SetThreadPriority(handle, priority).raw;
+ SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
+ if (thread == nullptr)
+ return InvalidHandle(ErrorModule::Kernel).raw;
+
+ thread->SetPriority(priority);
+ return RESULT_SUCCESS.raw;
}
/// Create a mutex
@@ -283,8 +300,13 @@ static Result ReleaseMutex(Handle handle) {
/// Get the ID for the specified thread.
static Result GetThreadId(u32* thread_id, Handle handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle);
- ResultCode result = Kernel::GetThreadId(thread_id, handle);
- return result.raw;
+
+ const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle);
+ if (thread == nullptr)
+ return InvalidHandle(ErrorModule::Kernel).raw;
+
+ *thread_id = thread->GetThreadId();
+ return RESULT_SUCCESS.raw;
}
/// Creates a semaphore
@@ -338,12 +360,42 @@ static Result ClearEvent(Handle evt) {
return Kernel::ClearEvent(evt).raw;
}
+/// Creates a timer
+static Result CreateTimer(Handle* handle, u32 reset_type) {
+ ResultCode res = Kernel::CreateTimer(handle, static_cast<ResetType>(reset_type));
+ LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X",
+ reset_type, *handle);
+ return res.raw;
+}
+
+/// Clears a timer
+static Result ClearTimer(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::ClearTimer(handle).raw;
+}
+
+/// Starts a timer
+static Result SetTimer(Handle handle, s64 initial, s64 interval) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::SetTimer(handle, initial, interval).raw;
+}
+
+/// Cancels a timer
+static Result CancelTimer(Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ return Kernel::CancelTimer(handle).raw;
+}
+
/// Sleep the current thread
static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
// Sleep current thread and check for next thread to schedule
Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
+
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds);
+
HLE::Reschedule(__func__);
}
@@ -374,7 +426,7 @@ const HLE::FunctionDef SVC_Table[] = {
{0x06, nullptr, "GetProcessIdealProcessor"},
{0x07, nullptr, "SetProcessIdealProcessor"},
{0x08, HLE::Wrap<CreateThread>, "CreateThread"},
- {0x09, HLE::Wrap<ExitThread>, "ExitThread"},
+ {0x09, ExitThread, "ExitThread"},
{0x0A, HLE::Wrap<SleepThread>, "SleepThread"},
{0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"},
{0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"},
@@ -391,10 +443,10 @@ const HLE::FunctionDef SVC_Table[] = {
{0x17, HLE::Wrap<CreateEvent>, "CreateEvent"},
{0x18, HLE::Wrap<SignalEvent>, "SignalEvent"},
{0x19, HLE::Wrap<ClearEvent>, "ClearEvent"},
- {0x1A, nullptr, "CreateTimer"},
- {0x1B, nullptr, "SetTimer"},
- {0x1C, nullptr, "CancelTimer"},
- {0x1D, nullptr, "ClearTimer"},
+ {0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"},
+ {0x1B, HLE::Wrap<SetTimer>, "SetTimer"},
+ {0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"},
+ {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"},
{0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"},
{0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
{0x20, nullptr, "UnmapMemoryBlock"},
diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h
index ad780818..5d020a5b 100644
--- a/src/core/hle/svc.h
+++ b/src/core/hle/svc.h
@@ -20,21 +20,6 @@ struct PageInfo {
u32 flags;
};
-struct ThreadContext {
- u32 cpu_registers[13];
- u32 sp;
- u32 lr;
- u32 pc;
- u32 cpsr;
- u32 fpu_registers[32];
- u32 fpscr;
- u32 fpexc;
-
- // These are not part of native ThreadContext, but needed by emu
- u32 reg_15;
- u32 mode;
-};
-
enum ResetType {
RESETTYPE_ONESHOT,
RESETTYPE_STICKY,
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index e346e0ad..58eec300 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -4,9 +4,12 @@
#include "common/common_types.h"
+#include "core/arm/arm_interface.h"
+
#include "core/settings.h"
#include "core/core.h"
#include "core/mem_map.h"
+#include "core/core_timing.h"
#include "core/hle/hle.h"
#include "core/hle/service/gsp_gpu.h"
@@ -22,14 +25,17 @@ namespace GPU {
Regs g_regs;
-bool g_skip_frame = false; ///< True if the current frame was skipped
+/// True if the current frame was skipped
+bool g_skip_frame = false;
-static u64 frame_ticks = 0; ///< 268MHz / gpu_refresh_rate frames per second
-static u64 line_ticks = 0; ///< Number of ticks for a screen line
-static u32 cur_line = 0; ///< Current screen line
-static u64 last_update_tick = 0; ///< CPU ticl count from last GPU update
-static u64 frame_count = 0; ///< Number of frames drawn
-static bool last_skip_frame = false; ///< True if the last frame was skipped
+/// 268MHz / gpu_refresh_rate frames per second
+static u64 frame_ticks;
+/// Event id for CoreTiming
+static int vblank_event;
+/// Total number of frames drawn
+static u64 frame_count;
+/// True if the last frame was skipped
+static bool last_skip_frame = false;
template <typename T>
inline void Read(T &var, const u32 raw_addr) {
@@ -77,6 +83,12 @@ inline void Write(u32 addr, const T data) {
*ptr = bswap32(config.value); // TODO: This is just a workaround to missing framebuffer format emulation
LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress());
+
+ if (!is_second_filler) {
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC0);
+ } else {
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1);
+ }
}
break;
}
@@ -88,22 +100,25 @@ inline void Write(u32 addr, const T data) {
u8* source_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalInputAddress()));
u8* dest_pointer = Memory::GetPointer(Memory::PhysicalToVirtualAddress(config.GetPhysicalOutputAddress()));
+ // Cheap emulation of horizontal scaling: Just skip each second pixel of the
+ // input framebuffer. We keep track of this in the pixel_skip variable.
+ unsigned pixel_skip = (config.scale_horizontally != 0) ? 2 : 1;
+
+ u32 output_width = config.output_width / pixel_skip;
+
for (u32 y = 0; y < config.output_height; ++y) {
// TODO: Why does the register seem to hold twice the framebuffer width?
- for (u32 x = 0; x < config.output_width; ++x) {
+
+ for (u32 x = 0; x < output_width; ++x) {
struct {
int r, g, b, a;
} source_color = { 0, 0, 0, 0 };
- // Cheap emulation of horizontal scaling: Just skip each second pixel of the
- // input framebuffer. We keep track of this in the pixel_skip variable.
- unsigned pixel_skip = (config.scale_horizontally != 0) ? 2 : 1;
-
switch (config.input_format) {
case Regs::PixelFormat::RGBA8:
{
// TODO: Most likely got the component order messed up.
- u8* srcptr = source_pointer + x * 4 * pixel_skip + y * config.input_width * 4 * pixel_skip;
+ u8* srcptr = source_pointer + (x * pixel_skip + y * config.input_width) * 4;
source_color.r = srcptr[0]; // blue
source_color.g = srcptr[1]; // green
source_color.b = srcptr[2]; // red
@@ -131,7 +146,7 @@ inline void Write(u32 addr, const T data) {
case Regs::PixelFormat::RGB8:
{
// TODO: Most likely got the component order messed up.
- u8* dstptr = dest_pointer + x * 3 + y * config.output_width * 3;
+ u8* dstptr = dest_pointer + (x + y * output_width) * 3;
dstptr[0] = source_color.r; // blue
dstptr[1] = source_color.g; // green
dstptr[2] = source_color.b; // red
@@ -146,10 +161,12 @@ inline void Write(u32 addr, const T data) {
}
LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x",
- config.output_height * config.output_width * 4,
+ config.output_height * output_width * 4,
config.GetPhysicalInputAddress(), (u32)config.input_width, (u32)config.input_height,
- config.GetPhysicalOutputAddress(), (u32)config.output_width, (u32)config.output_height,
+ config.GetPhysicalOutputAddress(), (u32)output_width, (u32)config.output_height,
config.output_format.Value());
+
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
}
break;
}
@@ -184,51 +201,39 @@ template void Write<u16>(u32 addr, const u16 data);
template void Write<u8>(u32 addr, const u8 data);
/// Update hardware
-void Update() {
+static void VBlankCallback(u64 userdata, int cycles_late) {
auto& framebuffer_top = g_regs.framebuffer_config[0];
- // Synchronize GPU on a thread reschedule: Because we cannot accurately predict a vertical
- // blank, we need to simulate it. Based on testing, it seems that retail applications work more
- // accurately when this is signalled between thread switches.
-
- if (HLE::g_reschedule) {
- u64 current_ticks = Core::g_app_core->GetTicks();
- u32 num_lines = static_cast<u32>((current_ticks - last_update_tick) / line_ticks);
-
- // Synchronize line...
- if (num_lines > 0) {
- GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
- cur_line += num_lines;
- last_update_tick += (num_lines * line_ticks);
- }
-
- // Synchronize frame...
- if (cur_line >= framebuffer_top.height) {
- cur_line = 0;
- frame_count++;
- last_skip_frame = g_skip_frame;
- g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
-
- // Swap buffers based on the frameskip mode, which is a little bit tricky. When
- // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
- // So, we should only swap frames if the last frame was rendered. The rules are:
- // - If frameskip == 0 (disabled), always swap buffers
- // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
- // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
- if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
- Settings::values.frame_skip == 0) {
- VideoCore::g_renderer->SwapBuffers();
- }
-
- // Signal to GSP that GPU interrupt has occurred
- GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
-
- // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
- // until we can emulate DSP interrupts, this is probably the only reasonable place to do
- // this. Certain games expect this to be periodically signaled.
- DSP_DSP::SignalInterrupt();
- }
+ frame_count++;
+ last_skip_frame = g_skip_frame;
+ g_skip_frame = (frame_count & Settings::values.frame_skip) != 0;
+
+ // Swap buffers based on the frameskip mode, which is a little bit tricky. When
+ // a frame is being skipped, nothing is being rendered to the internal framebuffer(s).
+ // So, we should only swap frames if the last frame was rendered. The rules are:
+ // - If frameskip == 0 (disabled), always swap buffers
+ // - If frameskip == 1, swap buffers every other frame (starting from the first frame)
+ // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame)
+ if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && last_skip_frame != g_skip_frame) ||
+ Settings::values.frame_skip == 0) {
+ VideoCore::g_renderer->SwapBuffers();
}
+
+ // Signal to GSP that GPU interrupt has occurred
+ // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub
+ // screen, or if both use the same interrupts and these two instead determine the
+ // beginning and end of the VBlank period. If needed, split the interrupt firing into
+ // two different intervals.
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC0);
+ GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PDC1);
+
+ // TODO(bunnei): Fake a DSP interrupt on each frame. This does not belong here, but
+ // until we can emulate DSP interrupts, this is probably the only reasonable place to do
+ // this. Certain games expect this to be periodically signaled.
+ DSP_DSP::SignalInterrupt();
+
+ // Reschedule recurrent event
+ CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event);
}
/// Initialize hardware
@@ -245,8 +250,8 @@ void Init() {
framebuffer_top.address_right1 = 0x18273000;
framebuffer_top.address_right2 = 0x182B9800;
framebuffer_sub.address_left1 = 0x1848F000;
- //framebuffer_sub.address_left2 = unknown;
- framebuffer_sub.address_right1 = 0x184C7800;
+ framebuffer_sub.address_left2 = 0x184C7800;
+ //framebuffer_sub.address_right1 = unknown;
//framebuffer_sub.address_right2 = unknown;
framebuffer_top.width = 240;
@@ -262,12 +267,12 @@ void Init() {
framebuffer_sub.active_fb = 0;
frame_ticks = 268123480 / Settings::values.gpu_refresh_rate;
- line_ticks = (GPU::frame_ticks / framebuffer_top.height);
- cur_line = 0;
- last_update_tick = Core::g_app_core->GetTicks();
last_skip_frame = false;
g_skip_frame = false;
+ vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback);
+ CoreTiming::ScheduleEvent(frame_ticks, vblank_event);
+
LOG_DEBUG(HW_GPU, "initialized OK");
}
diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index 7de05523..7c3a17ee 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -252,9 +252,6 @@ void Read(T &var, const u32 addr);
template <typename T>
void Write(u32 addr, const T data);
-/// Update hardware
-void Update();
-
/// Initialize hardware
void Init();
diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp
index 848ab534..50320062 100644
--- a/src/core/hw/hw.cpp
+++ b/src/core/hw/hw.cpp
@@ -75,7 +75,6 @@ template void Write<u8>(u32 addr, const u8 data);
/// Update hardware
void Update() {
- GPU::Update();
}
/// Initialize hardware
diff --git a/src/core/system.cpp b/src/core/system.cpp
index d6188f05..f4c2df1c 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -21,11 +21,11 @@ void UpdateState(State state) {
void Init(EmuWindow* emu_window) {
Core::Init();
+ CoreTiming::Init();
Memory::Init();
HW::Init();
Kernel::Init();
HLE::Init();
- CoreTiming::Init();
VideoCore::Init(emu_window);
}
@@ -38,11 +38,11 @@ void RunLoopUntil(u64 global_cycles) {
void Shutdown() {
VideoCore::Shutdown();
- CoreTiming::Shutdown();
HLE::Shutdown();
Kernel::Shutdown();
HW::Shutdown();
Memory::Shutdown();
+ CoreTiming::Shutdown();
Core::Shutdown();
}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index a494465b..12f0009b 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -18,6 +18,7 @@
#include "common/log.h"
#include "common/file_util.h"
+#include "common/math_util.h"
#include "video_core/color.h"
#include "video_core/math.h"
@@ -337,25 +338,31 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
i = (i ^ (i << 1)) & 0x1515; // ---2 -1-0
i = (i | (i >> 7)) & 0x3F;
- source += coarse_y * info.stride;
- const unsigned int offset = coarse_x * block_height + i;
+ if (info.format != Regs::TextureFormat::ETC1 &&
+ info.format != Regs::TextureFormat::ETC1A4) {
+ // TODO(neobrain): Fix code design to unify vertical block offsets!
+ source += coarse_y * info.stride;
+ }
+ const unsigned int offset = coarse_x * block_height;
+
+ // TODO: Assert that width/height are multiples of block dimensions
switch (info.format) {
case Regs::TextureFormat::RGBA8:
{
- const u8* source_ptr = source + offset * 4;
+ const u8* source_ptr = source + offset * 4 + i * 4;
return { source_ptr[3], source_ptr[2], source_ptr[1], disable_alpha ? (u8)255 : source_ptr[0] };
}
case Regs::TextureFormat::RGB8:
{
- const u8* source_ptr = source + offset * 3;
+ const u8* source_ptr = source + offset * 3 + i * 3;
return { source_ptr[2], source_ptr[1], source_ptr[0], 255 };
}
case Regs::TextureFormat::RGBA5551:
{
- const u16 source_ptr = *(const u16*)(source + offset * 2);
+ const u16 source_ptr = *(const u16*)(source + offset * 2 + i * 2);
u8 r = (source_ptr >> 11) & 0x1F;
u8 g = ((source_ptr) >> 6) & 0x1F;
u8 b = (source_ptr >> 1) & 0x1F;
@@ -366,7 +373,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::RGB565:
{
- const u16 source_ptr = *(const u16*)(source + offset * 2);
+ const u16 source_ptr = *(const u16*)(source + offset * 2 + i * 2);
u8 r = Color::Convert5To8((source_ptr >> 11) & 0x1F);
u8 g = Color::Convert6To8(((source_ptr) >> 5) & 0x3F);
u8 b = Color::Convert5To8((source_ptr) & 0x1F);
@@ -375,7 +382,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::RGBA4:
{
- const u8* source_ptr = source + offset * 2;
+ const u8* source_ptr = source + offset * 2 + i * 2;
u8 r = Color::Convert4To8(source_ptr[1] >> 4);
u8 g = Color::Convert4To8(source_ptr[1] & 0xF);
u8 b = Color::Convert4To8(source_ptr[0] >> 4);
@@ -385,7 +392,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::IA8:
{
- const u8* source_ptr = source + offset * 2;
+ const u8* source_ptr = source + offset * 2 + i * 2;
if (disable_alpha) {
// Show intensity as red, alpha as green
@@ -397,13 +404,13 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::I8:
{
- const u8* source_ptr = source + offset;
+ const u8* source_ptr = source + offset + i;
return { *source_ptr, *source_ptr, *source_ptr, 255 };
}
case Regs::TextureFormat::A8:
{
- const u8* source_ptr = source + offset;
+ const u8* source_ptr = source + offset + i;
if (disable_alpha) {
return { *source_ptr, *source_ptr, *source_ptr, 255 };
@@ -414,7 +421,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::IA4:
{
- const u8* source_ptr = source + offset;
+ const u8* source_ptr = source + offset + i;
u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4);
u8 a = Color::Convert4To8((*source_ptr) & 0xF);
@@ -429,7 +436,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
case Regs::TextureFormat::A4:
{
- const u8* source_ptr = source + offset / 2;
+ const u8* source_ptr = source + offset / 2 + i / 2;
u8 a = (coarse_x % 2) ? ((*source_ptr)&0xF) : (((*source_ptr) & 0xF0) >> 4);
a = Color::Convert4To8(a);
@@ -441,6 +448,127 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture
}
}
+ case Regs::TextureFormat::ETC1:
+ case Regs::TextureFormat::ETC1A4:
+ {
+ bool has_alpha = (info.format == Regs::TextureFormat::ETC1A4);
+
+ // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles
+ const int subtile_width = 4;
+ const int subtile_height = 4;
+
+ int subtile_index = ((x / subtile_width) & 1) + 2 * ((y / subtile_height) & 1);
+ unsigned subtile_bytes = has_alpha ? 2 : 1; // TODO: Name...
+
+ const u64* source_ptr = (const u64*)(source
+ + coarse_x * subtile_bytes * 4
+ + coarse_y * subtile_bytes * 4 * (info.width / 8)
+ + subtile_index * subtile_bytes * 8);
+ u64 alpha = 0xFFFFFFFFFFFFFFFF;
+ if (has_alpha) {
+ alpha = *source_ptr;
+ source_ptr++;
+ }
+
+ union ETC1Tile {
+ // Each of these two is a collection of 16 bits (one per lookup value)
+ BitField< 0, 16, u64> table_subindexes;
+ BitField<16, 16, u64> negation_flags;
+
+ unsigned GetTableSubIndex(unsigned index) const {
+ return (table_subindexes >> index) & 1;
+ }
+
+ bool GetNegationFlag(unsigned index) const {
+ return ((negation_flags >> index) & 1) == 1;
+ }
+
+ BitField<32, 1, u64> flip;
+ BitField<33, 1, u64> differential_mode;
+
+ BitField<34, 3, u64> table_index_2;
+ BitField<37, 3, u64> table_index_1;
+
+ union {
+ // delta value + base value
+ BitField<40, 3, s64> db;
+ BitField<43, 5, u64> b;
+
+ BitField<48, 3, s64> dg;
+ BitField<51, 5, u64> g;
+
+ BitField<56, 3, s64> dr;
+ BitField<59, 5, u64> r;
+ } differential;
+
+ union {
+ BitField<40, 4, u64> b2;
+ BitField<44, 4, u64> b1;
+
+ BitField<48, 4, u64> g2;
+ BitField<52, 4, u64> g1;
+
+ BitField<56, 4, u64> r2;
+ BitField<60, 4, u64> r1;
+ } separate;
+
+ const Math::Vec3<u8> GetRGB(int x, int y) const {
+ int texel = 4 * x + y;
+
+ if (flip)
+ std::swap(x, y);
+
+ // Lookup base value
+ Math::Vec3<int> ret;
+ if (differential_mode) {
+ ret.r() = differential.r;
+ ret.g() = differential.g;
+ ret.b() = differential.b;
+ if (x >= 2) {
+ ret.r() += differential.dr;
+ ret.g() += differential.dg;
+ ret.b() += differential.db;
+ }
+ ret.r() = Color::Convert5To8(ret.r());
+ ret.g() = Color::Convert5To8(ret.g());
+ ret.b() = Color::Convert5To8(ret.b());
+ } else {
+ if (x < 2) {
+ ret.r() = Color::Convert4To8(separate.r1);
+ ret.g() = Color::Convert4To8(separate.g1);
+ ret.b() = Color::Convert4To8(separate.b1);
+ } else {
+ ret.r() = Color::Convert4To8(separate.r2);
+ ret.g() = Color::Convert4To8(separate.g2);
+ ret.b() = Color::Convert4To8(separate.b2);
+ }
+ }
+
+ // Add modifier
+ unsigned table_index = (x < 2) ? table_index_2.Value() : table_index_1.Value();
+
+ static const auto etc1_modifier_table = std::array<std::array<u8, 2>, 8>{{
+ { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 },
+ { 18, 60 }, { 24, 80 }, { 33, 106 }, { 47, 183 }
+ }};
+
+ int modifier = etc1_modifier_table.at(table_index).at(GetTableSubIndex(texel));
+ if (GetNegationFlag(texel))
+ modifier *= -1;
+
+ ret.r() = MathUtil::Clamp(ret.r() + modifier, 0, 255);
+ ret.g() = MathUtil::Clamp(ret.g() + modifier, 0, 255);
+ ret.b() = MathUtil::Clamp(ret.b() + modifier, 0, 255);
+
+ return ret.Cast<u8>();
+ }
+ } const *etc1_tile = reinterpret_cast<const ETC1Tile*>(source_ptr);
+
+ alpha >>= 4 * ((x & 3) * 4 + (y & 3));
+ return Math::MakeVec(etc1_tile->GetRGB(x & 3, y & 3),
+ disable_alpha ? (u8)255 : Color::Convert4To8(alpha & 0xF));
+ }
+
default:
LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format);
_dbg_assert_(HW_GPU, 0);
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index f5771ed8..de1ce05b 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -161,8 +161,8 @@ struct Regs {
IA4 = 9,
A4 = 11,
- // TODO: Support for the other formats is not implemented, yet.
- // Seems like they are luminance formats and compressed textures.
+ ETC1 = 12, // compressed
+ ETC1A4 = 13, // compressed
};
static unsigned NibblesPerPixel(TextureFormat format) {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 29d220e8..aa47bd61 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -88,10 +88,8 @@ void RendererOpenGL::SwapBuffers() {
void RendererOpenGL::LoadFBToActiveGLTexture(const GPU::Regs::FramebufferConfig& framebuffer,
const TextureInfo& texture) {
- // TODO: Why are active_fb and the valid framebuffer flipped compared to 3dbrew documentation
- // and GSP definitions?
const VAddr framebuffer_vaddr = Memory::PhysicalToVirtualAddress(
- framebuffer.active_fb == 0 ? framebuffer.address_left2 : framebuffer.address_left1);
+ framebuffer.active_fb == 0 ? framebuffer.address_left1 : framebuffer.address_left2);
LOG_TRACE(Render_OpenGL, "0x%08x bytes from 0x%08x(%dx%d), fmt %x",
framebuffer.stride * framebuffer.height,
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp
index ff825e2e..48977380 100644
--- a/src/video_core/vertex_shader.cpp
+++ b/src/video_core/vertex_shader.cpp
@@ -348,13 +348,114 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
+
+ case Instruction::OpCodeType::MultiplyAdd:
+ {
+ if (instr.opcode.EffectiveOpCode() == Instruction::OpCode::MAD) {
+ const SwizzlePattern& swizzle = *(SwizzlePattern*)&swizzle_data[instr.mad.operand_desc_id];
+
+ const float24* src1_ = LookupSourceRegister(instr.mad.src1);
+ const float24* src2_ = LookupSourceRegister(instr.mad.src2);
+ const float24* src3_ = LookupSourceRegister(instr.mad.src3);
+
+ const bool negate_src1 = ((bool)swizzle.negate_src1 != false);
+ const bool negate_src2 = ((bool)swizzle.negate_src2 != false);
+ const bool negate_src3 = ((bool)swizzle.negate_src3 != false);
+
+ float24 src1[4] = {
+ src1_[(int)swizzle.GetSelectorSrc1(0)],
+ src1_[(int)swizzle.GetSelectorSrc1(1)],
+ src1_[(int)swizzle.GetSelectorSrc1(2)],
+ src1_[(int)swizzle.GetSelectorSrc1(3)],
+ };
+ if (negate_src1) {
+ src1[0] = src1[0] * float24::FromFloat32(-1);
+ src1[1] = src1[1] * float24::FromFloat32(-1);
+ src1[2] = src1[2] * float24::FromFloat32(-1);
+ src1[3] = src1[3] * float24::FromFloat32(-1);
+ }
+ float24 src2[4] = {
+ src2_[(int)swizzle.GetSelectorSrc2(0)],
+ src2_[(int)swizzle.GetSelectorSrc2(1)],
+ src2_[(int)swizzle.GetSelectorSrc2(2)],
+ src2_[(int)swizzle.GetSelectorSrc2(3)],
+ };
+ if (negate_src2) {
+ src2[0] = src2[0] * float24::FromFloat32(-1);
+ src2[1] = src2[1] * float24::FromFloat32(-1);
+ src2[2] = src2[2] * float24::FromFloat32(-1);
+ src2[3] = src2[3] * float24::FromFloat32(-1);
+ }
+ float24 src3[4] = {
+ src3_[(int)swizzle.GetSelectorSrc3(0)],
+ src3_[(int)swizzle.GetSelectorSrc3(1)],
+ src3_[(int)swizzle.GetSelectorSrc3(2)],
+ src3_[(int)swizzle.GetSelectorSrc3(3)],
+ };
+ if (negate_src3) {
+ src3[0] = src3[0] * float24::FromFloat32(-1);
+ src3[1] = src3[1] * float24::FromFloat32(-1);
+ src3[2] = src3[2] * float24::FromFloat32(-1);
+ src3[3] = src3[3] * float24::FromFloat32(-1);
+ }
+
+ float24* dest = (instr.mad.dest < 0x08) ? state.output_register_table[4*instr.mad.dest.GetIndex()]
+ : (instr.mad.dest < 0x10) ? dummy_vec4_float24
+ : (instr.mad.dest < 0x20) ? &state.temporary_registers[instr.mad.dest.GetIndex()][0]
+ : dummy_vec4_float24;
+
+ for (int i = 0; i < 4; ++i) {
+ if (!swizzle.DestComponentEnabled(i))
+ continue;
+
+ dest[i] = src1[i] * src2[i] + src3[i];
+ }
+ } else {
+ LOG_ERROR(HW_GPU, "Unhandled multiply-add instruction: 0x%02x (%s): 0x%08x",
+ (int)instr.opcode.Value(), instr.opcode.GetInfo().name, instr.hex);
+ }
+ break;
+ }
+
default:
+ {
+ static auto evaluate_condition = [](const VertexShaderState& state, bool refx, bool refy, Instruction::FlowControlType flow_control) {
+ bool results[2] = { refx == state.conditional_code[0],
+ refy == state.conditional_code[1] };
+
+ switch (flow_control.op) {
+ case flow_control.Or:
+ return results[0] || results[1];
+
+ case flow_control.And:
+ return results[0] && results[1];
+
+ case flow_control.JustX:
+ return results[0];
+
+ case flow_control.JustY:
+ return results[1];
+ }
+ };
+
// Handle each instruction on its own
switch (instr.opcode) {
case Instruction::OpCode::END:
exit_loop = true;
break;
+ case Instruction::OpCode::JMPC:
+ if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
+ state.program_counter = &shader_memory[instr.flow_control.dest_offset] - 1;
+ }
+ break;
+
+ case Instruction::OpCode::JMPU:
+ if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) {
+ state.program_counter = &shader_memory[instr.flow_control.dest_offset] - 1;
+ }
+ break;
+
case Instruction::OpCode::CALL:
call(state,
instr.flow_control.dest_offset,
@@ -362,6 +463,24 @@ static void ProcessShaderCode(VertexShaderState& state) {
binary_offset + 1);
break;
+ case Instruction::OpCode::CALLU:
+ if (shader_uniforms.b[instr.flow_control.bool_uniform_id]) {
+ call(state,
+ instr.flow_control.dest_offset,
+ instr.flow_control.num_instructions,
+ binary_offset + 1);
+ }
+ break;
+
+ case Instruction::OpCode::CALLC:
+ if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
+ call(state,
+ instr.flow_control.dest_offset,
+ instr.flow_control.num_instructions,
+ binary_offset + 1);
+ }
+ break;
+
case Instruction::OpCode::NOP:
break;
@@ -384,29 +503,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
{
// TODO: Do we need to consider swizzlers here?
- auto flow_control = instr.flow_control;
- bool results[3] = { (bool)flow_control.refx == state.conditional_code[0],
- (bool)flow_control.refy == state.conditional_code[1] };
-
- switch (flow_control.op) {
- case flow_control.Or:
- results[2] = results[0] || results[1];
- break;
-
- case flow_control.And:
- results[2] = results[0] && results[1];
- break;
-
- case flow_control.JustX:
- results[2] = results[0];
- break;
-
- case flow_control.JustY:
- results[2] = results[1];
- break;
- }
-
- if (results[2]) {
+ if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, instr.flow_control)) {
call(state,
binary_offset + 1,
instr.flow_control.dest_offset - binary_offset - 1,
@@ -429,6 +526,7 @@ static void ProcessShaderCode(VertexShaderState& state) {
break;
}
+ }
++state.program_counter;