summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/audio_core/CMakeLists.txt16
-rw-r--r--src/audio_core/audio_core.cpp53
-rw-r--r--src/audio_core/audio_core.h26
-rw-r--r--src/audio_core/hle/dsp.cpp42
-rw-r--r--src/audio_core/hle/dsp.h539
-rw-r--r--src/audio_core/hle/pipe.cpp159
-rw-r--r--src/audio_core/hle/pipe.h57
-rw-r--r--src/audio_core/sink.h34
-rw-r--r--src/citra/CMakeLists.txt21
-rw-r--r--src/citra/citra.cpp4
-rw-r--r--src/citra/config.cpp60
-rw-r--r--src/citra/config.h10
-rw-r--r--src/citra/default_ini.h2
-rw-r--r--src/citra/emu_window/emu_window_glfw.cpp168
-rw-r--r--src/citra/emu_window/emu_window_glfw.h54
-rw-r--r--src/citra/emu_window/emu_window_sdl2.cpp167
-rw-r--r--src/citra/emu_window/emu_window_sdl2.h64
-rw-r--r--src/citra_qt/CMakeLists.txt27
-rw-r--r--src/citra_qt/debugger/callstack.cpp12
-rw-r--r--src/citra_qt/debugger/graphics_cmdlists.cpp1
-rw-r--r--src/citra_qt/debugger/graphics_framebuffer.cpp1
-rw-r--r--src/citra_qt/debugger/graphics_tracing.cpp2
-rw-r--r--src/citra_qt/debugger/graphics_vertex_shader.cpp6
-rw-r--r--src/citra_qt/debugger/registers.cpp8
-rw-r--r--src/citra_qt/main.cpp89
-rw-r--r--src/citra_qt/main.h10
-rw-r--r--src/common/bit_field.h44
-rw-r--r--src/common/color.h16
-rw-r--r--src/common/common_funcs.h10
-rw-r--r--src/common/emu_window.cpp4
-rw-r--r--src/common/file_util.cpp20
-rw-r--r--src/common/key_map.h14
-rw-r--r--src/common/logging/backend.cpp4
-rw-r--r--src/common/logging/backend.h20
-rw-r--r--src/common/logging/log.h4
-rw-r--r--src/common/x64/emitter.cpp4
-rw-r--r--src/common/x64/emitter.h113
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/arm/arm_interface.h11
-rw-r--r--src/core/arm/disassembler/arm_disasm.cpp18
-rw-r--r--src/core/arm/dyncom/arm_dyncom_dec.cpp8
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp549
-rw-r--r--src/core/arm/dyncom/arm_dyncom_run.h4
-rw-r--r--src/core/arm/dyncom/arm_dyncom_thumb.h2
-rw-r--r--src/core/arm/skyeye_common/vfp/vfp_helper.h32
-rw-r--r--src/core/core.cpp18
-rw-r--r--src/core/core.h7
-rw-r--r--src/core/file_sys/archive_backend.cpp8
-rw-r--r--src/core/file_sys/archive_backend.h8
-rw-r--r--src/core/file_sys/disk_archive.cpp6
-rw-r--r--src/core/hle/function_wrappers.h4
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp25
-rw-r--r--src/core/hle/kernel/memory.cpp5
-rw-r--r--src/core/hle/kernel/process.cpp8
-rw-r--r--src/core/hle/kernel/process.h1
-rw-r--r--src/core/hle/kernel/shared_memory.cpp21
-rw-r--r--src/core/hle/kernel/shared_memory.h7
-rw-r--r--src/core/hle/kernel/thread.cpp2
-rw-r--r--src/core/hle/kernel/timer.cpp6
-rw-r--r--src/core/hle/kernel/vm_manager.cpp7
-rw-r--r--src/core/hle/kernel/vm_manager.h5
-rw-r--r--src/core/hle/result.h67
-rw-r--r--src/core/hle/service/ac_u.cpp20
-rw-r--r--src/core/hle/service/act_u.cpp8
-rw-r--r--src/core/hle/service/am/am.cpp4
-rw-r--r--src/core/hle/service/am/am.h6
-rw-r--r--src/core/hle/service/am/am_app.cpp1
-rw-r--r--src/core/hle/service/am/am_net.cpp31
-rw-r--r--src/core/hle/service/am/am_sys.cpp16
-rw-r--r--src/core/hle/service/am/am_u.cpp29
-rw-r--r--src/core/hle/service/apt/apt.cpp2
-rw-r--r--src/core/hle/service/apt/apt_a.cpp1
-rw-r--r--src/core/hle/service/apt/apt_s.cpp8
-rw-r--r--src/core/hle/service/apt/apt_u.cpp9
-rw-r--r--src/core/hle/service/boss/boss.cpp4
-rw-r--r--src/core/hle/service/boss/boss.h3
-rw-r--r--src/core/hle/service/boss/boss_p.cpp2
-rw-r--r--src/core/hle/service/boss/boss_u.cpp5
-rw-r--r--src/core/hle/service/cam/cam.cpp286
-rw-r--r--src/core/hle/service/cam/cam.h417
-rw-r--r--src/core/hle/service/cam/cam_c.cpp2
-rw-r--r--src/core/hle/service/cam/cam_q.cpp2
-rw-r--r--src/core/hle/service/cam/cam_s.cpp2
-rw-r--r--src/core/hle/service/cam/cam_u.cpp44
-rw-r--r--src/core/hle/service/cecd/cecd.cpp4
-rw-r--r--src/core/hle/service/cecd/cecd.h3
-rw-r--r--src/core/hle/service/cecd/cecd_s.cpp2
-rw-r--r--src/core/hle/service/cecd/cecd_u.cpp9
-rw-r--r--src/core/hle/service/cfg/cfg.cpp27
-rw-r--r--src/core/hle/service/cfg/cfg.h9
-rw-r--r--src/core/hle/service/cfg/cfg_i.cpp1
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp1
-rw-r--r--src/core/hle/service/cfg/cfg_u.cpp1
-rw-r--r--src/core/hle/service/csnd_snd.cpp7
-rw-r--r--src/core/hle/service/dsp_dsp.cpp311
-rw-r--r--src/core/hle/service/dsp_dsp.h12
-rw-r--r--src/core/hle/service/err_f.cpp3
-rw-r--r--src/core/hle/service/frd/frd.cpp4
-rw-r--r--src/core/hle/service/frd/frd.h3
-rw-r--r--src/core/hle/service/frd/frd_a.cpp2
-rw-r--r--src/core/hle/service/frd/frd_u.cpp45
-rw-r--r--src/core/hle/service/fs/fs_user.cpp196
-rw-r--r--src/core/hle/service/gsp_gpu.cpp16
-rw-r--r--src/core/hle/service/gsp_lcd.cpp13
-rw-r--r--src/core/hle/service/hid/hid.cpp3
-rw-r--r--src/core/hle/service/hid/hid_spvr.cpp1
-rw-r--r--src/core/hle/service/hid/hid_user.cpp3
-rw-r--r--src/core/hle/service/http_c.cpp13
-rw-r--r--src/core/hle/service/ir/ir.cpp73
-rw-r--r--src/core/hle/service/ir/ir.h53
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp1
-rw-r--r--src/core/hle/service/ir/ir_u.cpp2
-rw-r--r--src/core/hle/service/ir/ir_user.cpp41
-rw-r--r--src/core/hle/service/ldr_ro.cpp2
-rw-r--r--src/core/hle/service/mic_u.cpp11
-rw-r--r--src/core/hle/service/ndm_u.cpp19
-rw-r--r--src/core/hle/service/news/news.cpp4
-rw-r--r--src/core/hle/service/news/news.h3
-rw-r--r--src/core/hle/service/news/news_s.cpp13
-rw-r--r--src/core/hle/service/news/news_u.cpp2
-rw-r--r--src/core/hle/service/nim/nim.cpp5
-rw-r--r--src/core/hle/service/nim/nim.h6
-rw-r--r--src/core/hle/service/nim/nim_aoc.cpp2
-rw-r--r--src/core/hle/service/nim/nim_s.cpp5
-rw-r--r--src/core/hle/service/nim/nim_u.cpp1
-rw-r--r--src/core/hle/service/ns_s.cpp16
-rw-r--r--src/core/hle/service/nwm_uds.cpp20
-rw-r--r--src/core/hle/service/pm_app.cpp5
-rw-r--r--src/core/hle/service/ptm/ptm.cpp4
-rw-r--r--src/core/hle/service/ptm/ptm.h7
-rw-r--r--src/core/hle/service/ptm/ptm_play.cpp1
-rw-r--r--src/core/hle/service/ptm/ptm_sysm.cpp4
-rw-r--r--src/core/hle/service/ptm/ptm_u.cpp3
-rw-r--r--src/core/hle/service/soc_u.cpp32
-rw-r--r--src/core/hle/service/srv.cpp2
-rw-r--r--src/core/hle/service/ssl_c.cpp16
-rw-r--r--src/core/hle/service/y2r_u.cpp27
-rw-r--r--src/core/hle/svc.cpp37
-rw-r--r--src/core/hw/gpu.cpp34
-rw-r--r--src/core/loader/elf.cpp6
-rw-r--r--src/core/loader/loader.cpp6
-rw-r--r--src/core/loader/ncch.cpp3
-rw-r--r--src/core/memory.cpp88
-rw-r--r--src/core/memory_setup.h7
-rw-r--r--src/core/mmio.h34
-rw-r--r--src/core/system.cpp15
-rw-r--r--src/core/system.h9
-rw-r--r--src/video_core/CMakeLists.txt7
-rw-r--r--src/video_core/clipper.cpp13
-rw-r--r--src/video_core/clipper.h2
-rw-r--r--src/video_core/command_processor.cpp136
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp17
-rw-r--r--src/video_core/debug_utils/debug_utils.h3
-rw-r--r--src/video_core/pica.cpp523
-rw-r--r--src/video_core/pica.h429
-rw-r--r--src/video_core/pica_state.h60
-rw-r--r--src/video_core/pica_types.h146
-rw-r--r--src/video_core/primitive_assembly.cpp21
-rw-r--r--src/video_core/primitive_assembly.h10
-rw-r--r--src/video_core/rasterizer.cpp17
-rw-r--r--src/video_core/rasterizer_interface.h (renamed from src/video_core/hwrasterizer_base.h)19
-rw-r--r--src/video_core/renderer_base.cpp29
-rw-r--r--src/video_core/renderer_base.h24
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp456
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h181
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp225
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h2
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_state.h5
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h12
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp82
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h2
-rw-r--r--src/video_core/shader/shader.cpp9
-rw-r--r--src/video_core/shader/shader.h32
-rw-r--r--src/video_core/shader/shader_interpreter.cpp4
-rw-r--r--src/video_core/shader/shader_jit_x64.cpp16
-rw-r--r--src/video_core/shader/shader_jit_x64.h2
-rw-r--r--src/video_core/swrasterizer.cpp16
-rw-r--r--src/video_core/swrasterizer.h26
-rw-r--r--src/video_core/video_core.cpp23
-rw-r--r--src/video_core/video_core.h7
184 files changed, 5736 insertions, 1780 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cb09f3cd1..de4fe716a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,7 +4,8 @@ include_directories(.)
add_subdirectory(common)
add_subdirectory(core)
add_subdirectory(video_core)
-if (ENABLE_GLFW)
+add_subdirectory(audio_core)
+if (ENABLE_SDL2)
add_subdirectory(citra)
endif()
if (ENABLE_QT)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
new file mode 100644
index 000000000..b0d1c7eb6
--- /dev/null
+++ b/src/audio_core/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SRCS
+ audio_core.cpp
+ hle/dsp.cpp
+ hle/pipe.cpp
+ )
+
+set(HEADERS
+ audio_core.h
+ hle/dsp.h
+ hle/pipe.h
+ sink.h
+ )
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(audio_core STATIC ${SRCS} ${HEADERS}) \ No newline at end of file
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp
new file mode 100644
index 000000000..894f46990
--- /dev/null
+++ b/src/audio_core/audio_core.cpp
@@ -0,0 +1,53 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/audio_core.h"
+#include "audio_core/hle/dsp.h"
+
+#include "core/core_timing.h"
+#include "core/hle/kernel/vm_manager.h"
+#include "core/hle/service/dsp_dsp.h"
+
+namespace AudioCore {
+
+// Audio Ticks occur about every 5 miliseconds.
+static int tick_event; ///< CoreTiming event
+static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles
+
+static void AudioTickCallback(u64 /*userdata*/, int cycles_late) {
+ if (DSP::HLE::Tick()) {
+ // HACK: We're not signaling the interrups when they should be, but just firing them all off together.
+ // It should be only (interrupt_id = 2, channel_id = 2) that's signalled here.
+ // TODO(merry): Understand when the other interrupts are fired.
+ DSP_DSP::SignalAllInterrupts();
+ }
+
+ // Reschedule recurrent event
+ CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event);
+}
+
+/// Initialise Audio
+void Init() {
+ DSP::HLE::Init();
+
+ tick_event = CoreTiming::RegisterEvent("AudioCore::tick_event", AudioTickCallback);
+ CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event);
+}
+
+/// Add DSP address spaces to Process's address space.
+void AddAddressSpace(Kernel::VMManager& address_space) {
+ auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_region0), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom();
+ address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite);
+
+ auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_region1), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom();
+ address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite);
+}
+
+/// Shutdown Audio
+void Shutdown() {
+ CoreTiming::UnscheduleEvent(tick_event, 0);
+ DSP::HLE::Shutdown();
+}
+
+} //namespace
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h
new file mode 100644
index 000000000..64c330914
--- /dev/null
+++ b/src/audio_core/audio_core.h
@@ -0,0 +1,26 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Kernel {
+class VMManager;
+}
+
+namespace AudioCore {
+
+constexpr int num_sources = 24;
+constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
+constexpr int native_sample_rate = 32728; ///< 32kHz
+
+/// Initialise Audio Core
+void Init();
+
+/// Add DSP address spaces to a Process.
+void AddAddressSpace(Kernel::VMManager& vm_manager);
+
+/// Shutdown Audio Core
+void Shutdown();
+
+} // namespace
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp
new file mode 100644
index 000000000..c89356edc
--- /dev/null
+++ b/src/audio_core/hle/dsp.cpp
@@ -0,0 +1,42 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/pipe.h"
+
+namespace DSP {
+namespace HLE {
+
+SharedMemory g_region0;
+SharedMemory g_region1;
+
+void Init() {
+ DSP::HLE::ResetPipes();
+}
+
+void Shutdown() {
+}
+
+bool Tick() {
+ return true;
+}
+
+SharedMemory& CurrentRegion() {
+ // The region with the higher frame counter is chosen unless there is wraparound.
+
+ if (g_region0.frame_counter == 0xFFFFu && g_region1.frame_counter != 0xFFFEu) {
+ // Wraparound has occured.
+ return g_region1;
+ }
+
+ if (g_region1.frame_counter == 0xFFFFu && g_region0.frame_counter != 0xFFFEu) {
+ // Wraparound has occured.
+ return g_region0;
+ }
+
+ return (g_region0.frame_counter > g_region1.frame_counter) ? g_region0 : g_region1;
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
new file mode 100644
index 000000000..376436c29
--- /dev/null
+++ b/src/audio_core/hle/dsp.h
@@ -0,0 +1,539 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <type_traits>
+
+#include "audio_core/audio_core.h"
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace DSP {
+namespace HLE {
+
+// The application-accessible region of DSP memory consists of two parts.
+// Both are marked as IO and have Read/Write permissions.
+//
+// First Region: 0x1FF50000 (Size: 0x8000)
+// Second Region: 0x1FF70000 (Size: 0x8000)
+//
+// The DSP reads from each region alternately based on the frame counter for each region much like a
+// double-buffer. The frame counter is located as the very last u16 of each region and is incremented
+// each audio tick.
+
+struct SharedMemory;
+
+constexpr VAddr region0_base = 0x1FF50000;
+extern SharedMemory g_region0;
+
+constexpr VAddr region1_base = 0x1FF70000;
+extern SharedMemory g_region1;
+
+/**
+ * The DSP is native 16-bit. The DSP also appears to be big-endian. When reading 32-bit numbers from
+ * its memory regions, the higher and lower 16-bit halves are swapped compared to the little-endian
+ * layout of the ARM11. Hence from the ARM11's point of view the memory space appears to be
+ * middle-endian.
+ *
+ * Unusually this does not appear to be an issue for floating point numbers. The DSP makes the more
+ * sensible choice of keeping that little-endian. There are also some exceptions such as the
+ * IntermediateMixSamples structure, which is little-endian.
+ *
+ * This struct implements the conversion to and from this middle-endianness.
+ */
+struct u32_dsp {
+ u32_dsp() = default;
+ operator u32() const {
+ return Convert(storage);
+ }
+ void operator=(u32 new_value) {
+ storage = Convert(new_value);
+ }
+private:
+ static constexpr u32 Convert(u32 value) {
+ return (value << 16) | (value >> 16);
+ }
+ u32_le storage;
+};
+#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
+static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivially copyable");
+#endif
+
+// There are 15 structures in each memory region. A table of them in the order they appear in memory
+// is presented below:
+//
+// # First Region DSP Address Purpose Control
+// 5 0x8400 DSP Status DSP
+// 9 0x8410 DSP Debug Info DSP
+// 6 0x8540 Final Mix Samples DSP
+// 2 0x8680 Source Status [24] DSP
+// 8 0x8710 Compressor Table Application
+// 4 0x9430 DSP Configuration Application
+// 7 0x9492 Intermediate Mix Samples DSP + App
+// 1 0x9E92 Source Configuration [24] Application
+// 3 0xA792 Source ADPCM Coefficients [24] Application
+// 10 0xA912 Surround Sound Related
+// 11 0xAA12 Surround Sound Related
+// 12 0xAAD2 Surround Sound Related
+// 13 0xAC52 Surround Sound Related
+// 14 0xAC5C Surround Sound Related
+// 0 0xBFFF Frame Counter Application
+//
+// #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
+// See also: DSP::HLE::PipeRead.
+//
+// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are
+// not fixed in stone. The addresses above are only an examplar; they're what this implementation
+// does and provides to applications.
+//
+// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the
+// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the
+// second region via:
+// second_region_dsp_addr = first_region_dsp_addr | 0x10000
+//
+// Applications maintain most of its own audio state, the memory region is used mainly for
+// communication and not storage of state.
+//
+// In the documentation below, filter and effect transfer functions are specified in the z domain.
+// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
+// frequency domain, just like how the s domain is the analog frequency domain.)
+
+#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words))
+
+// GCC versions < 5.0 do not implement std::is_trivially_copyable.
+// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
+#if (__GNUC__ >= 5) || defined(__clang__)
+ #define ASSERT_DSP_STRUCT(name, size) \
+ static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
+ static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \
+ static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
+#else
+ #define ASSERT_DSP_STRUCT(name, size) \
+ static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
+ static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
+#endif
+
+struct SourceConfiguration {
+ struct Configuration {
+ /// These dirty flags are set by the application when it updates the fields in this struct.
+ /// The DSP clears these each audio frame.
+ union {
+ u32_le dirty_raw;
+
+ BitField<2, 1, u32_le> adpcm_coefficients_dirty;
+ BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued.
+
+ BitField<16, 1, u32_le> enable_dirty;
+ BitField<17, 1, u32_le> interpolation_dirty;
+ BitField<18, 1, u32_le> rate_multiplier_dirty;
+ BitField<19, 1, u32_le> buffer_queue_dirty;
+ BitField<20, 1, u32_le> loop_related_dirty;
+ BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated.
+ BitField<22, 1, u32_le> filters_enabled_dirty;
+ BitField<23, 1, u32_le> simple_filter_dirty;
+ BitField<24, 1, u32_le> biquad_filter_dirty;
+ BitField<25, 1, u32_le> gain_0_dirty;
+ BitField<26, 1, u32_le> gain_1_dirty;
+ BitField<27, 1, u32_le> gain_2_dirty;
+ BitField<28, 1, u32_le> sync_dirty;
+ BitField<29, 1, u32_le> reset_flag;
+
+ BitField<31, 1, u32_le> embedded_buffer_dirty;
+ };
+
+ // Gain control
+
+ /**
+ * Gain is between 0.0-1.0. This determines how much will this source appear on
+ * each of the 12 channels that feed into the intermediate mixers.
+ * Each of the three intermediate mixers is fed two left and two right channels.
+ */
+ float_le gain[3][4];
+
+ // Interpolation
+
+ /// Multiplier for sample rate. Resampling occurs with the selected interpolation method.
+ float_le rate_multiplier;
+
+ enum class InterpolationMode : u8 {
+ None = 0,
+ Linear = 1,
+ Polyphase = 2
+ };
+
+ InterpolationMode interpolation_mode;
+ INSERT_PADDING_BYTES(1); ///< Interpolation related
+
+ // Filters
+
+ /**
+ * This is the simplest normalized first-order digital recursive filter.
+ * The transfer function of this filter is:
+ * H(z) = b0 / (1 + a1 z^-1)
+ * Values are signed fixed point with 15 fractional bits.
+ */
+ struct SimpleFilter {
+ s16_le b0;
+ s16_le a1;
+ };
+
+ /**
+ * This is a normalised biquad filter (second-order).
+ * The transfer function of this filter is:
+ * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
+ * Nintendo chose to negate the feedbackward coefficients. This differs from standard notation
+ * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
+ * Values are signed fixed point with 14 fractional bits.
+ */
+ struct BiquadFilter {
+ s16_le b0;
+ s16_le b1;
+ s16_le b2;
+ s16_le a1;
+ s16_le a2;
+ };
+
+ union {
+ u16_le filters_enabled;
+ BitField<0, 1, u16_le> simple_filter_enabled;
+ BitField<1, 1, u16_le> biquad_filter_enabled;
+ };
+
+ SimpleFilter simple_filter;
+ BiquadFilter biquad_filter;
+
+ // Buffer Queue
+
+ /// A buffer of audio data from the application, along with metadata about it.
+ struct Buffer {
+ /// Physical memory address of the start of the buffer
+ u32_dsp physical_address;
+
+ /// This is length in terms of samples.
+ /// Note that in different buffer formats a sample takes up different number of bytes.
+ u32_dsp length;
+
+ /// ADPCM Predictor (4 bits) and Scale (4 bits)
+ union {
+ u16_le adpcm_ps;
+ BitField<0, 4, u16_le> adpcm_scale;
+ BitField<4, 4, u16_le> adpcm_predictor;
+ };
+
+ /// ADPCM Historical Samples (y[n-1] and y[n-2])
+ u16_le adpcm_yn[2];
+
+ /// This is non-zero when the ADPCM values above are to be updated.
+ u8 adpcm_dirty;
+
+ /// Is a looping buffer.
+ u8 is_looping;
+
+ /// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished.
+ /// This allows the emulated application to tell what buffer is currently playing
+ u16_le buffer_id;
+
+ INSERT_PADDING_DSPWORDS(1);
+ };
+
+ u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
+ Buffer buffers[4]; ///< Queued Buffers
+
+ // Playback controls
+
+ u32_dsp loop_related;
+ u8 enable;
+ INSERT_PADDING_BYTES(1);
+ u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
+ u32_dsp play_position; ///< Position. (Units: number of samples)
+ INSERT_PADDING_DSPWORDS(2);
+
+ // Embedded Buffer
+ // This buffer is often the first buffer to be used when initiating audio playback,
+ // after which the buffer queue is used.
+
+ u32_dsp physical_address;
+
+ /// This is length in terms of samples.
+ /// Note a sample takes up different number of bytes in different buffer formats.
+ u32_dsp length;
+
+ enum class MonoOrStereo : u16_le {
+ Mono = 1,
+ Stereo = 2
+ };
+
+ enum class Format : u16_le {
+ PCM8 = 0,
+ PCM16 = 1,
+ ADPCM = 2
+ };
+
+ union {
+ u16_le flags1_raw;
+ BitField<0, 2, MonoOrStereo> mono_or_stereo;
+ BitField<2, 2, Format> format;
+ BitField<5, 1, u16_le> fade_in;
+ };
+
+ /// ADPCM Predictor (4 bit) and Scale (4 bit)
+ union {
+ u16_le adpcm_ps;
+ BitField<0, 4, u16_le> adpcm_scale;
+ BitField<4, 4, u16_le> adpcm_predictor;
+ };
+
+ /// ADPCM Historical Samples (y[n-1] and y[n-2])
+ u16_le adpcm_yn[2];
+
+ union {
+ u16_le flags2_raw;
+ BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
+ BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
+ };
+
+ /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer).
+ u16_le buffer_id;
+ };
+
+ Configuration config[AudioCore::num_sources];
+};
+ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192);
+ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
+
+struct SourceStatus {
+ struct Status {
+ u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
+ u8 previous_buffer_id_dirty; ///< Non-zero when previous_buffer_id changes
+ u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
+ u32_dsp buffer_position; ///< Number of samples into the current buffer
+ u16_le previous_buffer_id; ///< Updated when a buffer finishes playing
+ INSERT_PADDING_DSPWORDS(1);
+ };
+
+ Status status[AudioCore::num_sources];
+};
+ASSERT_DSP_STRUCT(SourceStatus::Status, 12);
+
+struct DspConfiguration {
+ /// These dirty flags are set by the application when it updates the fields in this struct.
+ /// The DSP clears these each audio frame.
+ union {
+ u32_le dirty_raw;
+
+ BitField<8, 1, u32_le> mixer1_enabled_dirty;
+ BitField<9, 1, u32_le> mixer2_enabled_dirty;
+ BitField<10, 1, u32_le> delay_effect_0_dirty;
+ BitField<11, 1, u32_le> delay_effect_1_dirty;
+ BitField<12, 1, u32_le> reverb_effect_0_dirty;
+ BitField<13, 1, u32_le> reverb_effect_1_dirty;
+
+ BitField<16, 1, u32_le> volume_0_dirty;
+
+ BitField<24, 1, u32_le> volume_1_dirty;
+ BitField<25, 1, u32_le> volume_2_dirty;
+ BitField<26, 1, u32_le> output_format_dirty;
+ BitField<27, 1, u32_le> limiter_enabled_dirty;
+ BitField<28, 1, u32_le> headphones_connected_dirty;
+ };
+
+ /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer
+ float_le volume[3];
+
+ INSERT_PADDING_DSPWORDS(3);
+
+ enum class OutputFormat : u16_le {
+ Mono = 0,
+ Stereo = 1,
+ Surround = 2
+ };
+
+ OutputFormat output_format;
+
+ u16_le limiter_enabled; ///< Not sure of the exact gain equation for the limiter.
+ u16_le headphones_connected; ///< Application updates the DSP on headphone status.
+ INSERT_PADDING_DSPWORDS(4); ///< TODO: Surround sound related
+ INSERT_PADDING_DSPWORDS(2); ///< TODO: Intermediate mixer 1/2 related
+ u16_le mixer1_enabled;
+ u16_le mixer2_enabled;
+
+ /**
+ * This is delay with feedback.
+ * Transfer function:
+ * H(z) = a z^-N / (1 - b z^-1 + a g z^-N)
+ * where
+ * N = frame_count * samples_per_frame
+ * g, a and b are fixed point with 7 fractional bits
+ */
+ struct DelayEffect {
+ /// These dirty flags are set by the application when it updates the fields in this struct.
+ /// The DSP clears these each audio frame.
+ union {
+ u16_le dirty_raw;
+ BitField<0, 1, u16_le> enable_dirty;
+ BitField<1, 1, u16_le> work_buffer_address_dirty;
+ BitField<2, 1, u16_le> other_dirty; ///< Set when anything else has been changed
+ };
+
+ u16_le enable;
+ INSERT_PADDING_DSPWORDS(1);
+ u16_le outputs;
+ u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer.
+ u16_le frame_count; ///< Frames to delay by
+
+ // Coefficients
+ s16_le g; ///< Fixed point with 7 fractional bits
+ s16_le a; ///< Fixed point with 7 fractional bits
+ s16_le b; ///< Fixed point with 7 fractional bits
+ };
+
+ DelayEffect delay_effect[2];
+
+ struct ReverbEffect {
+ INSERT_PADDING_DSPWORDS(26); ///< TODO
+ };
+
+ ReverbEffect reverb_effect[2];
+
+ INSERT_PADDING_DSPWORDS(4);
+};
+ASSERT_DSP_STRUCT(DspConfiguration, 196);
+ASSERT_DSP_STRUCT(DspConfiguration::DelayEffect, 20);
+ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
+
+struct AdpcmCoefficients {
+ /// Coefficients are signed fixed point with 11 fractional bits.
+ /// Each source has 16 coefficients associated with it.
+ s16_le coeff[AudioCore::num_sources][16];
+};
+ASSERT_DSP_STRUCT(AdpcmCoefficients, 768);
+
+struct DspStatus {
+ u16_le unknown;
+ u16_le dropped_frames;
+ INSERT_PADDING_DSPWORDS(0xE);
+};
+ASSERT_DSP_STRUCT(DspStatus, 32);
+
+/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
+/// When the application writes to this region it has no effect.
+struct FinalMixSamples {
+ s16_le pcm16[2 * AudioCore::samples_per_frame];
+};
+ASSERT_DSP_STRUCT(FinalMixSamples, 640);
+
+/// DSP writes output of intermediate mixers 1 and 2 here.
+/// Writes to this region by the application edits the output of the intermediate mixers.
+/// This seems to be intended to allow the application to do custom effects on the ARM11.
+/// Values that exceed s16 range will be clipped by the DSP after further processing.
+struct IntermediateMixSamples {
+ struct Samples {
+ s32_le pcm32[4][AudioCore::samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
+ };
+
+ Samples mix1;
+ Samples mix2;
+};
+ASSERT_DSP_STRUCT(IntermediateMixSamples, 5120);
+
+/// Compressor table
+struct Compressor {
+ INSERT_PADDING_DSPWORDS(0xD20); ///< TODO
+};
+
+/// There is no easy way to implement this in a HLE implementation.
+struct DspDebug {
+ INSERT_PADDING_DSPWORDS(0x130);
+};
+ASSERT_DSP_STRUCT(DspDebug, 0x260);
+
+struct SharedMemory {
+ /// Padding
+ INSERT_PADDING_DSPWORDS(0x400);
+
+ DspStatus dsp_status;
+
+ DspDebug dsp_debug;
+
+ FinalMixSamples final_samples;
+
+ SourceStatus source_statuses;
+
+ Compressor compressor;
+
+ DspConfiguration dsp_configuration;
+
+ IntermediateMixSamples intermediate_mix_samples;
+
+ SourceConfiguration source_configurations;
+
+ AdpcmCoefficients adpcm_coefficients;
+
+ struct {
+ INSERT_PADDING_DSPWORDS(0x100);
+ } unknown10;
+
+ struct {
+ INSERT_PADDING_DSPWORDS(0xC0);
+ } unknown11;
+
+ struct {
+ INSERT_PADDING_DSPWORDS(0x180);
+ } unknown12;
+
+ struct {
+ INSERT_PADDING_DSPWORDS(0xA);
+ } unknown13;
+
+ struct {
+ INSERT_PADDING_DSPWORDS(0x13A3);
+ } unknown14;
+
+ u16_le frame_counter;
+};
+ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
+
+// Structures must have an offset that is a multiple of two.
+static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
+
+#undef INSERT_PADDING_DSPWORDS
+#undef ASSERT_DSP_STRUCT
+
+/// Initialize DSP hardware
+void Init();
+
+/// Shutdown DSP hardware
+void Shutdown();
+
+/**
+ * Perform processing and updates state of current shared memory buffer.
+ * This function is called every audio tick before triggering the audio interrupt.
+ * @return Whether an audio interrupt should be triggered this frame.
+ */
+bool Tick();
+
+/// Returns a mutable reference to the current region. Current region is selected based on the frame counter.
+SharedMemory& CurrentRegion();
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp
new file mode 100644
index 000000000..9381883b4
--- /dev/null
+++ b/src/audio_core/hle/pipe.cpp
@@ -0,0 +1,159 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <vector>
+
+#include "audio_core/hle/dsp.h"
+#include "audio_core/hle/pipe.h"
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+
+namespace DSP {
+namespace HLE {
+
+static DspState dsp_state = DspState::Off;
+
+static std::array<std::vector<u8>, static_cast<size_t>(DspPipe::DspPipe_MAX)> pipe_data;
+
+void ResetPipes() {
+ for (auto& data : pipe_data) {
+ data.clear();
+ }
+ dsp_state = DspState::Off;
+}
+
+std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
+ if (pipe_number >= DspPipe::DspPipe_MAX) {
+ LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number);
+ return {};
+ }
+
+ std::vector<u8>& data = pipe_data[static_cast<size_t>(pipe_number)];
+
+ if (length > data.size()) {
+ LOG_WARNING(Audio_DSP, "pipe_number = %u is out of data, application requested read of %u but %zu remain",
+ pipe_number, length, data.size());
+ length = data.size();
+ }
+
+ if (length == 0)
+ return {};
+
+ std::vector<u8> ret(data.begin(), data.begin() + length);
+ data.erase(data.begin(), data.begin() + length);
+ return ret;
+}
+
+size_t GetPipeReadableSize(DspPipe pipe_number) {
+ if (pipe_number >= DspPipe::DspPipe_MAX) {
+ LOG_ERROR(Audio_DSP, "pipe_number = %u invalid", pipe_number);
+ return 0;
+ }
+
+ return pipe_data[static_cast<size_t>(pipe_number)].size();
+}
+
+static void WriteU16(DspPipe pipe_number, u16 value) {
+ std::vector<u8>& data = pipe_data[static_cast<size_t>(pipe_number)];
+ // Little endian
+ data.emplace_back(value & 0xFF);
+ data.emplace_back(value >> 8);
+}
+
+static void AudioPipeWriteStructAddresses() {
+ // These struct addresses are DSP dram addresses.
+ // See also: DSP_DSP::ConvertProcessAddressFromDspDram
+ static const std::array<u16, 15> struct_addresses = {
+ 0x8000 + offsetof(SharedMemory, frame_counter) / 2,
+ 0x8000 + offsetof(SharedMemory, source_configurations) / 2,
+ 0x8000 + offsetof(SharedMemory, source_statuses) / 2,
+ 0x8000 + offsetof(SharedMemory, adpcm_coefficients) / 2,
+ 0x8000 + offsetof(SharedMemory, dsp_configuration) / 2,
+ 0x8000 + offsetof(SharedMemory, dsp_status) / 2,
+ 0x8000 + offsetof(SharedMemory, final_samples) / 2,
+ 0x8000 + offsetof(SharedMemory, intermediate_mix_samples) / 2,
+ 0x8000 + offsetof(SharedMemory, compressor) / 2,
+ 0x8000 + offsetof(SharedMemory, dsp_debug) / 2,
+ 0x8000 + offsetof(SharedMemory, unknown10) / 2,
+ 0x8000 + offsetof(SharedMemory, unknown11) / 2,
+ 0x8000 + offsetof(SharedMemory, unknown12) / 2,
+ 0x8000 + offsetof(SharedMemory, unknown13) / 2,
+ 0x8000 + offsetof(SharedMemory, unknown14) / 2
+ };
+
+ // Begin with a u16 denoting the number of structs.
+ WriteU16(DspPipe::Audio, struct_addresses.size());
+ // Then write the struct addresses.
+ for (u16 addr : struct_addresses) {
+ WriteU16(DspPipe::Audio, addr);
+ }
+}
+
+void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
+ switch (pipe_number) {
+ case DspPipe::Audio: {
+ if (buffer.size() != 4) {
+ LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", buffer.size());
+ return;
+ }
+
+ enum class StateChange {
+ Initalize = 0,
+ Shutdown = 1,
+ Wakeup = 2,
+ Sleep = 3
+ };
+
+ // The difference between Initialize and Wakeup is that Input state is maintained
+ // when sleeping but isn't when turning it off and on again. (TODO: Implement this.)
+ // Waking up from sleep garbles some of the structs in the memory region. (TODO:
+ // Implement this.) Applications store away the state of these structs before
+ // sleeping and reset it back after wakeup on behalf of the DSP.
+
+ switch (static_cast<StateChange>(buffer[0])) {
+ case StateChange::Initalize:
+ LOG_INFO(Audio_DSP, "Application has requested initialization of DSP hardware");
+ ResetPipes();
+ AudioPipeWriteStructAddresses();
+ dsp_state = DspState::On;
+ break;
+ case StateChange::Shutdown:
+ LOG_INFO(Audio_DSP, "Application has requested shutdown of DSP hardware");
+ dsp_state = DspState::Off;
+ break;
+ case StateChange::Wakeup:
+ LOG_INFO(Audio_DSP, "Application has requested wakeup of DSP hardware");
+ ResetPipes();
+ AudioPipeWriteStructAddresses();
+ dsp_state = DspState::On;
+ break;
+ case StateChange::Sleep:
+ LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware");
+ UNIMPLEMENTED();
+ dsp_state = DspState::Sleeping;
+ break;
+ default:
+ LOG_ERROR(Audio_DSP, "Application has requested unknown state transition of DSP hardware %hhu", buffer[0]);
+ dsp_state = DspState::Off;
+ break;
+ }
+
+ return;
+ }
+ default:
+ LOG_CRITICAL(Audio_DSP, "pipe_number = %u unimplemented", pipe_number);
+ UNIMPLEMENTED();
+ return;
+ }
+}
+
+DspState GetDspState() {
+ return dsp_state;
+}
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h
new file mode 100644
index 000000000..382d35e87
--- /dev/null
+++ b/src/audio_core/hle/pipe.h
@@ -0,0 +1,57 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace DSP {
+namespace HLE {
+
+/// Reset the pipes by setting pipe positions back to the beginning.
+void ResetPipes();
+
+enum class DspPipe {
+ Debug = 0,
+ Dma = 1,
+ Audio = 2,
+ Binary = 3,
+ DspPipe_MAX
+};
+
+/**
+ * Read a DSP pipe.
+ * @param pipe_number The Pipe ID
+ * @param length How much data to request.
+ * @return The data read from the pipe. The size of this vector can be less than the length requested.
+ */
+std::vector<u8> PipeRead(DspPipe pipe_number, u32 length);
+
+/**
+ * How much data is left in pipe
+ * @param pipe_number The Pipe ID
+ * @return The amount of data remaning in the pipe. This is the maximum length PipeRead will return.
+ */
+size_t GetPipeReadableSize(DspPipe pipe_number);
+
+/**
+ * Write to a DSP pipe.
+ * @param pipe_number The Pipe ID
+ * @param buffer The data to write to the pipe.
+ */
+void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer);
+
+enum class DspState {
+ Off,
+ On,
+ Sleeping
+};
+/// Get the state of the DSP
+DspState GetDspState();
+
+} // namespace HLE
+} // namespace DSP
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
new file mode 100644
index 000000000..cad21a85e
--- /dev/null
+++ b/src/audio_core/sink.h
@@ -0,0 +1,34 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace AudioCore {
+
+/**
+ * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output.
+ * Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs.
+ */
+class Sink {
+public:
+ virtual ~Sink() = default;
+
+ /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec)
+ virtual unsigned GetNativeSampleRate() const = 0;
+
+ /**
+ * Feed stereo samples to sink.
+ * @param samples Samples in interleaved stereo PCM16 format. Size of vector must be multiple of two.
+ */
+ virtual void EnqueueSamples(const std::vector<s16>& samples) = 0;
+
+ /// Samples enqueued that have not been played yet.
+ virtual std::size_t SamplesInQueue() const = 0;
+};
+
+} // namespace
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt
index e7f8a17f9..fa615deb9 100644
--- a/src/citra/CMakeLists.txt
+++ b/src/citra/CMakeLists.txt
@@ -1,11 +1,11 @@
set(SRCS
- emu_window/emu_window_glfw.cpp
+ emu_window/emu_window_sdl2.cpp
citra.cpp
config.cpp
citra.rc
)
set(HEADERS
- emu_window/emu_window_glfw.h
+ emu_window/emu_window_sdl2.h
config.h
default_ini.h
resource.h
@@ -13,12 +13,11 @@ set(HEADERS
create_directory_groups(${SRCS} ${HEADERS})
-include_directories(${GLFW_INCLUDE_DIRS})
-link_directories(${GLFW_LIBRARY_DIRS})
+include_directories(${SDL2_INCLUDE_DIR})
add_executable(citra ${SRCS} ${HEADERS})
-target_link_libraries(citra core video_core common)
-target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih glad)
+target_link_libraries(citra core video_core audio_core common)
+target_link_libraries(citra ${SDL2_LIBRARY} ${OPENGL_gl_LIBRARY} inih glad)
if (MSVC)
target_link_libraries(citra getopt)
endif()
@@ -27,3 +26,13 @@ target_link_libraries(citra ${PLATFORM_LIBRARIES})
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
install(TARGETS citra RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
endif()
+
+if (MSVC)
+ include(WindowsCopyFiles)
+
+ set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
+
+ windows_copy_files(citra ${SDL2_DLL_DIR} ${DLL_DEST} SDL2.dll)
+
+ unset(DLL_DEST)
+endif()
diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp
index c96fc1374..415b98a05 100644
--- a/src/citra/citra.cpp
+++ b/src/citra/citra.cpp
@@ -27,7 +27,7 @@
#include "core/loader/loader.h"
#include "citra/config.h"
-#include "citra/emu_window/emu_window_glfw.h"
+#include "citra/emu_window/emu_window_sdl2.h"
#include "video_core/video_core.h"
@@ -76,7 +76,7 @@ int main(int argc, char **argv) {
GDBStub::ToggleServer(Settings::values.use_gdbstub);
GDBStub::SetServerPort(static_cast<u32>(Settings::values.gdbstub_port));
- EmuWindow_GLFW* emu_window = new EmuWindow_GLFW;
+ EmuWindow_SDL2* emu_window = new EmuWindow_SDL2;
VideoCore::g_hw_renderer_enabled = Settings::values.use_hw_renderer;
VideoCore::g_shader_jit_enabled = Settings::values.use_shader_jit;
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 2f13c29a2..9034b188e 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -2,14 +2,15 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
#include <inih/cpp/INIReader.h>
+#include <SDL.h>
+
#include "citra/default_ini.h"
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "common/make_unique.h"
#include "core/settings.h"
@@ -17,21 +18,22 @@
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- glfw_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "glfw-config.ini";
- glfw_config = new INIReader(glfw_config_loc);
+ sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini";
+ sdl2_config = Common::make_unique<INIReader>(sdl2_config_loc);
Reload();
}
-bool Config::LoadINI(INIReader* config, const char* location, const std::string& default_contents, bool retry) {
- if (config->ParseError() < 0) {
+bool Config::LoadINI(const std::string& default_contents, bool retry) {
+ const char* location = this->sdl2_config_loc.c_str();
+ if (sdl2_config->ParseError() < 0) {
if (retry) {
LOG_WARNING(Config, "Failed to load %s. Creating file from defaults...", location);
FileUtil::CreateFullPath(location);
FileUtil::WriteStringToFile(true, default_contents, location);
- *config = INIReader(location); // Reopen file
+ sdl2_config = Common::make_unique<INIReader>(location); // Reopen file
- return LoadINI(config, location, default_contents, false);
+ return LoadINI(default_contents, false);
}
LOG_ERROR(Config, "Failed.");
return false;
@@ -41,51 +43,47 @@ bool Config::LoadINI(INIReader* config, const char* location, const std::string&
}
static const std::array<int, Settings::NativeInput::NUM_INPUTS> defaults = {
- GLFW_KEY_A, GLFW_KEY_S, GLFW_KEY_Z, GLFW_KEY_X,
- GLFW_KEY_Q, GLFW_KEY_W, GLFW_KEY_1, GLFW_KEY_2,
- GLFW_KEY_M, GLFW_KEY_N, GLFW_KEY_B,
- GLFW_KEY_T, GLFW_KEY_G, GLFW_KEY_F, GLFW_KEY_H,
- GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_KEY_LEFT, GLFW_KEY_RIGHT,
- GLFW_KEY_I, GLFW_KEY_K, GLFW_KEY_J, GLFW_KEY_L
+ SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X,
+ SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_1, SDL_SCANCODE_2,
+ SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_B,
+ SDL_SCANCODE_T, SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H,
+ SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,
+ SDL_SCANCODE_I, SDL_SCANCODE_K, SDL_SCANCODE_J, SDL_SCANCODE_L
};
void Config::ReadValues() {
// Controls
for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
Settings::values.input_mappings[Settings::NativeInput::All[i]] =
- glfw_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
+ sdl2_config->GetInteger("Controls", Settings::NativeInput::Mapping[i], defaults[i]);
}
// Core
- Settings::values.frame_skip = glfw_config->GetInteger("Core", "frame_skip", 0);
+ Settings::values.frame_skip = sdl2_config->GetInteger("Core", "frame_skip", 0);
// Renderer
- Settings::values.use_hw_renderer = glfw_config->GetBoolean("Renderer", "use_hw_renderer", false);
- Settings::values.use_shader_jit = glfw_config->GetBoolean("Renderer", "use_shader_jit", true);
+ Settings::values.use_hw_renderer = sdl2_config->GetBoolean("Renderer", "use_hw_renderer", false);
+ Settings::values.use_shader_jit = sdl2_config->GetBoolean("Renderer", "use_shader_jit", true);
- Settings::values.bg_red = (float)glfw_config->GetReal("Renderer", "bg_red", 1.0);
- Settings::values.bg_green = (float)glfw_config->GetReal("Renderer", "bg_green", 1.0);
- Settings::values.bg_blue = (float)glfw_config->GetReal("Renderer", "bg_blue", 1.0);
+ Settings::values.bg_red = (float)sdl2_config->GetReal("Renderer", "bg_red", 1.0);
+ Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0);
+ Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0);
// Data Storage
- Settings::values.use_virtual_sd = glfw_config->GetBoolean("Data Storage", "use_virtual_sd", true);
+ Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
// System Region
- Settings::values.region_value = glfw_config->GetInteger("System Region", "region_value", 1);
+ Settings::values.region_value = sdl2_config->GetInteger("System Region", "region_value", 1);
// Miscellaneous
- Settings::values.log_filter = glfw_config->Get("Miscellaneous", "log_filter", "*:Info");
+ Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info");
// Debugging
- Settings::values.use_gdbstub = glfw_config->GetBoolean("Debugging", "use_gdbstub", false);
- Settings::values.gdbstub_port = glfw_config->GetInteger("Debugging", "gdbstub_port", 24689);
+ Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
+ Settings::values.gdbstub_port = sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689);
}
void Config::Reload() {
- LoadINI(glfw_config, glfw_config_loc.c_str(), DefaultINI::glfw_config_file);
+ LoadINI(DefaultINI::sdl2_config_file);
ReadValues();
}
-
-Config::~Config() {
- delete glfw_config;
-}
diff --git a/src/citra/config.h b/src/citra/config.h
index c326ec669..52a478146 100644
--- a/src/citra/config.h
+++ b/src/citra/config.h
@@ -4,19 +4,19 @@
#pragma once
+#include <memory>
#include <string>
-class INIReader;
+#include <inih/cpp/INIReader.h>
class Config {
- INIReader* glfw_config;
- std::string glfw_config_loc;
+ std::unique_ptr<INIReader> sdl2_config;
+ std::string sdl2_config_loc;
- bool LoadINI(INIReader* config, const char* location, const std::string& default_contents="", bool retry=true);
+ bool LoadINI(const std::string& default_contents="", bool retry=true);
void ReadValues();
public:
Config();
- ~Config();
void Reload();
};
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 5ba40a8ed..c9b490a00 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -6,7 +6,7 @@
namespace DefaultINI {
-const char* glfw_config_file = R"(
+const char* sdl2_config_file = R"(
[Controls]
pad_start =
pad_select =
diff --git a/src/citra/emu_window/emu_window_glfw.cpp b/src/citra/emu_window/emu_window_glfw.cpp
deleted file mode 100644
index 9453b1f48..000000000
--- a/src/citra/emu_window/emu_window_glfw.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <cstdlib>
-#include <string>
-
-// Let’s use our own GL header, instead of one from GLFW.
-#include <glad/glad.h>
-#define GLFW_INCLUDE_NONE
-#include <GLFW/glfw3.h>
-
-#include "common/assert.h"
-#include "common/key_map.h"
-#include "common/logging/log.h"
-#include "common/scm_rev.h"
-#include "common/string_util.h"
-
-#include "video_core/video_core.h"
-
-#include "core/settings.h"
-#include "core/hle/service/hid/hid.h"
-
-#include "citra/emu_window/emu_window_glfw.h"
-
-EmuWindow_GLFW* EmuWindow_GLFW::GetEmuWindow(GLFWwindow* win) {
- return static_cast<EmuWindow_GLFW*>(glfwGetWindowUserPointer(win));
-}
-
-void EmuWindow_GLFW::OnMouseButtonEvent(GLFWwindow* win, int button, int action, int mods) {
- if (button == GLFW_MOUSE_BUTTON_LEFT) {
- auto emu_window = GetEmuWindow(win);
- auto layout = emu_window->GetFramebufferLayout();
- double x, y;
- glfwGetCursorPos(win, &x, &y);
-
- if (action == GLFW_PRESS)
- emu_window->TouchPressed(static_cast<unsigned>(x), static_cast<unsigned>(y));
- else if (action == GLFW_RELEASE)
- emu_window->TouchReleased();
- }
-}
-
-void EmuWindow_GLFW::OnCursorPosEvent(GLFWwindow* win, double x, double y) {
- GetEmuWindow(win)->TouchMoved(static_cast<unsigned>(std::max(x, 0.0)), static_cast<unsigned>(std::max(y, 0.0)));
-}
-
-/// Called by GLFW when a key event occurs
-void EmuWindow_GLFW::OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods) {
- auto emu_window = GetEmuWindow(win);
- int keyboard_id = emu_window->keyboard_id;
-
- if (action == GLFW_PRESS) {
- emu_window->KeyPressed({key, keyboard_id});
- } else if (action == GLFW_RELEASE) {
- emu_window->KeyReleased({key, keyboard_id});
- }
-}
-
-/// Whether the window is still open, and a close request hasn't yet been sent
-const bool EmuWindow_GLFW::IsOpen() {
- return glfwWindowShouldClose(m_render_window) == 0;
-}
-
-void EmuWindow_GLFW::OnFramebufferResizeEvent(GLFWwindow* win, int width, int height) {
- GetEmuWindow(win)->NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
-}
-
-void EmuWindow_GLFW::OnClientAreaResizeEvent(GLFWwindow* win, int width, int height) {
- // NOTE: GLFW provides no proper way to set a minimal window size.
- // Hence, we just ignore the corresponding EmuWindow hint.
- OnFramebufferResizeEvent(win, width, height);
-}
-
-/// EmuWindow_GLFW constructor
-EmuWindow_GLFW::EmuWindow_GLFW() {
- keyboard_id = KeyMap::NewDeviceId();
-
- ReloadSetKeymaps();
-
- glfwSetErrorCallback([](int error, const char *desc){
- LOG_ERROR(Frontend, "GLFW 0x%08x: %s", error, desc);
- });
-
- // Initialize the window
- if(glfwInit() != GL_TRUE) {
- LOG_CRITICAL(Frontend, "Failed to initialize GLFW! Exiting...");
- exit(1);
- }
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- // GLFW on OSX requires these window hints to be set to create a 3.2+ GL context.
- glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-
- std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
- m_render_window = glfwCreateWindow(VideoCore::kScreenTopWidth,
- (VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight),
- window_title.c_str(), nullptr, nullptr);
-
- if (m_render_window == nullptr) {
- LOG_CRITICAL(Frontend, "Failed to create GLFW window! Exiting...");
- exit(1);
- }
-
- glfwSetWindowUserPointer(m_render_window, this);
-
- // Notify base interface about window state
- int width, height;
- glfwGetFramebufferSize(m_render_window, &width, &height);
- OnFramebufferResizeEvent(m_render_window, width, height);
-
- glfwGetWindowSize(m_render_window, &width, &height);
- OnClientAreaResizeEvent(m_render_window, width, height);
-
- // Setup callbacks
- glfwSetKeyCallback(m_render_window, OnKeyEvent);
- glfwSetMouseButtonCallback(m_render_window, OnMouseButtonEvent);
- glfwSetCursorPosCallback(m_render_window, OnCursorPosEvent);
- glfwSetFramebufferSizeCallback(m_render_window, OnFramebufferResizeEvent);
- glfwSetWindowSizeCallback(m_render_window, OnClientAreaResizeEvent);
-
- DoneCurrent();
-}
-
-/// EmuWindow_GLFW destructor
-EmuWindow_GLFW::~EmuWindow_GLFW() {
- glfwTerminate();
-}
-
-/// Swap buffers to display the next frame
-void EmuWindow_GLFW::SwapBuffers() {
- glfwSwapBuffers(m_render_window);
-}
-
-/// Polls window events
-void EmuWindow_GLFW::PollEvents() {
- glfwPollEvents();
-}
-
-/// Makes the GLFW OpenGL context current for the caller thread
-void EmuWindow_GLFW::MakeCurrent() {
- glfwMakeContextCurrent(m_render_window);
-}
-
-/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
-void EmuWindow_GLFW::DoneCurrent() {
- glfwMakeContextCurrent(nullptr);
-}
-
-void EmuWindow_GLFW::ReloadSetKeymaps() {
- for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
- KeyMap::SetKeyMapping({Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id}, Service::HID::pad_mapping[i]);
- }
-}
-
-void EmuWindow_GLFW::OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) {
- std::pair<int,int> current_size;
- glfwGetWindowSize(m_render_window, &current_size.first, &current_size.second);
-
- DEBUG_ASSERT((int)minimal_size.first > 0 && (int)minimal_size.second > 0);
- int new_width = std::max(current_size.first, (int)minimal_size.first);
- int new_height = std::max(current_size.second, (int)minimal_size.second);
-
- if (current_size != std::make_pair(new_width, new_height))
- glfwSetWindowSize(m_render_window, new_width, new_height);
-}
diff --git a/src/citra/emu_window/emu_window_glfw.h b/src/citra/emu_window/emu_window_glfw.h
deleted file mode 100644
index 7ccd5e6aa..000000000
--- a/src/citra/emu_window/emu_window_glfw.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <utility>
-
-#include "common/emu_window.h"
-
-struct GLFWwindow;
-
-class EmuWindow_GLFW : public EmuWindow {
-public:
- EmuWindow_GLFW();
- ~EmuWindow_GLFW();
-
- /// Swap buffers to display the next frame
- void SwapBuffers() override;
-
- /// Polls window events
- void PollEvents() override;
-
- /// Makes the graphics context current for the caller thread
- void MakeCurrent() override;
-
- /// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
- void DoneCurrent() override;
-
- static void OnKeyEvent(GLFWwindow* win, int key, int scancode, int action, int mods);
-
- static void OnMouseButtonEvent(GLFWwindow* window, int button, int action, int mods);
-
- static void OnCursorPosEvent(GLFWwindow* window, double x, double y);
-
- /// Whether the window is still open, and a close request hasn't yet been sent
- const bool IsOpen();
-
- static void OnClientAreaResizeEvent(GLFWwindow* win, int width, int height);
-
- static void OnFramebufferResizeEvent(GLFWwindow* win, int width, int height);
-
- void ReloadSetKeymaps() override;
-
-private:
- void OnMinimalClientAreaChangeRequest(const std::pair<unsigned,unsigned>& minimal_size) override;
-
- static EmuWindow_GLFW* GetEmuWindow(GLFWwindow* win);
-
- GLFWwindow* m_render_window; ///< Internal GLFW render window
-
- /// Device id of keyboard for use with KeyMap
- int keyboard_id;
-};
diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp
new file mode 100644
index 000000000..1fed82e78
--- /dev/null
+++ b/src/citra/emu_window/emu_window_sdl2.cpp
@@ -0,0 +1,167 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+
+#define SDL_MAIN_HANDLED
+#include <SDL.h>
+
+#include "common/key_map.h"
+#include "common/logging/log.h"
+#include "common/scm_rev.h"
+#include "common/string_util.h"
+
+#include "core/settings.h"
+#include "core/hle/service/hid/hid.h"
+
+#include "citra/emu_window/emu_window_sdl2.h"
+
+#include "video_core/video_core.h"
+
+void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
+ TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
+}
+
+void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
+ if (button != SDL_BUTTON_LEFT)
+ return;
+
+ if (state == SDL_PRESSED) {
+ TouchPressed((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
+ } else {
+ TouchReleased();
+ }
+}
+
+void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
+ if (state == SDL_PRESSED) {
+ KeyPressed({ key, keyboard_id });
+ } else if (state == SDL_RELEASED) {
+ KeyReleased({ key, keyboard_id });
+ }
+}
+
+bool EmuWindow_SDL2::IsOpen() const {
+ return is_open;
+}
+
+void EmuWindow_SDL2::OnResize() {
+ int width, height;
+
+ SDL_GetWindowSize(render_window, &width, &height);
+
+ NotifyFramebufferLayoutChanged(EmuWindow::FramebufferLayout::DefaultScreenLayout(width, height));
+}
+
+EmuWindow_SDL2::EmuWindow_SDL2() {
+ keyboard_id = KeyMap::NewDeviceId();
+
+ ReloadSetKeymaps();
+
+ SDL_SetMainReady();
+
+ // Initialize the window
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
+ exit(1);
+ }
+
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
+ render_window = SDL_CreateWindow(window_title.c_str(),
+ SDL_WINDOWPOS_UNDEFINED, // x position
+ SDL_WINDOWPOS_UNDEFINED, // y position
+ VideoCore::kScreenTopWidth,
+ VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight,
+ SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
+
+ if (render_window == nullptr) {
+ LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting...");
+ exit(1);
+ }
+
+ gl_context = SDL_GL_CreateContext(render_window);
+
+ if (gl_context == nullptr) {
+ LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting...");
+ exit(1);
+ }
+
+ OnResize();
+ OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
+ SDL_PumpEvents();
+
+ DoneCurrent();
+}
+
+EmuWindow_SDL2::~EmuWindow_SDL2() {
+ SDL_GL_DeleteContext(gl_context);
+ SDL_Quit();
+}
+
+void EmuWindow_SDL2::SwapBuffers() {
+ SDL_GL_SwapWindow(render_window);
+}
+
+void EmuWindow_SDL2::PollEvents() {
+ SDL_Event event;
+
+ // SDL_PollEvent returns 0 when there are no more events in the event queue
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_WINDOWEVENT:
+ switch (event.window.event) {
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ case SDL_WINDOWEVENT_RESIZED:
+ case SDL_WINDOWEVENT_MAXIMIZED:
+ case SDL_WINDOWEVENT_RESTORED:
+ case SDL_WINDOWEVENT_MINIMIZED:
+ OnResize();
+ break;
+ case SDL_WINDOWEVENT_CLOSE:
+ is_open = false;
+ break;
+ }
+ break;
+ case SDL_KEYDOWN:
+ case SDL_KEYUP:
+ OnKeyEvent(static_cast<int>(event.key.keysym.scancode), event.key.state);
+ break;
+ case SDL_MOUSEMOTION:
+ OnMouseMotion(event.motion.x, event.motion.y);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ case SDL_MOUSEBUTTONUP:
+ OnMouseButton(event.button.button, event.button.state, event.button.x, event.button.y);
+ break;
+ case SDL_QUIT:
+ is_open = false;
+ break;
+ }
+ }
+}
+
+void EmuWindow_SDL2::MakeCurrent() {
+ SDL_GL_MakeCurrent(render_window, gl_context);
+}
+
+void EmuWindow_SDL2::DoneCurrent() {
+ SDL_GL_MakeCurrent(render_window, nullptr);
+}
+
+void EmuWindow_SDL2::ReloadSetKeymaps() {
+ for (int i = 0; i < Settings::NativeInput::NUM_INPUTS; ++i) {
+ KeyMap::SetKeyMapping({ Settings::values.input_mappings[Settings::NativeInput::All[i]], keyboard_id }, Service::HID::pad_mapping[i]);
+ }
+}
+
+void EmuWindow_SDL2::OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) {
+ SDL_SetWindowMinimumSize(render_window, minimal_size.first, minimal_size.second);
+}
diff --git a/src/citra/emu_window/emu_window_sdl2.h b/src/citra/emu_window/emu_window_sdl2.h
new file mode 100644
index 000000000..77279f022
--- /dev/null
+++ b/src/citra/emu_window/emu_window_sdl2.h
@@ -0,0 +1,64 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <utility>
+
+#include "common/emu_window.h"
+
+struct SDL_Window;
+
+class EmuWindow_SDL2 : public EmuWindow {
+public:
+ EmuWindow_SDL2();
+ ~EmuWindow_SDL2();
+
+ /// Swap buffers to display the next frame
+ void SwapBuffers() override;
+
+ /// Polls window events
+ void PollEvents() override;
+
+ /// Makes the graphics context current for the caller thread
+ void MakeCurrent() override;
+
+ /// Releases the GL context from the caller thread
+ void DoneCurrent() override;
+
+ /// Whether the window is still open, and a close request hasn't yet been sent
+ bool IsOpen() const;
+
+ /// Load keymap from configuration
+ void ReloadSetKeymaps() override;
+
+private:
+ /// Called by PollEvents when a key is pressed or released.
+ void OnKeyEvent(int key, u8 state);
+
+ /// Called by PollEvents when the mouse moves.
+ void OnMouseMotion(s32 x, s32 y);
+
+ /// Called by PollEvents when a mouse button is pressed or released
+ void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
+
+ /// Called by PollEvents when any event that may cause the window to be resized occurs
+ void OnResize();
+
+ /// Called when a configuration change affects the minimal size of the window
+ void OnMinimalClientAreaChangeRequest(const std::pair<unsigned, unsigned>& minimal_size) override;
+
+ /// Is the window still open?
+ bool is_open = true;
+
+ /// Internal SDL2 render window
+ SDL_Window* render_window;
+
+ using SDL_GLContext = void *;
+ /// The OpenGL context associated with the window
+ SDL_GLContext gl_context;
+
+ /// Device id of keyboard for use with KeyMap
+ int keyboard_id;
+};
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index bbf6ae001..9b3eb2cd6 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -79,7 +79,7 @@ if (APPLE)
else()
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS})
endif()
-target_link_libraries(citra-qt core video_core common qhexedit)
+target_link_libraries(citra-qt core video_core audio_core common qhexedit)
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
@@ -88,9 +88,14 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
endif()
if (Qt5_FOUND AND MSVC)
+ include(WindowsCopyFiles)
+
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
- set(Qt5_DLLS
+ set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
+ set(PLATFORMS ${DLL_DEST}platforms/)
+
+ windows_copy_files(citra-qt ${Qt5_DLL_DIR} ${DLL_DEST}
icudt*.dll
icuin*.dll
icuuc*.dll
@@ -99,24 +104,8 @@ if (Qt5_FOUND AND MSVC)
Qt5OpenGL$<$<CONFIG:Debug>:d>.*
Qt5Widgets$<$<CONFIG:Debug>:d>.*
)
- set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
- set(PLATFORMS ${DLL_DEST}platforms/)
+ windows_copy_files(citra-qt ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
- # windows commandline expects the / to be \ so switch them
- string(REPLACE "/" "\\\\" Qt5_DLL_DIR ${Qt5_DLL_DIR})
- string(REPLACE "/" "\\\\" Qt5_PLATFORMS_DIR ${Qt5_PLATFORMS_DIR})
- string(REPLACE "/" "\\\\" DLL_DEST ${DLL_DEST})
- string(REPLACE "/" "\\\\" PLATFORMS ${PLATFORMS})
-
- # /NJH /NJS /NDL /NFL /NC /NS /NP - Silence any output
- # cmake adds an extra check for command success which doesn't work too well with robocopy
- # so trick it into thinking the command was successful with the || cmd /c "exit /b 0"
- add_custom_command(TARGET citra-qt POST_BUILD
- COMMAND robocopy ${Qt5_DLL_DIR} ${DLL_DEST} ${Qt5_DLLS} /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
- COMMAND if not exist ${PLATFORMS} mkdir ${PLATFORMS} 2> nul
- COMMAND robocopy ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.* /NJH /NJS /NDL /NFL /NC /NS /NP || cmd /c "exit /b 0"
- )
- unset(Qt5_DLLS)
unset(Qt5_DLL_DIR)
unset(Qt5_PLATFORMS_DIR)
unset(DLL_DEST)
diff --git a/src/citra_qt/debugger/callstack.cpp b/src/citra_qt/debugger/callstack.cpp
index d45eed179..793944639 100644
--- a/src/citra_qt/debugger/callstack.cpp
+++ b/src/citra_qt/debugger/callstack.cpp
@@ -29,18 +29,16 @@ CallstackWidget::CallstackWidget(QWidget* parent): QDockWidget(parent)
void CallstackWidget::OnDebugModeEntered()
{
- ARM_Interface* app_core = Core::g_app_core;
-
- u32 sp = app_core->GetReg(13); //stack pointer
- u32 ret_addr, call_addr, func_addr;
+ // Stack pointer
+ const u32 sp = Core::g_app_core->GetReg(13);
Clear();
int counter = 0;
for (u32 addr = 0x10000000; addr >= sp; addr -= 4)
{
- ret_addr = Memory::Read32(addr);
- call_addr = ret_addr - 4; //get call address???
+ const u32 ret_addr = Memory::Read32(addr);
+ const u32 call_addr = ret_addr - 4; //get call address???
if (Memory::GetPointer(call_addr) == nullptr)
break;
@@ -60,7 +58,7 @@ void CallstackWidget::OnDebugModeEntered()
// Pre-compute the left-shift and the prefetch offset
i_offset <<= 2;
i_offset += 8;
- func_addr = call_addr + i_offset;
+ const u32 func_addr = call_addr + i_offset;
callstack_model->setItem(counter, 0, new QStandardItem(QString("0x%1").arg(addr, 8, 16, QLatin1Char('0'))));
callstack_model->setItem(counter, 1, new QStandardItem(QString("0x%1").arg(ret_addr, 8, 16, QLatin1Char('0'))));
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp
index ab97c8d2d..5186d2b44 100644
--- a/src/citra_qt/debugger/graphics_cmdlists.cpp
+++ b/src/citra_qt/debugger/graphics_cmdlists.cpp
@@ -21,6 +21,7 @@
#include "common/vector_math.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/debug_utils/debug_utils.h"
QImage LoadTexture(u8* src, const Pica::DebugUtils::TextureInfo& info) {
diff --git a/src/citra_qt/debugger/graphics_framebuffer.cpp b/src/citra_qt/debugger/graphics_framebuffer.cpp
index 80b32eaff..c30e75933 100644
--- a/src/citra_qt/debugger/graphics_framebuffer.cpp
+++ b/src/citra_qt/debugger/graphics_framebuffer.cpp
@@ -18,6 +18,7 @@
#include "core/hw/gpu.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/utils.h"
GraphicsFramebufferWidget::GraphicsFramebufferWidget(std::shared_ptr<Pica::DebugContext> debug_context,
diff --git a/src/citra_qt/debugger/graphics_tracing.cpp b/src/citra_qt/debugger/graphics_tracing.cpp
index b0bc782df..e06498744 100644
--- a/src/citra_qt/debugger/graphics_tracing.cpp
+++ b/src/citra_qt/debugger/graphics_tracing.cpp
@@ -22,7 +22,7 @@
#include "nihstro/float24.h"
#include "video_core/pica.h"
-
+#include "video_core/pica_state.h"
GraphicsTracingWidget::GraphicsTracingWidget(std::shared_ptr<Pica::DebugContext> debug_context,
QWidget* parent)
diff --git a/src/citra_qt/debugger/graphics_vertex_shader.cpp b/src/citra_qt/debugger/graphics_vertex_shader.cpp
index a5a5fe6b0..4b676f1b1 100644
--- a/src/citra_qt/debugger/graphics_vertex_shader.cpp
+++ b/src/citra_qt/debugger/graphics_vertex_shader.cpp
@@ -19,6 +19,8 @@
#include "citra_qt/debugger/graphics_vertex_shader.h"
#include "citra_qt/util/util.h"
+#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/shader/shader.h"
using nihstro::OpCode;
@@ -496,8 +498,8 @@ void GraphicsVertexShaderWidget::Reload(bool replace_vertex_data, void* vertex_d
// Reload widget state
for (int attr = 0; attr < num_attributes; ++attr) {
unsigned source_attr = shader_config.input_register_map.GetRegisterForAttribute(attr);
- input_data_mapping[source_attr]->setText(QString("-> v%1").arg(attr));
- input_data_container[source_attr]->setVisible(true);
+ input_data_mapping[attr]->setText(QString("-> v%1").arg(source_attr));
+ input_data_container[attr]->setVisible(true);
}
// Only show input attributes which are used as input to the shader
for (unsigned int attr = num_attributes; attr < 16; ++attr) {
diff --git a/src/citra_qt/debugger/registers.cpp b/src/citra_qt/debugger/registers.cpp
index 6100d67c5..1bd0bfebc 100644
--- a/src/citra_qt/debugger/registers.cpp
+++ b/src/citra_qt/debugger/registers.cpp
@@ -59,16 +59,14 @@ RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
}
void RegistersWidget::OnDebugModeEntered() {
- ARM_Interface* app_core = Core::g_app_core;
-
- if (app_core == nullptr)
+ if (!Core::g_app_core)
return;
for (int i = 0; i < core_registers->childCount(); ++i)
- core_registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetReg(i), 8, 16, QLatin1Char('0')));
+ core_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetReg(i), 8, 16, QLatin1Char('0')));
for (int i = 0; i < vfp_registers->childCount(); ++i)
- vfp_registers->child(i)->setText(1, QString("0x%1").arg(app_core->GetVFPReg(i), 8, 16, QLatin1Char('0')));
+ vfp_registers->child(i)->setText(1, QString("0x%1").arg(Core::g_app_core->GetVFPReg(i), 8, 16, QLatin1Char('0')));
UpdateCPSRValues();
UpdateVFPSystemRegisterValues();
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index d6c27f0df..57adbc136 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <clocale>
#include <thread>
#include <QDesktopWidget>
@@ -171,6 +172,8 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
}
UpdateRecentFiles();
+ confirm_before_closing = settings.value("confirmClose", true).toBool();
+
// Setup connections
connect(game_list, SIGNAL(GameChosen(QString)), this, SLOT(OnGameListLoadFile(QString)));
connect(ui.action_Load_File, SIGNAL(triggered()), this, SLOT(OnMenuLoadFile()));
@@ -208,7 +211,7 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
show();
- game_list->PopulateAsync(settings.value("gameListRootDir").toString(), settings.value("gameListDeepScan").toBool());
+ game_list->PopulateAsync(settings.value("gameListRootDir", ".").toString(), settings.value("gameListDeepScan", false).toBool());
QStringList args = QApplication::arguments();
if (args.length() >= 2) {
@@ -246,22 +249,73 @@ void GMainWindow::OnDisplayTitleBars(bool show)
}
}
-void GMainWindow::BootGame(const std::string& filename) {
- LOG_INFO(Frontend, "Citra starting...");
-
+bool GMainWindow::InitializeSystem() {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
// Initialize the core emulation
- System::Init(render_window);
+ System::Result system_result = System::Init(render_window);
+ if (System::Result::Success != system_result) {
+ switch (system_result) {
+ case System::Result::ErrorInitVideoCore:
+ QMessageBox::critical(this, tr("Error while starting Citra!"),
+ tr("Failed to initialize the video core!\n\n"
+ "Please ensure that your GPU supports OpenGL 3.3 and that you have the latest graphics driver."));
+ break;
+
+ default:
+ QMessageBox::critical(this, tr("Error while starting Citra!"),
+ tr("Unknown error (please check the log)!"));
+ break;
+ }
+ return false;
+ }
+ return true;
+}
- // Load the game
- if (Loader::ResultStatus::Success != Loader::LoadFile(filename)) {
+bool GMainWindow::LoadROM(const std::string& filename) {
+ Loader::ResultStatus result = Loader::LoadFile(filename);
+ if (Loader::ResultStatus::Success != result) {
LOG_CRITICAL(Frontend, "Failed to load ROM!");
System::Shutdown();
- return;
+
+ switch (result) {
+ case Loader::ResultStatus::ErrorEncrypted: {
+ // Build the MessageBox ourselves to have clickable link
+ QMessageBox popup_error;
+ popup_error.setTextFormat(Qt::RichText);
+ popup_error.setWindowTitle(tr("Error while loading ROM!"));
+ popup_error.setText(tr("The game that you are trying to load must be decrypted before being used with Citra.<br/><br/>"
+ "For more information on dumping and decrypting games, please see: <a href='https://citra-emu.org/wiki/Dumping-Game-Cartridges'>https://citra-emu.org/wiki/Dumping-Game-Cartridges</a>"));
+ popup_error.setIcon(QMessageBox::Critical);
+ popup_error.exec();
+ break;
+ }
+ case Loader::ResultStatus::ErrorInvalidFormat:
+ QMessageBox::critical(this, tr("Error while loading ROM!"),
+ tr("The ROM format is not supported."));
+ break;
+ case Loader::ResultStatus::Error:
+
+ default:
+ QMessageBox::critical(this, tr("Error while loading ROM!"),
+ tr("Unknown error!"));
+ break;
+ }
+ return false;
}
+ return true;
+}
+
+void GMainWindow::BootGame(const std::string& filename) {
+ LOG_INFO(Frontend, "Citra starting...");
+
+ if (!InitializeSystem())
+ return;
+
+ if (!LoadROM(filename))
+ return;
// Create and start the emulation thread
emu_thread = Common::make_unique<EmuThread>(render_window);
@@ -497,7 +551,22 @@ void GMainWindow::OnConfigure() {
//GControllerConfigDialog* dialog = new GControllerConfigDialog(controller_ports, this);
}
+bool GMainWindow::ConfirmClose() {
+ if (emu_thread == nullptr || !confirm_before_closing)
+ return true;
+
+ auto answer = QMessageBox::question(this, tr("Citra"),
+ tr("Are you sure you want to close Citra?"),
+ QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ return answer != QMessageBox::No;
+}
+
void GMainWindow::closeEvent(QCloseEvent* event) {
+ if (!ConfirmClose()) {
+ event->ignore();
+ return;
+ }
+
// Save window layout
QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Citra team", "Citra");
@@ -512,6 +581,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
settings.setValue("singleWindowMode", ui.action_Single_Window_Mode->isChecked());
settings.setValue("displayTitleBars", ui.actionDisplay_widget_title_bars->isChecked());
settings.setValue("firstStart", false);
+ settings.setValue("confirmClose", confirm_before_closing);
game_list->SaveInterfaceLayout(settings);
SaveHotkeys(settings);
@@ -545,6 +615,9 @@ int main(int argc, char* argv[]) {
QApplication::setAttribute(Qt::AA_X11InitThreads);
QApplication app(argc, argv);
+ // Qt changes the locale and causes issues in float conversion using std::to_string() when generating shaders
+ setlocale(LC_ALL, "C");
+
GMainWindow main_window;
// After settings have been loaded by GMainWindow, apply the filter
log_filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index f6d429cd9..945aea0cd 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -59,6 +59,8 @@ signals:
void EmulationStopping();
private:
+ bool InitializeSystem();
+ bool LoadROM(const std::string& filename);
void BootGame(const std::string& filename);
void ShutdownGame();
@@ -82,6 +84,13 @@ private:
*/
void UpdateRecentFiles();
+ /**
+ * If the emulation is running,
+ * asks the user if he really want to close the emulator
+ *
+ * @return true if the user confirmed
+ */
+ bool ConfirmClose();
void closeEvent(QCloseEvent* event) override;
private slots:
@@ -122,6 +131,7 @@ private:
GPUCommandListWidget* graphicsCommandsWidget;
QAction* actions_recent_files[max_recent_files_item];
+ bool confirm_before_closing;
};
#endif // _CITRA_QT_MAIN_HXX_
diff --git a/src/common/bit_field.h b/src/common/bit_field.h
index 66689f398..371eb17a1 100644
--- a/src/common/bit_field.h
+++ b/src/common/bit_field.h
@@ -115,29 +115,24 @@ template<std::size_t position, std::size_t bits, typename T>
struct BitField
{
private:
- // This constructor might be considered ambiguous:
- // Would it initialize the storage or just the bitfield?
- // Hence, delete it. Use the assignment operator to set bitfield values!
- BitField(T val) = delete;
+ // We hide the copy assigment operator here, because the default copy
+ // assignment would copy the full storage value, rather than just the bits
+ // relevant to this particular bit field.
+ // We don't delete it because we want BitField to be trivially copyable.
+ BitField& operator=(const BitField&) = default;
public:
+ // This constructor and assignment operator might be considered ambiguous:
+ // Would they initialize the storage or just the bitfield?
+ // Hence, delete them. Use the Assign method to set bitfield values!
+ BitField(T val) = delete;
+ BitField& operator=(T val) = delete;
+
// Force default constructor to be created
// so that we can use this within unions
BitField() = default;
- // We explicitly delete the copy assigment operator here, because the
- // default copy assignment would copy the full storage value, rather than
- // just the bits relevant to this particular bit field.
- BitField& operator=(const BitField&) = delete;
-
- FORCE_INLINE BitField& operator=(T val)
- {
- Assign(val);
- return *this;
- }
-
- FORCE_INLINE operator T() const
- {
+ FORCE_INLINE operator T() const {
return Value();
}
@@ -145,8 +140,7 @@ public:
storage = (storage & ~GetMask()) | (((StorageType)value << position) & GetMask());
}
- FORCE_INLINE T Value() const
- {
+ FORCE_INLINE T Value() const {
if (std::numeric_limits<T>::is_signed)
{
std::size_t shift = 8 * sizeof(T)-bits;
@@ -159,8 +153,7 @@ public:
}
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
- FORCE_INLINE bool ToBool() const
- {
+ FORCE_INLINE bool ToBool() const {
return Value() != 0;
}
@@ -176,8 +169,7 @@ private:
// Unsigned version of StorageType
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
- FORCE_INLINE StorageType GetMask() const
- {
+ FORCE_INLINE StorageType GetMask() const {
return (((StorageTypeU)~0) >> (8 * sizeof(T)-bits)) << position;
}
@@ -189,6 +181,10 @@ private:
static_assert(position < 8 * sizeof(T), "Invalid position");
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
static_assert(bits > 0, "Invalid number of bits");
- static_assert(std::is_standard_layout<T>::value, "Invalid base type");
+ static_assert(std::is_pod<T>::value, "Invalid base type");
};
#pragma pack()
+
+#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER)
+static_assert(std::is_trivially_copyable<BitField<0, 1, u32>>::value, "BitField must be trivially copyable");
+#endif
diff --git a/src/common/color.h b/src/common/color.h
index eb199e308..908879ea6 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -11,42 +11,42 @@
namespace Color {
/// Convert a 1-bit color component to 8 bit
-inline u8 Convert1To8(u8 value) {
+constexpr u8 Convert1To8(u8 value) {
return value * 255;
}
/// Convert a 4-bit color component to 8 bit
-inline u8 Convert4To8(u8 value) {
+constexpr u8 Convert4To8(u8 value) {
return (value << 4) | value;
}
/// Convert a 5-bit color component to 8 bit
-inline u8 Convert5To8(u8 value) {
+constexpr u8 Convert5To8(u8 value) {
return (value << 3) | (value >> 2);
}
/// Convert a 6-bit color component to 8 bit
-inline u8 Convert6To8(u8 value) {
+constexpr u8 Convert6To8(u8 value) {
return (value << 2) | (value >> 4);
}
/// Convert a 8-bit color component to 1 bit
-inline u8 Convert8To1(u8 value) {
+constexpr u8 Convert8To1(u8 value) {
return value >> 7;
}
/// Convert a 8-bit color component to 4 bit
-inline u8 Convert8To4(u8 value) {
+constexpr u8 Convert8To4(u8 value) {
return value >> 4;
}
/// Convert a 8-bit color component to 5 bit
-inline u8 Convert8To5(u8 value) {
+constexpr u8 Convert8To5(u8 value) {
return value >> 3;
}
/// Convert a 8-bit color component to 6 bit
-inline u8 Convert8To6(u8 value) {
+constexpr u8 Convert8To6(u8 value) {
return value >> 2;
}
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index ed20c3629..aa6aff7b9 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -18,19 +18,11 @@
#define INSERT_PADDING_BYTES(num_bytes) u8 CONCAT2(pad, __LINE__)[(num_bytes)]
#define INSERT_PADDING_WORDS(num_words) u32 CONCAT2(pad, __LINE__)[(num_words)]
+// Inlining
#ifdef _WIN32
- // Alignment
#define FORCE_INLINE __forceinline
- #define MEMORY_ALIGNED16(x) __declspec(align(16)) x
- #define MEMORY_ALIGNED32(x) __declspec(align(32)) x
- #define MEMORY_ALIGNED64(x) __declspec(align(64)) x
- #define MEMORY_ALIGNED128(x) __declspec(align(128)) x
#else
#define FORCE_INLINE inline __attribute__((always_inline))
- #define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x
- #define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x
- #define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x
- #define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x
#endif
#ifndef _MSC_VER
diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp
index b69b05cb9..b2807354a 100644
--- a/src/common/emu_window.cpp
+++ b/src/common/emu_window.cpp
@@ -55,14 +55,14 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
(framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top);
touch_pressed = true;
- pad_state.touch = 1;
+ pad_state.touch.Assign(1);
}
void EmuWindow::TouchReleased() {
touch_pressed = false;
touch_x = 0;
touch_y = 0;
- pad_state.touch = 0;
+ pad_state.touch.Assign(0);
}
void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) {
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 4c7113390..c3061479a 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -427,6 +427,9 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
// How many files + directories we found
unsigned found_entries = 0;
+ // Save the status of callback function
+ bool callback_error = false;
+
#ifdef _WIN32
// Find the first file in the directory.
WIN32_FIND_DATA ffd;
@@ -454,9 +457,11 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
if (virtual_name == "." || virtual_name == "..")
continue;
- unsigned ret_entries;
- if (!callback(&ret_entries, directory, virtual_name))
+ unsigned ret_entries = 0;
+ if (!callback(&ret_entries, directory, virtual_name)) {
+ callback_error = true;
break;
+ }
found_entries += ret_entries;
#ifdef _WIN32
@@ -467,9 +472,14 @@ bool ForeachDirectoryEntry(unsigned* num_entries_out, const std::string &directo
closedir(dirp);
#endif
- // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
- if (num_entries_out != nullptr)
- *num_entries_out = found_entries;
+ if (!callback_error) {
+ // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it
+ if (num_entries_out != nullptr)
+ *num_entries_out = found_entries;
+ return true;
+ } else {
+ return false;
+ }
}
unsigned ScanDirectoryTree(const std::string &directory, FSTEntry& parent_entry)
diff --git a/src/common/key_map.h b/src/common/key_map.h
index 0ecec714f..68f7e2f99 100644
--- a/src/common/key_map.h
+++ b/src/common/key_map.h
@@ -4,6 +4,7 @@
#pragma once
+#include <tuple>
#include "core/hle/service/hid/hid.h"
namespace KeyMap {
@@ -15,15 +16,14 @@ struct HostDeviceKey {
int key_code;
int device_id; ///< Uniquely identifies a host device
- bool operator < (const HostDeviceKey &other) const {
- if (device_id == other.device_id) {
- return key_code < other.key_code;
- }
- return device_id < other.device_id;
+ bool operator<(const HostDeviceKey &other) const {
+ return std::tie(key_code, device_id) <
+ std::tie(other.key_code, other.device_id);
}
- bool operator == (const HostDeviceKey &other) const {
- return device_id == other.device_id && key_code == other.key_code;
+ bool operator==(const HostDeviceKey &other) const {
+ return std::tie(key_code, device_id) ==
+ std::tie(other.key_code, other.device_id);
}
};
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 21a9ae8d0..54291429a 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -44,10 +44,12 @@ namespace Log {
SUB(Service, LDR) \
SUB(Service, NIM) \
SUB(Service, NWM) \
+ SUB(Service, CAM) \
SUB(Service, CFG) \
SUB(Service, DSP) \
SUB(Service, HID) \
SUB(Service, SOC) \
+ SUB(Service, IR) \
SUB(Service, Y2R) \
CLS(HW) \
SUB(HW, Memory) \
@@ -57,6 +59,8 @@ namespace Log {
CLS(Render) \
SUB(Render, Software) \
SUB(Render, OpenGL) \
+ CLS(Audio) \
+ SUB(Audio, DSP) \
CLS(Loader)
// GetClassName is a macro defined by Windows.h, grrr...
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index c1f4d08e4..795d42ebd 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -27,25 +27,9 @@ struct Entry {
std::string message;
Entry() = default;
+ Entry(Entry&& o) = default;
- // TODO(yuriks) Use defaulted move constructors once MSVC supports them
-#define MOVE(member) member(std::move(o.member))
- Entry(Entry&& o)
- : MOVE(timestamp), MOVE(log_class), MOVE(log_level),
- MOVE(location), MOVE(message)
- {}
-#undef MOVE
-
- Entry& operator=(const Entry&& o) {
-#define MOVE(member) member = std::move(o.member)
- MOVE(timestamp);
- MOVE(log_class);
- MOVE(log_level);
- MOVE(location);
- MOVE(message);
-#undef MOVE
- return *this;
- }
+ Entry& operator=(Entry&& o) = default;
};
/**
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 43f0c59e4..4b01805ae 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -59,10 +59,12 @@ enum class Class : ClassType {
Service_LDR, ///< The LDR (3ds dll loader) service
Service_NIM, ///< The NIM (Network interface manager) service
Service_NWM, ///< The NWM (Network wlan manager) service
+ Service_CAM, ///< The CAM (Camera) service
Service_CFG, ///< The CFG (Configuration) service
Service_DSP, ///< The DSP (DSP control) service
Service_HID, ///< The HID (Human interface device) service
Service_SOC, ///< The SOC (Socket) service
+ Service_IR, ///< The IR service
Service_Y2R, ///< The Y2R (YUV to RGB conversion) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
@@ -72,6 +74,8 @@ enum class Class : ClassType {
Render, ///< Emulator video output and hardware acceleration
Render_Software, ///< Software renderer backend
Render_OpenGL, ///< OpenGL backend
+ Audio, ///< Emulator audio output
+ Audio_DSP, ///< The HLE implementation of the DSP
Loader, ///< ROM loader
Count ///< Total number of logging classes
diff --git a/src/common/x64/emitter.cpp b/src/common/x64/emitter.cpp
index 939df210e..1dcf2416c 100644
--- a/src/common/x64/emitter.cpp
+++ b/src/common/x64/emitter.cpp
@@ -225,14 +225,14 @@ void OpArg::WriteVex(XEmitter* emit, X64Reg regOp1, X64Reg regOp2, int L, int pp
// do we need any VEX fields that only appear in the three-byte form?
if (X == 1 && B == 1 && W == 0 && mmmmm == 1)
{
- u8 RvvvvLpp = (R << 7) | (vvvv << 3) | (L << 1) | pp;
+ u8 RvvvvLpp = (R << 7) | (vvvv << 3) | (L << 2) | pp;
emit->Write8(0xC5);
emit->Write8(RvvvvLpp);
}
else
{
u8 RXBmmmmm = (R << 7) | (X << 6) | (B << 5) | mmmmm;
- u8 WvvvvLpp = (W << 7) | (vvvv << 3) | (L << 1) | pp;
+ u8 WvvvvLpp = (W << 7) | (vvvv << 3) | (L << 2) | pp;
emit->Write8(0xC4);
emit->Write8(RXBmmmmm);
emit->Write8(WvvvvLpp);
diff --git a/src/common/x64/emitter.h b/src/common/x64/emitter.h
index 2dd0dc94e..7c6548fb5 100644
--- a/src/common/x64/emitter.h
+++ b/src/common/x64/emitter.h
@@ -157,45 +157,37 @@ class XEmitter;
// RIP addressing does not benefit from micro op fusion on Core arch
struct OpArg
{
- OpArg() {} // dummy op arg, used for storage
- OpArg(u64 _offset, int _scale, X64Reg rmReg = RAX, X64Reg scaledReg = RAX)
+ friend class XEmitter;
+
+ constexpr OpArg() = default; // dummy op arg, used for storage
+ constexpr OpArg(u64 offset_, int scale_, X64Reg rmReg = RAX, X64Reg scaledReg = RAX)
+ : scale(static_cast<u8>(scale_))
+ , offsetOrBaseReg(static_cast<u16>(rmReg))
+ , indexReg(static_cast<u16>(scaledReg))
+ , offset(offset_)
{
- operandReg = 0;
- scale = (u8)_scale;
- offsetOrBaseReg = (u16)rmReg;
- indexReg = (u16)scaledReg;
- //if scale == 0 never mind offsetting
- offset = _offset;
}
- bool operator==(const OpArg &b) const
+
+ constexpr bool operator==(const OpArg &b) const
{
- return operandReg == b.operandReg && scale == b.scale && offsetOrBaseReg == b.offsetOrBaseReg &&
- indexReg == b.indexReg && offset == b.offset;
+ return operandReg == b.operandReg &&
+ scale == b.scale &&
+ offsetOrBaseReg == b.offsetOrBaseReg &&
+ indexReg == b.indexReg &&
+ offset == b.offset;
}
+
void WriteRex(XEmitter *emit, int opBits, int bits, int customOp = -1) const;
void WriteVex(XEmitter* emit, X64Reg regOp1, X64Reg regOp2, int L, int pp, int mmmmm, int W = 0) const;
void WriteRest(XEmitter *emit, int extraBytes=0, X64Reg operandReg=INVALID_REG, bool warn_64bit_offset = true) const;
- void WriteFloatModRM(XEmitter *emit, FloatOp op);
void WriteSingleByteOp(XEmitter *emit, u8 op, X64Reg operandReg, int bits);
- // This one is public - must be written to
- u64 offset; // use RIP-relative as much as possible - 64-bit immediates are not available.
- u16 operandReg;
-
void WriteNormalOp(XEmitter *emit, bool toRM, NormalOp op, const OpArg &operand, int bits) const;
- bool IsImm() const {return scale == SCALE_IMM8 || scale == SCALE_IMM16 || scale == SCALE_IMM32 || scale == SCALE_IMM64;}
- bool IsSimpleReg() const {return scale == SCALE_NONE;}
- bool IsSimpleReg(X64Reg reg) const
- {
- if (!IsSimpleReg())
- return false;
- return GetSimpleReg() == reg;
- }
- bool CanDoOpWith(const OpArg &other) const
+ constexpr bool IsImm() const { return scale == SCALE_IMM8 || scale == SCALE_IMM16 || scale == SCALE_IMM32 || scale == SCALE_IMM64; }
+ constexpr bool IsSimpleReg() const { return scale == SCALE_NONE; }
+ constexpr bool IsSimpleReg(X64Reg reg) const
{
- if (IsSimpleReg()) return true;
- if (!IsSimpleReg() && !other.IsSimpleReg() && !other.IsImm()) return false;
- return true;
+ return IsSimpleReg() && GetSimpleReg() == reg;
}
int GetImmBits() const
@@ -220,16 +212,15 @@ struct OpArg
}
}
- X64Reg GetSimpleReg() const
+ constexpr X64Reg GetSimpleReg() const
{
- if (scale == SCALE_NONE)
- return (X64Reg)offsetOrBaseReg;
- else
- return INVALID_REG;
+ return scale == SCALE_NONE
+ ? static_cast<X64Reg>(offsetOrBaseReg)
+ : INVALID_REG;
}
- u32 GetImmValue() const {
- return (u32)offset;
+ constexpr u32 GetImmValue() const {
+ return static_cast<u32>(offset);
}
// For loops.
@@ -238,56 +229,60 @@ struct OpArg
}
private:
- u8 scale;
- u16 offsetOrBaseReg;
- u16 indexReg;
+ u8 scale = 0;
+ u16 offsetOrBaseReg = 0;
+ u16 indexReg = 0;
+ u64 offset = 0; // use RIP-relative as much as possible - 64-bit immediates are not available.
+ u16 operandReg = 0;
};
-inline OpArg M(const void *ptr) {return OpArg((u64)ptr, (int)SCALE_RIP);}
template <typename T>
-inline OpArg M(const T *ptr) {return OpArg((u64)(const void *)ptr, (int)SCALE_RIP);}
-inline OpArg R(X64Reg value) {return OpArg(0, SCALE_NONE, value);}
-inline OpArg MatR(X64Reg value) {return OpArg(0, SCALE_ATREG, value);}
+inline OpArg M(const T *ptr) { return OpArg(reinterpret_cast<u64>(ptr), static_cast<int>(SCALE_RIP)); }
+constexpr OpArg R(X64Reg value) { return OpArg(0, SCALE_NONE, value); }
+constexpr OpArg MatR(X64Reg value) { return OpArg(0, SCALE_ATREG, value); }
-inline OpArg MDisp(X64Reg value, int offset)
+constexpr OpArg MDisp(X64Reg value, int offset)
{
- return OpArg((u32)offset, SCALE_ATREG, value);
+ return OpArg(static_cast<u32>(offset), SCALE_ATREG, value);
}
-inline OpArg MComplex(X64Reg base, X64Reg scaled, int scale, int offset)
+constexpr OpArg MComplex(X64Reg base, X64Reg scaled, int scale, int offset)
{
return OpArg(offset, scale, base, scaled);
}
-inline OpArg MScaled(X64Reg scaled, int scale, int offset)
+constexpr OpArg MScaled(X64Reg scaled, int scale, int offset)
{
- if (scale == SCALE_1)
- return OpArg(offset, SCALE_ATREG, scaled);
- else
- return OpArg(offset, scale | 0x20, RAX, scaled);
+ return scale == SCALE_1
+ ? OpArg(offset, SCALE_ATREG, scaled)
+ : OpArg(offset, scale | 0x20, RAX, scaled);
}
-inline OpArg MRegSum(X64Reg base, X64Reg offset)
+constexpr OpArg MRegSum(X64Reg base, X64Reg offset)
{
return MComplex(base, offset, 1, 0);
}
-inline OpArg Imm8 (u8 imm) {return OpArg(imm, SCALE_IMM8);}
-inline OpArg Imm16(u16 imm) {return OpArg(imm, SCALE_IMM16);} //rarely used
-inline OpArg Imm32(u32 imm) {return OpArg(imm, SCALE_IMM32);}
-inline OpArg Imm64(u64 imm) {return OpArg(imm, SCALE_IMM64);}
-inline OpArg UImmAuto(u32 imm) {
+constexpr OpArg Imm8 (u8 imm) { return OpArg(imm, SCALE_IMM8); }
+constexpr OpArg Imm16(u16 imm) { return OpArg(imm, SCALE_IMM16); } //rarely used
+constexpr OpArg Imm32(u32 imm) { return OpArg(imm, SCALE_IMM32); }
+constexpr OpArg Imm64(u64 imm) { return OpArg(imm, SCALE_IMM64); }
+constexpr OpArg UImmAuto(u32 imm) {
return OpArg(imm, imm >= 128 ? SCALE_IMM32 : SCALE_IMM8);
}
-inline OpArg SImmAuto(s32 imm) {
+constexpr OpArg SImmAuto(s32 imm) {
return OpArg(imm, (imm >= 128 || imm < -128) ? SCALE_IMM32 : SCALE_IMM8);
}
+template <typename T>
+OpArg ImmPtr(const T* imm)
+{
#ifdef _ARCH_64
-inline OpArg ImmPtr(const void* imm) {return Imm64((u64)imm);}
+ return Imm64(reinterpret_cast<u64>(imm));
#else
-inline OpArg ImmPtr(const void* imm) {return Imm32((u32)imm);}
+ return Imm32(reinterpret_cast<u32>(imm));
#endif
+}
inline u32 PtrOffset(const void* ptr, const void* base)
{
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index d21f198e5..35b61dada 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -252,6 +252,7 @@ set(HEADERS
tracer/citrace.h
memory.h
memory_setup.h
+ mmio.h
settings.h
system.h
)
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 5cffe513c..533067d4f 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -14,10 +14,6 @@ namespace Core {
/// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable {
public:
- ARM_Interface() {
- num_instructions = 0;
- }
-
virtual ~ARM_Interface() {
}
@@ -146,11 +142,11 @@ public:
virtual void PrepareReschedule() = 0;
/// Getter for num_instructions
- u64 GetNumInstructions() {
+ u64 GetNumInstructions() const {
return num_instructions;
}
- s64 down_count; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
+ s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, decreased by the cpu run loop
protected:
@@ -162,6 +158,5 @@ protected:
private:
- u64 num_instructions; ///< Number of instructions executed
-
+ u64 num_instructions = 0; ///< Number of instructions executed
};
diff --git a/src/core/arm/disassembler/arm_disasm.cpp b/src/core/arm/disassembler/arm_disasm.cpp
index 76408e9fa..5ad1f1c29 100644
--- a/src/core/arm/disassembler/arm_disasm.cpp
+++ b/src/core/arm/disassembler/arm_disasm.cpp
@@ -738,23 +738,23 @@ std::string ARM_Disasm::DisassembleMemHalf(u32 insn)
if (is_immed) {
if (is_pre) {
if (offset == 0) {
- return Common::StringFromFormat("%s%sh\tr%d, [r%d]", opname, cond_to_str(cond), rd, rn);
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d]", opname, cond_to_str(cond), width, rd, rn);
} else {
- return Common::StringFromFormat("%s%sh\tr%d, [r%d, #%s%u]%s",
- opname, cond_to_str(cond), rd, rn, minus, offset, bang);
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d, #%s%u]%s",
+ opname, cond_to_str(cond), width, rd, rn, minus, offset, bang);
}
} else {
- return Common::StringFromFormat("%s%sh\tr%d, [r%d], #%s%u",
- opname, cond_to_str(cond), rd, rn, minus, offset);
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d], #%s%u",
+ opname, cond_to_str(cond), width, rd, rn, minus, offset);
}
}
if (is_pre) {
- return Common::StringFromFormat("%s%sh\tr%d, [r%d, %sr%d]%s",
- opname, cond_to_str(cond), rd, rn, minus, rm, bang);
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d]%s",
+ opname, cond_to_str(cond), width, rd, rn, minus, rm, bang);
} else {
- return Common::StringFromFormat("%s%sh\tr%d, [r%d], %sr%d",
- opname, cond_to_str(cond), rd, rn, minus, rm);
+ return Common::StringFromFormat("%s%s%s\tr%d, [r%d], %sr%d",
+ opname, cond_to_str(cond), width, rd, rn, minus, rm);
}
}
diff --git a/src/core/arm/dyncom/arm_dyncom_dec.cpp b/src/core/arm/dyncom/arm_dyncom_dec.cpp
index ee4288314..8cd6755cb 100644
--- a/src/core/arm/dyncom/arm_dyncom_dec.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_dec.cpp
@@ -6,10 +6,9 @@
#include "core/arm/skyeye_common/armsupp.h"
const InstructionSetEncodingItem arm_instruction[] = {
- { "vmla", 4, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 4, 4, 0 }},
- { "vmls", 7, ARMVFP2, { 28, 31, 0xF, 25, 27, 0x1, 23, 23, 1, 11, 11, 0, 8, 9, 0x2, 6, 6, 1, 4, 4, 0 }},
- { "vnmla", 4, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 4, 4, 0 }},
- { "vnmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
+ { "vmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
+ { "vmls", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x0, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
+ { "vnmla", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
{ "vnmls", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x1, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
{ "vnmul", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 1, 4, 4, 0 }},
{ "vmul", 5, ARMVFP2, { 23, 27, 0x1C, 20, 21, 0x2, 9, 11, 0x5, 6, 6, 0, 4, 4, 0 }},
@@ -211,7 +210,6 @@ const InstructionSetEncodingItem arm_exclusion_code[] = {
{ "vmla", 0, ARMVFP2, { 0 }},
{ "vmls", 0, ARMVFP2, { 0 }},
{ "vnmla", 0, ARMVFP2, { 0 }},
- { "vnmla", 0, ARMVFP2, { 0 }},
{ "vnmls", 0, ARMVFP2, { 0 }},
{ "vnmul", 0, ARMVFP2, { 0 }},
{ "vmul", 0, ARMVFP2, { 0 }},
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 96c88c83a..5f8826034 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -51,7 +51,7 @@ enum {
typedef unsigned int (*shtop_fp_t)(ARMul_State* cpu, unsigned int sht_oper);
-static bool CondPassed(ARMul_State* cpu, unsigned int cond) {
+static bool CondPassed(const ARMul_State* cpu, unsigned int cond) {
const bool n_flag = cpu->NFlag != 0;
const bool z_flag = cpu->ZFlag != 0;
const bool c_flag = cpu->CFlag != 0;
@@ -1623,9 +1623,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrb)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index)
@@ -1646,9 +1643,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrbt)(unsigned int inst, int index)
DEBUG_MSG;
}
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrd)(unsigned int inst, int index)
@@ -1703,9 +1697,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrh)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(unsigned int inst, int index)
@@ -1720,9 +1711,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsb)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index)
@@ -1737,9 +1725,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrsh)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(ldrt)(unsigned int inst, int index)
@@ -2597,9 +2582,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(str)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(uxtb)(unsigned int inst, int index)
@@ -2645,9 +2627,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strb)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index)
@@ -2669,9 +2648,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strbt)(unsigned int inst, int index)
DEBUG_MSG;
}
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(unsigned int inst, int index){
@@ -2685,9 +2661,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strd)(unsigned int inst, int index){
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(strex)(unsigned int inst, int index)
@@ -2729,9 +2702,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strh)(unsigned int inst, int index)
inst_cream->inst = inst;
inst_cream->get_addr = get_calc_addr_op(inst);
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index)
@@ -2757,9 +2727,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(strt)(unsigned int inst, int index)
DEBUG_MSG;
}
- if (BITS(inst, 12, 15) == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(sub)(unsigned int inst, int index)
@@ -2808,9 +2775,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(swp)(unsigned int inst, int index)
inst_cream->Rd = BITS(inst, 12, 15);
inst_cream->Rm = BITS(inst, 0, 3);
- if (inst_cream->Rd == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(unsigned int inst, int index){
@@ -2825,9 +2789,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(swpb)(unsigned int inst, int index){
inst_cream->Rd = BITS(inst, 12, 15);
inst_cream->Rm = BITS(inst, 0, 3);
- if (inst_cream->Rd == 15) {
- inst_base->br = INDIRECT_BRANCH;
- }
return inst_base;
}
static ARM_INST_PTR INTERPRETER_TRANSLATE(sxtab)(unsigned int inst, int index){
@@ -2915,9 +2876,6 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(tst)(unsigned int inst, int index)
inst_cream->shifter_operand = BITS(inst, 0, 11);
inst_cream->shtop_func = get_shtop(inst);
- if (inst_cream->Rd == 15)
- inst_base->br = INDIRECT_BRANCH;
-
return inst_base;
}
@@ -3244,7 +3202,6 @@ const transop_fp_t arm_instruction_trans[] = {
INTERPRETER_TRANSLATE(vmla),
INTERPRETER_TRANSLATE(vmls),
INTERPRETER_TRANSLATE(vnmla),
- INTERPRETER_TRANSLATE(vnmla),
INTERPRETER_TRANSLATE(vnmls),
INTERPRETER_TRANSLATE(vnmul),
INTERPRETER_TRANSLATE(vmul),
@@ -3636,209 +3593,208 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
case 0: goto VMLA_INST; \
case 1: goto VMLS_INST; \
case 2: goto VNMLA_INST; \
- case 3: goto VNMLA_INST; \
- case 4: goto VNMLS_INST; \
- case 5: goto VNMUL_INST; \
- case 6: goto VMUL_INST; \
- case 7: goto VADD_INST; \
- case 8: goto VSUB_INST; \
- case 9: goto VDIV_INST; \
- case 10: goto VMOVI_INST; \
- case 11: goto VMOVR_INST; \
- case 12: goto VABS_INST; \
- case 13: goto VNEG_INST; \
- case 14: goto VSQRT_INST; \
- case 15: goto VCMP_INST; \
- case 16: goto VCMP2_INST; \
- case 17: goto VCVTBDS_INST; \
- case 18: goto VCVTBFF_INST; \
- case 19: goto VCVTBFI_INST; \
- case 20: goto VMOVBRS_INST; \
- case 21: goto VMSR_INST; \
- case 22: goto VMOVBRC_INST; \
- case 23: goto VMRS_INST; \
- case 24: goto VMOVBCR_INST; \
- case 25: goto VMOVBRRSS_INST; \
- case 26: goto VMOVBRRD_INST; \
- case 27: goto VSTR_INST; \
- case 28: goto VPUSH_INST; \
- case 29: goto VSTM_INST; \
- case 30: goto VPOP_INST; \
- case 31: goto VLDR_INST; \
- case 32: goto VLDM_INST ; \
- case 33: goto SRS_INST; \
- case 34: goto RFE_INST; \
- case 35: goto BKPT_INST; \
- case 36: goto BLX_INST; \
- case 37: goto CPS_INST; \
- case 38: goto PLD_INST; \
- case 39: goto SETEND_INST; \
- case 40: goto CLREX_INST; \
- case 41: goto REV16_INST; \
- case 42: goto USAD8_INST; \
- case 43: goto SXTB_INST; \
- case 44: goto UXTB_INST; \
- case 45: goto SXTH_INST; \
- case 46: goto SXTB16_INST; \
- case 47: goto UXTH_INST; \
- case 48: goto UXTB16_INST; \
- case 49: goto CPY_INST; \
- case 50: goto UXTAB_INST; \
- case 51: goto SSUB8_INST; \
- case 52: goto SHSUB8_INST; \
- case 53: goto SSUBADDX_INST; \
- case 54: goto STREX_INST; \
- case 55: goto STREXB_INST; \
- case 56: goto SWP_INST; \
- case 57: goto SWPB_INST; \
- case 58: goto SSUB16_INST; \
- case 59: goto SSAT16_INST; \
- case 60: goto SHSUBADDX_INST; \
- case 61: goto QSUBADDX_INST; \
- case 62: goto SHADDSUBX_INST; \
- case 63: goto SHADD8_INST; \
- case 64: goto SHADD16_INST; \
- case 65: goto SEL_INST; \
- case 66: goto SADDSUBX_INST; \
- case 67: goto SADD8_INST; \
- case 68: goto SADD16_INST; \
- case 69: goto SHSUB16_INST; \
- case 70: goto UMAAL_INST; \
- case 71: goto UXTAB16_INST; \
- case 72: goto USUBADDX_INST; \
- case 73: goto USUB8_INST; \
- case 74: goto USUB16_INST; \
- case 75: goto USAT16_INST; \
- case 76: goto USADA8_INST; \
- case 77: goto UQSUBADDX_INST; \
- case 78: goto UQSUB8_INST; \
- case 79: goto UQSUB16_INST; \
- case 80: goto UQADDSUBX_INST; \
- case 81: goto UQADD8_INST; \
- case 82: goto UQADD16_INST; \
- case 83: goto SXTAB_INST; \
- case 84: goto UHSUBADDX_INST; \
- case 85: goto UHSUB8_INST; \
- case 86: goto UHSUB16_INST; \
- case 87: goto UHADDSUBX_INST; \
- case 88: goto UHADD8_INST; \
- case 89: goto UHADD16_INST; \
- case 90: goto UADDSUBX_INST; \
- case 91: goto UADD8_INST; \
- case 92: goto UADD16_INST; \
- case 93: goto SXTAH_INST; \
- case 94: goto SXTAB16_INST; \
- case 95: goto QADD8_INST; \
- case 96: goto BXJ_INST; \
- case 97: goto CLZ_INST; \
- case 98: goto UXTAH_INST; \
- case 99: goto BX_INST; \
- case 100: goto REV_INST; \
- case 101: goto BLX_INST; \
- case 102: goto REVSH_INST; \
- case 103: goto QADD_INST; \
- case 104: goto QADD16_INST; \
- case 105: goto QADDSUBX_INST; \
- case 106: goto LDREX_INST; \
- case 107: goto QDADD_INST; \
- case 108: goto QDSUB_INST; \
- case 109: goto QSUB_INST; \
- case 110: goto LDREXB_INST; \
- case 111: goto QSUB8_INST; \
- case 112: goto QSUB16_INST; \
- case 113: goto SMUAD_INST; \
- case 114: goto SMMUL_INST; \
- case 115: goto SMUSD_INST; \
- case 116: goto SMLSD_INST; \
- case 117: goto SMLSLD_INST; \
- case 118: goto SMMLA_INST; \
- case 119: goto SMMLS_INST; \
- case 120: goto SMLALD_INST; \
- case 121: goto SMLAD_INST; \
- case 122: goto SMLAW_INST; \
- case 123: goto SMULW_INST; \
- case 124: goto PKHTB_INST; \
- case 125: goto PKHBT_INST; \
- case 126: goto SMUL_INST; \
- case 127: goto SMLALXY_INST; \
- case 128: goto SMLA_INST; \
- case 129: goto MCRR_INST; \
- case 130: goto MRRC_INST; \
- case 131: goto CMP_INST; \
- case 132: goto TST_INST; \
- case 133: goto TEQ_INST; \
- case 134: goto CMN_INST; \
- case 135: goto SMULL_INST; \
- case 136: goto UMULL_INST; \
- case 137: goto UMLAL_INST; \
- case 138: goto SMLAL_INST; \
- case 139: goto MUL_INST; \
- case 140: goto MLA_INST; \
- case 141: goto SSAT_INST; \
- case 142: goto USAT_INST; \
- case 143: goto MRS_INST; \
- case 144: goto MSR_INST; \
- case 145: goto AND_INST; \
- case 146: goto BIC_INST; \
- case 147: goto LDM_INST; \
- case 148: goto EOR_INST; \
- case 149: goto ADD_INST; \
- case 150: goto RSB_INST; \
- case 151: goto RSC_INST; \
- case 152: goto SBC_INST; \
- case 153: goto ADC_INST; \
- case 154: goto SUB_INST; \
- case 155: goto ORR_INST; \
- case 156: goto MVN_INST; \
- case 157: goto MOV_INST; \
- case 158: goto STM_INST; \
- case 159: goto LDM_INST; \
- case 160: goto LDRSH_INST; \
- case 161: goto STM_INST; \
- case 162: goto LDM_INST; \
- case 163: goto LDRSB_INST; \
- case 164: goto STRD_INST; \
- case 165: goto LDRH_INST; \
- case 166: goto STRH_INST; \
- case 167: goto LDRD_INST; \
- case 168: goto STRT_INST; \
- case 169: goto STRBT_INST; \
- case 170: goto LDRBT_INST; \
- case 171: goto LDRT_INST; \
- case 172: goto MRC_INST; \
- case 173: goto MCR_INST; \
+ case 3: goto VNMLS_INST; \
+ case 4: goto VNMUL_INST; \
+ case 5: goto VMUL_INST; \
+ case 6: goto VADD_INST; \
+ case 7: goto VSUB_INST; \
+ case 8: goto VDIV_INST; \
+ case 9: goto VMOVI_INST; \
+ case 10: goto VMOVR_INST; \
+ case 11: goto VABS_INST; \
+ case 12: goto VNEG_INST; \
+ case 13: goto VSQRT_INST; \
+ case 14: goto VCMP_INST; \
+ case 15: goto VCMP2_INST; \
+ case 16: goto VCVTBDS_INST; \
+ case 17: goto VCVTBFF_INST; \
+ case 18: goto VCVTBFI_INST; \
+ case 19: goto VMOVBRS_INST; \
+ case 20: goto VMSR_INST; \
+ case 21: goto VMOVBRC_INST; \
+ case 22: goto VMRS_INST; \
+ case 23: goto VMOVBCR_INST; \
+ case 24: goto VMOVBRRSS_INST; \
+ case 25: goto VMOVBRRD_INST; \
+ case 26: goto VSTR_INST; \
+ case 27: goto VPUSH_INST; \
+ case 28: goto VSTM_INST; \
+ case 29: goto VPOP_INST; \
+ case 30: goto VLDR_INST; \
+ case 31: goto VLDM_INST ; \
+ case 32: goto SRS_INST; \
+ case 33: goto RFE_INST; \
+ case 34: goto BKPT_INST; \
+ case 35: goto BLX_INST; \
+ case 36: goto CPS_INST; \
+ case 37: goto PLD_INST; \
+ case 38: goto SETEND_INST; \
+ case 39: goto CLREX_INST; \
+ case 40: goto REV16_INST; \
+ case 41: goto USAD8_INST; \
+ case 42: goto SXTB_INST; \
+ case 43: goto UXTB_INST; \
+ case 44: goto SXTH_INST; \
+ case 45: goto SXTB16_INST; \
+ case 46: goto UXTH_INST; \
+ case 47: goto UXTB16_INST; \
+ case 48: goto CPY_INST; \
+ case 49: goto UXTAB_INST; \
+ case 50: goto SSUB8_INST; \
+ case 51: goto SHSUB8_INST; \
+ case 52: goto SSUBADDX_INST; \
+ case 53: goto STREX_INST; \
+ case 54: goto STREXB_INST; \
+ case 55: goto SWP_INST; \
+ case 56: goto SWPB_INST; \
+ case 57: goto SSUB16_INST; \
+ case 58: goto SSAT16_INST; \
+ case 59: goto SHSUBADDX_INST; \
+ case 60: goto QSUBADDX_INST; \
+ case 61: goto SHADDSUBX_INST; \
+ case 62: goto SHADD8_INST; \
+ case 63: goto SHADD16_INST; \
+ case 64: goto SEL_INST; \
+ case 65: goto SADDSUBX_INST; \
+ case 66: goto SADD8_INST; \
+ case 67: goto SADD16_INST; \
+ case 68: goto SHSUB16_INST; \
+ case 69: goto UMAAL_INST; \
+ case 70: goto UXTAB16_INST; \
+ case 71: goto USUBADDX_INST; \
+ case 72: goto USUB8_INST; \
+ case 73: goto USUB16_INST; \
+ case 74: goto USAT16_INST; \
+ case 75: goto USADA8_INST; \
+ case 76: goto UQSUBADDX_INST; \
+ case 77: goto UQSUB8_INST; \
+ case 78: goto UQSUB16_INST; \
+ case 79: goto UQADDSUBX_INST; \
+ case 80: goto UQADD8_INST; \
+ case 81: goto UQADD16_INST; \
+ case 82: goto SXTAB_INST; \
+ case 83: goto UHSUBADDX_INST; \
+ case 84: goto UHSUB8_INST; \
+ case 85: goto UHSUB16_INST; \
+ case 86: goto UHADDSUBX_INST; \
+ case 87: goto UHADD8_INST; \
+ case 88: goto UHADD16_INST; \
+ case 89: goto UADDSUBX_INST; \
+ case 90: goto UADD8_INST; \
+ case 91: goto UADD16_INST; \
+ case 92: goto SXTAH_INST; \
+ case 93: goto SXTAB16_INST; \
+ case 94: goto QADD8_INST; \
+ case 95: goto BXJ_INST; \
+ case 96: goto CLZ_INST; \
+ case 97: goto UXTAH_INST; \
+ case 98: goto BX_INST; \
+ case 99: goto REV_INST; \
+ case 100: goto BLX_INST; \
+ case 101: goto REVSH_INST; \
+ case 102: goto QADD_INST; \
+ case 103: goto QADD16_INST; \
+ case 104: goto QADDSUBX_INST; \
+ case 105: goto LDREX_INST; \
+ case 106: goto QDADD_INST; \
+ case 107: goto QDSUB_INST; \
+ case 108: goto QSUB_INST; \
+ case 109: goto LDREXB_INST; \
+ case 110: goto QSUB8_INST; \
+ case 111: goto QSUB16_INST; \
+ case 112: goto SMUAD_INST; \
+ case 113: goto SMMUL_INST; \
+ case 114: goto SMUSD_INST; \
+ case 115: goto SMLSD_INST; \
+ case 116: goto SMLSLD_INST; \
+ case 117: goto SMMLA_INST; \
+ case 118: goto SMMLS_INST; \
+ case 119: goto SMLALD_INST; \
+ case 120: goto SMLAD_INST; \
+ case 121: goto SMLAW_INST; \
+ case 122: goto SMULW_INST; \
+ case 123: goto PKHTB_INST; \
+ case 124: goto PKHBT_INST; \
+ case 125: goto SMUL_INST; \
+ case 126: goto SMLALXY_INST; \
+ case 127: goto SMLA_INST; \
+ case 128: goto MCRR_INST; \
+ case 129: goto MRRC_INST; \
+ case 130: goto CMP_INST; \
+ case 131: goto TST_INST; \
+ case 132: goto TEQ_INST; \
+ case 133: goto CMN_INST; \
+ case 134: goto SMULL_INST; \
+ case 135: goto UMULL_INST; \
+ case 136: goto UMLAL_INST; \
+ case 137: goto SMLAL_INST; \
+ case 138: goto MUL_INST; \
+ case 139: goto MLA_INST; \
+ case 140: goto SSAT_INST; \
+ case 141: goto USAT_INST; \
+ case 142: goto MRS_INST; \
+ case 143: goto MSR_INST; \
+ case 144: goto AND_INST; \
+ case 145: goto BIC_INST; \
+ case 146: goto LDM_INST; \
+ case 147: goto EOR_INST; \
+ case 148: goto ADD_INST; \
+ case 149: goto RSB_INST; \
+ case 150: goto RSC_INST; \
+ case 151: goto SBC_INST; \
+ case 152: goto ADC_INST; \
+ case 153: goto SUB_INST; \
+ case 154: goto ORR_INST; \
+ case 155: goto MVN_INST; \
+ case 156: goto MOV_INST; \
+ case 157: goto STM_INST; \
+ case 158: goto LDM_INST; \
+ case 159: goto LDRSH_INST; \
+ case 160: goto STM_INST; \
+ case 161: goto LDM_INST; \
+ case 162: goto LDRSB_INST; \
+ case 163: goto STRD_INST; \
+ case 164: goto LDRH_INST; \
+ case 165: goto STRH_INST; \
+ case 166: goto LDRD_INST; \
+ case 167: goto STRT_INST; \
+ case 168: goto STRBT_INST; \
+ case 169: goto LDRBT_INST; \
+ case 170: goto LDRT_INST; \
+ case 171: goto MRC_INST; \
+ case 172: goto MCR_INST; \
+ case 173: goto MSR_INST; \
case 174: goto MSR_INST; \
case 175: goto MSR_INST; \
case 176: goto MSR_INST; \
case 177: goto MSR_INST; \
- case 178: goto MSR_INST; \
- case 179: goto LDRB_INST; \
- case 180: goto STRB_INST; \
- case 181: goto LDR_INST; \
- case 182: goto LDRCOND_INST ; \
- case 183: goto STR_INST; \
- case 184: goto CDP_INST; \
- case 185: goto STC_INST; \
- case 186: goto LDC_INST; \
- case 187: goto LDREXD_INST; \
- case 188: goto STREXD_INST; \
- case 189: goto LDREXH_INST; \
- case 190: goto STREXH_INST; \
- case 191: goto NOP_INST; \
- case 192: goto YIELD_INST; \
- case 193: goto WFE_INST; \
- case 194: goto WFI_INST; \
- case 195: goto SEV_INST; \
- case 196: goto SWI_INST; \
- case 197: goto BBL_INST; \
- case 198: goto B_2_THUMB ; \
- case 199: goto B_COND_THUMB ; \
- case 200: goto BL_1_THUMB ; \
- case 201: goto BL_2_THUMB ; \
- case 202: goto BLX_1_THUMB ; \
- case 203: goto DISPATCH; \
- case 204: goto INIT_INST_LENGTH; \
- case 205: goto END; \
+ case 178: goto LDRB_INST; \
+ case 179: goto STRB_INST; \
+ case 180: goto LDR_INST; \
+ case 181: goto LDRCOND_INST ; \
+ case 182: goto STR_INST; \
+ case 183: goto CDP_INST; \
+ case 184: goto STC_INST; \
+ case 185: goto LDC_INST; \
+ case 186: goto LDREXD_INST; \
+ case 187: goto STREXD_INST; \
+ case 188: goto LDREXH_INST; \
+ case 189: goto STREXH_INST; \
+ case 190: goto NOP_INST; \
+ case 191: goto YIELD_INST; \
+ case 192: goto WFE_INST; \
+ case 193: goto WFI_INST; \
+ case 194: goto SEV_INST; \
+ case 195: goto SWI_INST; \
+ case 196: goto BBL_INST; \
+ case 197: goto B_2_THUMB ; \
+ case 198: goto B_COND_THUMB ; \
+ case 199: goto BL_1_THUMB ; \
+ case 200: goto BL_2_THUMB ; \
+ case 201: goto BLX_1_THUMB ; \
+ case 202: goto DISPATCH; \
+ case 203: goto INIT_INST_LENGTH; \
+ case 204: goto END; \
}
#endif
@@ -3865,7 +3821,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
// to a clunky switch statement.
#if defined __GNUC__ || defined __clang__
void *InstLabel[] = {
- &&VMLA_INST, &&VMLS_INST, &&VNMLA_INST, &&VNMLA_INST, &&VNMLS_INST, &&VNMUL_INST, &&VMUL_INST, &&VADD_INST, &&VSUB_INST,
+ &&VMLA_INST, &&VMLS_INST, &&VNMLA_INST, &&VNMLS_INST, &&VNMUL_INST, &&VMUL_INST, &&VADD_INST, &&VSUB_INST,
&&VDIV_INST, &&VMOVI_INST, &&VMOVR_INST, &&VABS_INST, &&VNEG_INST, &&VSQRT_INST, &&VCMP_INST, &&VCMP2_INST, &&VCVTBDS_INST,
&&VCVTBFF_INST, &&VCVTBFI_INST, &&VMOVBRS_INST, &&VMSR_INST, &&VMOVBRC_INST, &&VMRS_INST, &&VMOVBCR_INST, &&VMOVBRRSS_INST,
&&VMOVBRRD_INST, &&VSTR_INST, &&VPUSH_INST, &&VSTM_INST, &&VPOP_INST, &&VLDR_INST, &&VLDM_INST,
@@ -4477,11 +4433,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
inst_cream->get_addr(cpu, inst_cream->inst, addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr);
-
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4494,12 +4445,14 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory8(addr);
+ const u32 dest_index = BITS(inst_cream->inst, 12, 15);
+ const u32 previous_mode = cpu->Mode;
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
+ cpu->ChangePrivilegeMode(USER32MODE);
+ const u8 value = cpu->ReadMemory8(addr);
+ cpu->ChangePrivilegeMode(previous_mode);
+
+ cpu->Reg[dest_index] = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4535,10 +4488,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory32(read_addr);
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(generic_arm_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
@@ -4554,10 +4503,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory8(read_addr);
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(generic_arm_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
@@ -4573,10 +4518,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
cpu->SetExclusiveMemoryAddress(read_addr);
RD = cpu->ReadMemory16(read_addr);
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(generic_arm_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
@@ -4593,11 +4534,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
RD = cpu->ReadMemory32(read_addr);
RD2 = cpu->ReadMemory32(read_addr + 4);
-
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(generic_arm_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(generic_arm_inst));
@@ -4611,10 +4547,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
inst_cream->get_addr(cpu, inst_cream->inst, addr);
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = cpu->ReadMemory16(addr);
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4631,10 +4563,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
value |= 0xffffff00;
}
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4652,10 +4580,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
value |= 0xffff0000;
}
cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4668,13 +4592,14 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- unsigned int value = cpu->ReadMemory32(addr);
- cpu->Reg[BITS(inst_cream->inst, 12, 15)] = value;
+ const u32 dest_index = BITS(inst_cream->inst, 12, 15);
+ const u32 previous_mode = cpu->Mode;
- if (BITS(inst_cream->inst, 12, 15) == 15) {
- INC_PC(sizeof(ldst_inst));
- goto DISPATCH;
- }
+ cpu->ChangePrivilegeMode(USER32MODE);
+ const u32 value = cpu->ReadMemory32(addr);
+ cpu->ChangePrivilegeMode(previous_mode);
+
+ cpu->Reg[dest_index] = value;
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -4731,10 +4656,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
}
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(mla_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mla_inst));
@@ -4773,18 +4694,15 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
mrc_inst* inst_cream = (mrc_inst*)inst_base->component;
- unsigned int inst = inst_cream->inst;
- if (inst_cream->Rd == 15) {
- DEBUG_MSG;
- }
- if (inst_cream->inst == 0xeef04a10) {
- // Undefined instruction fmrx
- RD = 0x20000000;
- CITRA_IGNORE_EXIT(-1);
- goto END;
- } else {
- if (inst_cream->cp_num == 15)
- RD = cpu->ReadCP15Register(CRn, OPCODE_1, CRm, OPCODE_2);
+ if (inst_cream->cp_num == 15) {
+ const uint32_t value = cpu->ReadCP15Register(CRn, OPCODE_1, CRm, OPCODE_2);
+
+ if (inst_cream->Rd == 15) {
+ cpu->Cpsr = (cpu->Cpsr & ~0xF0000000) | (value & 0xF0000000);
+ LOAD_NZCVT;
+ } else {
+ RD = value;
+ }
}
}
cpu->Reg[15] += cpu->GetInstructionSize();
@@ -4883,10 +4801,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
UPDATE_NFLAG(RD);
UPDATE_ZFLAG(RD);
}
- if (inst_cream->Rd == 15) {
- INC_PC(sizeof(mul_inst));
- goto DISPATCH;
- }
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(mul_inst));
@@ -6061,8 +5975,13 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
+
+ const u32 previous_mode = cpu->Mode;
+ const u32 value = cpu->Reg[BITS(inst_cream->inst, 12, 15)] & 0xff;
+
+ cpu->ChangePrivilegeMode(USER32MODE);
cpu->WriteMemory8(addr, value);
+ cpu->ChangePrivilegeMode(previous_mode);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
@@ -6196,8 +6115,16 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)];
+ const u32 previous_mode = cpu->Mode;
+ const u32 rt_index = BITS(inst_cream->inst, 12, 15);
+
+ u32 value = cpu->Reg[rt_index];
+ if (rt_index == 15)
+ value += 2 * cpu->GetInstructionSize();
+
+ cpu->ChangePrivilegeMode(USER32MODE);
cpu->WriteMemory32(addr, value);
+ cpu->ChangePrivilegeMode(previous_mode);
}
cpu->Reg[15] += cpu->GetInstructionSize();
INC_PC(sizeof(ldst_inst));
diff --git a/src/core/arm/dyncom/arm_dyncom_run.h b/src/core/arm/dyncom/arm_dyncom_run.h
index 13bef17fc..8eb694fee 100644
--- a/src/core/arm/dyncom/arm_dyncom_run.h
+++ b/src/core/arm/dyncom/arm_dyncom_run.h
@@ -30,7 +30,7 @@
* @return If the PC is being read, then the word-aligned PC value is returned.
* If the PC is not being read, then the value stored in the register is returned.
*/
-static inline u32 CHECK_READ_REG15_WA(ARMul_State* cpu, int Rn) {
+inline u32 CHECK_READ_REG15_WA(const ARMul_State* cpu, int Rn) {
return (Rn == 15) ? ((cpu->Reg[15] & ~0x3) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn];
}
@@ -43,6 +43,6 @@ static inline u32 CHECK_READ_REG15_WA(ARMul_State* cpu, int Rn) {
* @return If the PC is being read, then the incremented PC value is returned.
* If the PC is not being read, then the values stored in the register is returned.
*/
-static inline u32 CHECK_READ_REG15(ARMul_State* cpu, int Rn) {
+inline u32 CHECK_READ_REG15(const ARMul_State* cpu, int Rn) {
return (Rn == 15) ? ((cpu->Reg[15] & ~0x1) + cpu->GetInstructionSize() * 2) : cpu->Reg[Rn];
}
diff --git a/src/core/arm/dyncom/arm_dyncom_thumb.h b/src/core/arm/dyncom/arm_dyncom_thumb.h
index 447974363..c1be3c735 100644
--- a/src/core/arm/dyncom/arm_dyncom_thumb.h
+++ b/src/core/arm/dyncom/arm_dyncom_thumb.h
@@ -38,7 +38,7 @@ enum class ThumbDecodeStatus {
// Translates a Thumb mode instruction into its ARM equivalent.
ThumbDecodeStatus TranslateThumbInstruction(u32 addr, u32 instr, u32* ainstr, u32* inst_size);
-static inline u32 GetThumbInstruction(u32 instr, u32 address) {
+inline u32 GetThumbInstruction(u32 instr, u32 address) {
// Normally you would need to handle instruction endianness,
// however, it is fixed to little-endian on the MPCore, so
// there's no need to check for this beforehand.
diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h
index 91a8d4d57..210972917 100644
--- a/src/core/arm/skyeye_common/vfp/vfp_helper.h
+++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h
@@ -85,7 +85,7 @@ enum : u32 {
#define vfp_single(inst) (((inst) & 0x0000f00) == 0xa00)
-static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
+inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
{
if (shift) {
if (shift < 32)
@@ -96,7 +96,7 @@ static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
return val;
}
-static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
+inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
{
if (shift) {
if (shift < 64)
@@ -107,7 +107,7 @@ static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
return val;
}
-static inline u32 vfp_hi64to32jamming(u64 val)
+inline u32 vfp_hi64to32jamming(u64 val)
{
u32 v;
u32 highval = val >> 32;
@@ -121,7 +121,7 @@ static inline u32 vfp_hi64to32jamming(u64 val)
return v;
}
-static inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
+inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
{
*resl = nl + ml;
*resh = nh + mh;
@@ -129,7 +129,7 @@ static inline void add128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
*resh += 1;
}
-static inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
+inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
{
*resl = nl - ml;
*resh = nh - mh;
@@ -137,7 +137,7 @@ static inline void sub128(u64* resh, u64* resl, u64 nh, u64 nl, u64 mh, u64 ml)
*resh -= 1;
}
-static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
+inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
{
u32 nh, nl, mh, ml;
u64 rh, rma, rmb, rl;
@@ -164,20 +164,20 @@ static inline void mul64to128(u64* resh, u64* resl, u64 n, u64 m)
*resh = rh;
}
-static inline void shift64left(u64* resh, u64* resl, u64 n)
+inline void shift64left(u64* resh, u64* resl, u64 n)
{
*resh = n >> 63;
*resl = n << 1;
}
-static inline u64 vfp_hi64multiply64(u64 n, u64 m)
+inline u64 vfp_hi64multiply64(u64 n, u64 m)
{
u64 rh, rl;
mul64to128(&rh, &rl, n, m);
return rh | (rl != 0);
}
-static inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
+inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
{
u64 mh, ml, remh, reml, termh, terml, z;
@@ -249,7 +249,7 @@ enum : u32 {
VFP_SNAN = (VFP_NAN|VFP_NAN_SIGNAL)
};
-static inline int vfp_single_type(vfp_single* s)
+inline int vfp_single_type(const vfp_single* s)
{
int type = VFP_NUMBER;
if (s->exponent == 255) {
@@ -271,7 +271,7 @@ static inline int vfp_single_type(vfp_single* s)
// Unpack a single-precision float. Note that this returns the magnitude
// of the single-precision float mantissa with the 1. if necessary,
// aligned to bit 30.
-static inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
+inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
{
s->sign = vfp_single_packed_sign(val) >> 16,
s->exponent = vfp_single_packed_exponent(val);
@@ -293,7 +293,7 @@ static inline void vfp_single_unpack(vfp_single* s, s32 val, u32* fpscr)
// Re-pack a single-precision float. This assumes that the float is
// already normalised such that the MSB is bit 30, _not_ bit 31.
-static inline s32 vfp_single_pack(vfp_single* s)
+inline s32 vfp_single_pack(const vfp_single* s)
{
u32 val = (s->sign << 16) +
(s->exponent << VFP_SINGLE_MANTISSA_BITS) +
@@ -335,7 +335,7 @@ struct vfp_double {
#define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1))
#define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1))
-static inline int vfp_double_type(vfp_double* s)
+inline int vfp_double_type(const vfp_double* s)
{
int type = VFP_NUMBER;
if (s->exponent == 2047) {
@@ -357,7 +357,7 @@ static inline int vfp_double_type(vfp_double* s)
// Unpack a double-precision float. Note that this returns the magnitude
// of the double-precision float mantissa with the 1. if necessary,
// aligned to bit 62.
-static inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
+inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
{
s->sign = vfp_double_packed_sign(val) >> 48;
s->exponent = vfp_double_packed_exponent(val);
@@ -379,7 +379,7 @@ static inline void vfp_double_unpack(vfp_double* s, s64 val, u32* fpscr)
// Re-pack a double-precision float. This assumes that the float is
// already normalised such that the MSB is bit 30, _not_ bit 31.
-static inline s64 vfp_double_pack(vfp_double* s)
+inline s64 vfp_double_pack(const vfp_double* s)
{
u64 val = ((u64)s->sign << 48) +
((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) +
@@ -415,7 +415,7 @@ struct op {
u32 flags;
};
-static inline u32 fls(u32 x)
+inline u32 fls(u32 x)
{
int r = 32;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 219b03af4..84d6c392e 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -2,6 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <memory>
+
+#include "common/make_unique.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -17,8 +20,8 @@
namespace Core {
-ARM_Interface* g_app_core = nullptr; ///< ARM11 application core
-ARM_Interface* g_sys_core = nullptr; ///< ARM11 system (OS) core
+std::unique_ptr<ARM_Interface> g_app_core; ///< ARM11 application core
+std::unique_ptr<ARM_Interface> g_sys_core; ///< ARM11 system (OS) core
/// Run the core CPU loop
void RunLoop(int tight_loop) {
@@ -70,17 +73,16 @@ void Stop() {
}
/// Initialize the core
-int Init() {
- g_sys_core = new ARM_DynCom(USER32MODE);
- g_app_core = new ARM_DynCom(USER32MODE);
+void Init() {
+ g_sys_core = Common::make_unique<ARM_DynCom>(USER32MODE);
+ g_app_core = Common::make_unique<ARM_DynCom>(USER32MODE);
LOG_DEBUG(Core, "Initialized OK");
- return 0;
}
void Shutdown() {
- delete g_app_core;
- delete g_sys_core;
+ g_app_core.reset();
+ g_sys_core.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
diff --git a/src/core/core.h b/src/core/core.h
index 491230a74..ad26dca3f 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -4,6 +4,7 @@
#pragma once
+#include <memory>
#include "common/common_types.h"
class ARM_Interface;
@@ -23,8 +24,8 @@ struct ThreadContext {
u32 fpexc;
};
-extern ARM_Interface* g_app_core; ///< ARM11 application core
-extern ARM_Interface* g_sys_core; ///< ARM11 system (OS) core
+extern std::unique_ptr<ARM_Interface> g_app_core; ///< ARM11 application core
+extern std::unique_ptr<ARM_Interface> g_sys_core; ///< ARM11 system (OS) core
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -51,7 +52,7 @@ void Halt(const char *msg);
void Stop();
/// Initialize the core
-int Init();
+void Init();
/// Shutdown the core
void Shutdown();
diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp
index 3f81447df..97adf0e12 100644
--- a/src/core/file_sys/archive_backend.cpp
+++ b/src/core/file_sys/archive_backend.cpp
@@ -43,7 +43,7 @@ Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) {
}
}
-const std::string Path::DebugStr() const {
+std::string Path::DebugStr() const {
switch (GetType()) {
case Invalid:
default:
@@ -66,7 +66,7 @@ const std::string Path::DebugStr() const {
}
}
-const std::string Path::AsString() const {
+std::string Path::AsString() const {
switch (GetType()) {
case Char:
return string;
@@ -83,7 +83,7 @@ const std::string Path::AsString() const {
}
}
-const std::u16string Path::AsU16Str() const {
+std::u16string Path::AsU16Str() const {
switch (GetType()) {
case Char:
return Common::UTF8ToUTF16(string);
@@ -99,7 +99,7 @@ const std::u16string Path::AsU16Str() const {
}
}
-const std::vector<u8> Path::AsBinary() const {
+std::vector<u8> Path::AsBinary() const {
switch (GetType()) {
case Binary:
return binary;
diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h
index e7a59a1ed..601e95d8c 100644
--- a/src/core/file_sys/archive_backend.h
+++ b/src/core/file_sys/archive_backend.h
@@ -49,11 +49,11 @@ public:
* Gets the string representation of the path for debugging
* @return String representation of the path for debugging
*/
- const std::string DebugStr() const;
+ std::string DebugStr() const;
- const std::string AsString() const;
- const std::u16string AsU16Str() const;
- const std::vector<u8> AsBinary() const;
+ std::string AsString() const;
+ std::u16string AsU16Str() const;
+ std::vector<u8> AsBinary() const;
private:
LowPathType type;
diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp
index 0ba502200..a51416774 100644
--- a/src/core/file_sys/disk_archive.cpp
+++ b/src/core/file_sys/disk_archive.cpp
@@ -139,7 +139,7 @@ bool DiskFile::Close() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
-DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) {
+DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) : directory() {
// TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass
// the root directory we set while opening the archive.
// For example, opening /../../usr/bin can give the emulated program your installed programs.
@@ -149,7 +149,9 @@ DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) {
bool DiskDirectory::Open() {
if (!FileUtil::IsDirectory(path))
return false;
- FileUtil::ScanDirectoryTree(path, directory);
+ unsigned size = FileUtil::ScanDirectoryTree(path, directory);
+ directory.size = size;
+ directory.isDirectory = true;
children_iterator = directory.children.begin();
return true;
}
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 3501e45db..882a51df1 100644
--- a/src/core/hle/function_wrappers.h
+++ b/src/core/hle/function_wrappers.h
@@ -188,6 +188,10 @@ template<ResultCode func(s64*, Handle, u32)> void Wrap() {
FuncReturn(retval);
}
+template<ResultCode func(Handle, u32)> void Wrap() {
+ FuncReturn(func(PARAM(0), PARAM(1)).raw);
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 195286422..5c3c47acf 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -45,30 +45,32 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
// Wait current thread (acquire the arbiter)...
case ArbitrationType::WaitIfLessThan:
- if ((s32)Memory::Read32(address) <= value) {
+ if ((s32)Memory::Read32(address) < value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
}
break;
case ArbitrationType::WaitIfLessThanWithTimeout:
- if ((s32)Memory::Read32(address) <= value) {
+ if ((s32)Memory::Read32(address) < value) {
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
break;
case ArbitrationType::DecrementAndWaitIfLessThan:
{
- s32 memory_value = Memory::Read32(address) - 1;
- Memory::Write32(address, memory_value);
- if (memory_value <= value) {
+ s32 memory_value = Memory::Read32(address);
+ if (memory_value < value) {
+ // Only change the memory value if the thread should wait
+ Memory::Write32(address, (s32)memory_value - 1);
Kernel::WaitCurrentThread_ArbitrateAddress(address);
}
break;
}
case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout:
{
- s32 memory_value = Memory::Read32(address) - 1;
- Memory::Write32(address, memory_value);
- if (memory_value <= value) {
+ s32 memory_value = Memory::Read32(address);
+ if (memory_value < value) {
+ // Only change the memory value if the thread should wait
+ Memory::Write32(address, (s32)memory_value - 1);
Kernel::WaitCurrentThread_ArbitrateAddress(address);
GetCurrentThread()->WakeAfterDelay(nanoseconds);
}
@@ -82,6 +84,13 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
HLE::Reschedule(__func__);
+ // The calls that use a timeout seem to always return a Timeout error even if they did not put the thread to sleep
+ if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
+ type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
+
+ return ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
+ ErrorSummary::StatusChanged, ErrorLevel::Info);
+ }
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 0cfb43fc7..862643448 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -7,6 +7,8 @@
#include <utility>
#include <vector>
+#include "audio_core/audio_core.h"
+
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -107,7 +109,6 @@ struct MemoryArea {
static MemoryArea memory_areas[] = {
{SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, "Shared Memory"}, // Shared memory
{VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM)
- {DSP_RAM_VADDR, DSP_RAM_SIZE, "DSP RAM"}, // DSP memory
{TLS_AREA_VADDR, TLS_AREA_SIZE, "TLS Area"}, // TLS memory
};
@@ -133,6 +134,8 @@ void InitLegacyAddressSpace(Kernel::VMManager& address_space) {
auto shared_page_vma = address_space.MapBackingMemory(SHARED_PAGE_VADDR,
(u8*)&SharedPage::shared_page, SHARED_PAGE_SIZE, MemoryState::Shared).MoveFrom();
address_space.Reprotect(shared_page_vma, VMAPermission::Read);
+
+ AudioCore::AddAddressSpace(address_space);
}
} // namespace
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index d148efde2..24b266eae 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -35,7 +35,7 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) {
process->codeset = std::move(code_set);
process->flags.raw = 0;
- process->flags.memory_region = MemoryRegion::APPLICATION;
+ process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
Memory::InitLegacyAddressSpace(process->vm_manager);
return process;
@@ -130,9 +130,11 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) {
Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority);
}
+VAddr Process::GetLinearHeapAreaAddress() const {
+ return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR;
+}
VAddr Process::GetLinearHeapBase() const {
- return (kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR)
- + memory_region->base;
+ return GetLinearHeapAreaAddress() + memory_region->base;
}
VAddr Process::GetLinearHeapLimit() const {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 60e17f251..6d2ca96a2 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -143,6 +143,7 @@ public:
/// Bitmask of the used TLS slots
std::bitset<300> used_tls_slots;
+ VAddr GetLinearHeapAreaAddress() const;
VAddr GetLinearHeapBase() const;
VAddr GetLinearHeapLimit() const;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 1f477664b..d90f0f00f 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -39,6 +39,12 @@ ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
+ // TODO(Subv): Return E0E01BEE when permissions and other_permissions don't
+ // match what was specified when the memory block was created.
+
+ // TODO(Subv): Return E0E01BEE when address should be 0.
+ // Note: Find out when that's the case.
+
if (fixed_address != 0) {
if (address != 0 && address != fixed_address) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s: fixed_addres is 0x%08X!",
@@ -74,6 +80,21 @@ ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions,
return RESULT_SUCCESS;
}
+ResultCode SharedMemory::Unmap(VAddr address) {
+ if (base_address == 0) {
+ // TODO(Subv): Verify what actually happens when you want to unmap a memory block that
+ // was originally mapped with address = 0
+ return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ }
+
+ if (base_address != address)
+ return ResultCode(ErrorDescription::WrongAddress, ErrorModule::OS, ErrorSummary::InvalidState, ErrorLevel::Usage);
+
+ base_address = 0;
+
+ return RESULT_SUCCESS;
+}
+
u8* SharedMemory::GetPointer(u32 offset) {
if (base_address != 0)
return Memory::GetPointer(base_address + offset);
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 35b550d12..b51049ad0 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -53,6 +53,13 @@ public:
ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions);
/**
+ * Unmaps a shared memory block from the specified address in system memory
+ * @param address Address in system memory where the shared memory block is mapped
+ * @return Result code of the unmap operation
+ */
+ ResultCode Unmap(VAddr address);
+
+ /**
* Gets a pointer to the shared memory block
* @param offset Offset from the start of the shared memory block to get pointer
* @return Pointer to the shared memory block from the specified offset
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c08fc1c7a..bf32f653d 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -300,7 +300,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
thread->waitsynch_waited = false;
- if (thread->status == THREADSTATUS_WAIT_SYNCH) {
+ if (thread->status == THREADSTATUS_WAIT_SYNCH || thread->status == THREADSTATUS_WAIT_ARB) {
thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
ErrorSummary::StatusChanged, ErrorLevel::Info));
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 08b3ea8c0..ce6bbd719 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -42,6 +42,9 @@ bool Timer::ShouldWait() {
void Timer::Acquire() {
ASSERT_MSG( !ShouldWait(), "object unavailable!");
+
+ if (reset_type == RESETTYPE_ONESHOT)
+ signaled = false;
}
void Timer::Set(s64 initial, s64 interval) {
@@ -84,9 +87,6 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
// Resume all waiting threads
timer->WakeupAllWaitingThreads();
- if (timer->reset_type == RESETTYPE_ONESHOT)
- timer->signaled = false;
-
if (timer->interval_delay != 0) {
// Reschedule the timer with the interval delay
u64 interval_microseconds = timer->interval_delay / 1000;
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 2610acf76..1e289f38a 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -8,6 +8,7 @@
#include "core/hle/kernel/vm_manager.h"
#include "core/memory_setup.h"
+#include "core/mmio.h"
namespace Kernel {
@@ -104,7 +105,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8 * m
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
}
-ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state) {
+ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler) {
// This is the appropriately sized VMA that will turn into our allocation.
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
VirtualMemoryArea& final_vma = vma_handle->second;
@@ -114,6 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3
final_vma.permissions = VMAPermission::ReadWrite;
final_vma.meminfo_state = state;
final_vma.paddr = paddr;
+ final_vma.mmio_handler = mmio_handler;
UpdatePageTableForVMA(final_vma);
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
@@ -330,8 +332,7 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory);
break;
case VMAType::MMIO:
- // TODO(yuriks): Add support for MMIO handlers.
- Memory::MapIoRegion(vma.base, vma.size);
+ Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler);
break;
}
}
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 4e95f1f0c..91d40655b 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -11,6 +11,7 @@
#include "common/common_types.h"
#include "core/hle/result.h"
+#include "core/mmio.h"
namespace Kernel {
@@ -92,6 +93,7 @@ struct VirtualMemoryArea {
// Settings for type = MMIO
/// Physical address of the register area this VMA maps to.
PAddr paddr = 0;
+ Memory::MMIORegionPointer mmio_handler = nullptr;
/// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
@@ -168,8 +170,9 @@ public:
* @param paddr The physical address where the registers are present.
* @param size Size of the mapping.
* @param state MemoryState tag to attach to the VMA.
+ * @param mmio_handler The handler that will implement read and write for this MMIO region.
*/
- ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state);
+ ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler);
/// Unmaps a range of addresses, splitting VMAs as necessary.
ResultCode UnmapRange(VAddr target, u32 size);
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index cb2d681e0..69613fbbb 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -18,6 +18,7 @@
/// Detailed description of the error. This listing is likely incomplete.
enum class ErrorDescription : u32 {
Success = 0,
+ WrongAddress = 53,
FS_NotFound = 100,
FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
InvalidSection = 1000,
@@ -192,10 +193,10 @@ union ResultCode {
explicit ResultCode(u32 raw) : raw(raw) {}
ResultCode(ErrorDescription description_, ErrorModule module_,
ErrorSummary summary_, ErrorLevel level_) : raw(0) {
- description = description_;
- module = module_;
- summary = summary_;
- level = level_;
+ description.Assign(description_);
+ module.Assign(module_);
+ summary.Assign(summary_);
+ level.Assign(level_);
}
ResultCode& operator=(const ResultCode& o) { raw = o.raw; return *this; }
@@ -268,7 +269,6 @@ public:
: result_code(error_code)
{
ASSERT(error_code.IsError());
- UpdateDebugPtr();
}
/**
@@ -286,40 +286,37 @@ public:
: result_code(o.result_code)
{
if (!o.empty()) {
- new (&storage) T(*o.GetPointer());
+ new (&object) T(o.object);
}
- UpdateDebugPtr();
}
ResultVal(ResultVal&& o)
: result_code(o.result_code)
{
if (!o.empty()) {
- new (&storage) T(std::move(*o.GetPointer()));
+ new (&object) T(std::move(o.object));
}
- UpdateDebugPtr();
}
~ResultVal() {
if (!empty()) {
- GetPointer()->~T();
+ object.~T();
}
}
ResultVal& operator=(const ResultVal& o) {
if (!empty()) {
if (!o.empty()) {
- *GetPointer() = *o.GetPointer();
+ object = o.object;
} else {
- GetPointer()->~T();
+ object.~T();
}
} else {
if (!o.empty()) {
- new (&storage) T(*o.GetPointer());
+ new (&object) T(o.object);
}
}
result_code = o.result_code;
- UpdateDebugPtr();
return *this;
}
@@ -332,11 +329,10 @@ public:
void emplace(ResultCode success_code, Args&&... args) {
ASSERT(success_code.IsSuccess());
if (!empty()) {
- GetPointer()->~T();
+ object.~T();
}
- new (&storage) T(std::forward<Args>(args)...);
+ new (&object) T(std::forward<Args>(args)...);
result_code = success_code;
- UpdateDebugPtr();
}
/// Returns true if the `ResultVal` contains an error code and no value.
@@ -349,15 +345,15 @@ public:
ResultCode Code() const { return result_code; }
- const T& operator* () const { return *GetPointer(); }
- T& operator* () { return *GetPointer(); }
- const T* operator->() const { return GetPointer(); }
- T* operator->() { return GetPointer(); }
+ const T& operator* () const { return object; }
+ T& operator* () { return object; }
+ const T* operator->() const { return &object; }
+ T* operator->() { return &object; }
/// Returns the value contained in this `ResultVal`, or the supplied default if it is missing.
template <typename U>
T ValueOr(U&& value) const {
- return !empty() ? *GetPointer() : std::move(value);
+ return !empty() ? object : std::move(value);
}
/// Asserts that the result succeeded and returns a reference to it.
@@ -371,31 +367,10 @@ public:
}
private:
- typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType;
-
- StorageType storage;
+ // A union is used to allocate the storage for the value, while allowing us to construct and
+ // destruct it at will.
+ union { T object; };
ResultCode result_code;
-#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() {
-#ifdef _DEBUG
- debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage));
-#endif
- }
-
- const T* GetPointer() const {
- ASSERT(!empty());
- return static_cast<const T*>(static_cast<const void*>(&storage));
- }
-
- T* GetPointer() {
- ASSERT(!empty());
- return static_cast<T*>(static_cast<void*>(&storage));
- }
};
/**
diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp
index f8aab6bc7..d67325506 100644
--- a/src/core/hle/service/ac_u.cpp
+++ b/src/core/hle/service/ac_u.cpp
@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
-#include "core/hle/hle.h"
#include "core/hle/service/ac_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -23,12 +22,27 @@ static void GetWifiStatus(Service::Interface* self) {
// TODO(purpasmart96): This function is only a stub,
// it returns a valid result without implementing full functionality.
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = 0; // Connection type set to none
LOG_WARNING(Service_AC, "(STUBBED) called");
}
+/**
+ * AC_U::IsConnected service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : bool, is connected
+ */
+static void IsConnected(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = false; // Not connected to ac:u service
+
+ LOG_WARNING(Service_AC, "(STUBBED) called");
+}
+
const Interface::FunctionInfo FunctionTable[] = {
{0x00010000, nullptr, "CreateDefaultConfig"},
{0x00040006, nullptr, "ConnectAsync"},
@@ -45,7 +59,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x002D0082, nullptr, "SetRequestEulaVersion"},
{0x00300004, nullptr, "RegisterDisconnectEvent"},
{0x003C0042, nullptr, "GetAPSSIDList"},
- {0x003E0042, nullptr, "IsConnected"},
+ {0x003E0042, IsConnected, "IsConnected"},
{0x00400042, nullptr, "SetClientVersion"},
};
diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp
index 57f49c91f..b23d17fba 100644
--- a/src/core/hle/service/act_u.cpp
+++ b/src/core/hle/service/act_u.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/act_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -10,14 +9,15 @@
namespace ACT_U {
-// Empty arrays are illegal -- commented out until an entry is added.
-//const Interface::FunctionInfo FunctionTable[] = { };
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000600C2, nullptr, "GetAccountDataBlock"},
+};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
- //Register(FunctionTable);
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 7ae4859a7..06be9940e 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -10,10 +10,6 @@
#include "core/hle/service/am/am_net.h"
#include "core/hle/service/am/am_sys.h"
-#include "core/hle/hle.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-
namespace Service {
namespace AM {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 0b78f5393..15e63bc7b 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -4,10 +4,10 @@
#pragma once
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/service.h"
-
namespace Service {
+
+class Interface;
+
namespace AM {
/**
diff --git a/src/core/hle/service/am/am_app.cpp b/src/core/hle/service/am/am_app.cpp
index f40a87cb4..16c76a1eb 100644
--- a/src/core/hle/service/am/am_app.cpp
+++ b/src/core/hle/service/am/am_app.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/am_app.h"
diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp
index aa391f3b2..065e04118 100644
--- a/src/core/hle/service/am/am_net.cpp
+++ b/src/core/hle/service/am/am_net.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/am_net.h"
@@ -10,6 +9,36 @@ namespace Service {
namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
+ {0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
+ {0x00020082, GetTitleIDList, "GetTitleIDList"},
+ {0x00030084, nullptr, "ListTitles"},
+ {0x000400C0, nullptr, "DeleteApplicationTitle"},
+ {0x000500C0, nullptr, "GetTitleProductCode"},
+ {0x00080000, nullptr, "TitleIDListGetTotal3"},
+ {0x00090082, nullptr, "GetTitleIDList3"},
+ {0x000A0000, nullptr, "GetDeviceID"},
+ {0x000D0084, nullptr, "ListTitles2"},
+ {0x00140040, nullptr, "FinishInstallToMedia"},
+ {0x00180080, nullptr, "InitializeTitleDatabase"},
+ {0x00190040, nullptr, "ReloadDBS"},
+ {0x001A00C0, nullptr, "GetDSiWareExportSize"},
+ {0x001B0144, nullptr, "ExportDSiWare"},
+ {0x001C0084, nullptr, "ImportDSiWare"},
+ {0x00230080, nullptr, "TitleIDListGetTotal2"},
+ {0x002400C2, nullptr, "GetTitleIDList2"},
+ {0x04010080, nullptr, "InstallFIRM"},
+ {0x04020040, nullptr, "StartInstallCIADB0"},
+ {0x04030000, nullptr, "StartInstallCIADB1"},
+ {0x04040002, nullptr, "AbortCIAInstall"},
+ {0x04050002, nullptr, "CloseCIAFinalizeInstall"},
+ {0x04060002, nullptr, "CloseCIA"},
+ {0x040700C2, nullptr, "FinalizeTitlesInstall"},
+ {0x04080042, nullptr, "GetCiaFileInfo"},
+ {0x040E00C2, nullptr, "InstallTitlesFinish"},
+ {0x040F0000, nullptr, "InstallNATIVEFIRM"},
+ {0x041000C0, nullptr, "DeleteTitle"},
+ {0x04120000, nullptr, "Initialize"},
+ {0x041700C0, nullptr, "MigrateAGBtoSAV"},
{0x08010000, nullptr, "OpenTicket"},
{0x08020002, nullptr, "TicketAbortInstall"},
{0x08030002, nullptr, "TicketFinalizeInstall"},
diff --git a/src/core/hle/service/am/am_sys.cpp b/src/core/hle/service/am/am_sys.cpp
index 864fc14df..e38812297 100644
--- a/src/core/hle/service/am/am_sys.cpp
+++ b/src/core/hle/service/am/am_sys.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/am_sys.h"
@@ -12,6 +11,21 @@ namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
{0x00020082, GetTitleIDList, "GetTitleIDList"},
+ {0x00030084, nullptr, "ListTitles"},
+ {0x000400C0, nullptr, "DeleteApplicationTitle"},
+ {0x000500C0, nullptr, "GetTitleProductCode"},
+ {0x00080000, nullptr, "TitleIDListGetTotal3"},
+ {0x00090082, nullptr, "GetTitleIDList3"},
+ {0x000A0000, nullptr, "GetDeviceID"},
+ {0x000D0084, nullptr, "ListTitles2"},
+ {0x00140040, nullptr, "FinishInstallToMedia"},
+ {0x00180080, nullptr, "InitializeTitleDatabase"},
+ {0x00190040, nullptr, "ReloadDBS"},
+ {0x001A00C0, nullptr, "GetDSiWareExportSize"},
+ {0x001B0144, nullptr, "ExportDSiWare"},
+ {0x001C0084, nullptr, "ImportDSiWare"},
+ {0x00230080, nullptr, "TitleIDListGetTotal2"},
+ {0x002400C2, nullptr, "GetTitleIDList2"}
};
AM_SYS_Interface::AM_SYS_Interface() {
diff --git a/src/core/hle/service/am/am_u.cpp b/src/core/hle/service/am/am_u.cpp
index 6bf84b36b..c0392b754 100644
--- a/src/core/hle/service/am/am_u.cpp
+++ b/src/core/hle/service/am/am_u.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/am_u.h"
@@ -12,6 +11,34 @@ namespace AM {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, TitleIDListGetTotal, "TitleIDListGetTotal"},
{0x00020082, GetTitleIDList, "GetTitleIDList"},
+ {0x00030084, nullptr, "ListTitles"},
+ {0x000400C0, nullptr, "DeleteApplicationTitle"},
+ {0x000500C0, nullptr, "GetTitleProductCode"},
+ {0x00080000, nullptr, "TitleIDListGetTotal3"},
+ {0x00090082, nullptr, "GetTitleIDList3"},
+ {0x000A0000, nullptr, "GetDeviceID"},
+ {0x000D0084, nullptr, "ListTitles2"},
+ {0x00140040, nullptr, "FinishInstallToMedia"},
+ {0x00180080, nullptr, "InitializeTitleDatabase"},
+ {0x00190040, nullptr, "ReloadDBS"},
+ {0x001A00C0, nullptr, "GetDSiWareExportSize"},
+ {0x001B0144, nullptr, "ExportDSiWare"},
+ {0x001C0084, nullptr, "ImportDSiWare"},
+ {0x00230080, nullptr, "TitleIDListGetTotal2"},
+ {0x002400C2, nullptr, "GetTitleIDList2"},
+ {0x04010080, nullptr, "InstallFIRM"},
+ {0x04020040, nullptr, "StartInstallCIADB0"},
+ {0x04030000, nullptr, "StartInstallCIADB1"},
+ {0x04040002, nullptr, "AbortCIAInstall"},
+ {0x04050002, nullptr, "CloseCIAFinalizeInstall"},
+ {0x04060002, nullptr, "CloseCIA"},
+ {0x040700C2, nullptr, "FinalizeTitlesInstall"},
+ {0x04080042, nullptr, "GetCiaFileInfo"},
+ {0x040E00C2, nullptr, "InstallTitlesFinish"},
+ {0x040F0000, nullptr, "InstallNATIVEFIRM"},
+ {0x041000C0, nullptr, "DeleteTitle"},
+ {0x04120000, nullptr, "Initialize"},
+ {0x041700C0, nullptr, "MigrateAGBtoSAV"}
};
AM_U_Interface::AM_U_Interface() {
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index feb579778..98c72fc32 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -14,12 +14,10 @@
#include "core/hle/service/apt/apt_u.h"
#include "core/hle/service/fs/archive.h"
-#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/kernel/thread.h"
namespace Service {
namespace APT {
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index 22800c56f..0c6a77305 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/apt/apt.h"
#include "core/hle/service/apt/apt_a.h"
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index 3ac6ff94f..7f6e81a63 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-
-#include "core/hle/hle.h"
#include "core/hle/service/apt/apt.h"
#include "core/hle/service/apt/apt_s.h"
@@ -91,6 +89,12 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x004E0000, nullptr, "HardwareResetAsync"},
{0x004F0080, nullptr, "SetApplicationCpuTimeLimit"},
{0x00500040, nullptr, "GetApplicationCpuTimeLimit"},
+ {0x00510080, nullptr, "GetStartupArgument"},
+ {0x00520104, nullptr, "Wrap1"},
+ {0x00530104, nullptr, "Unwrap1"},
+ {0x00580002, nullptr, "GetProgramID"},
+ {0x01010000, nullptr, "CheckNew3DSApp"},
+ {0x01020000, nullptr, "CheckNew3DS"}
};
APT_S_Interface::APT_S_Interface() {
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index 2e9d1f4b4..b13b51549 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -2,9 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-
-#include "common/file_util.h"
-
#include "core/hle/service/apt/apt.h"
#include "core/hle/service/apt/apt_u.h"
@@ -92,6 +89,12 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x004E0000, nullptr, "HardwareResetAsync"},
{0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"},
{0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"},
+ {0x00510080, nullptr, "GetStartupArgument"},
+ {0x00520104, nullptr, "Wrap1"},
+ {0x00530104, nullptr, "Unwrap1"},
+ {0x00580002, nullptr, "GetProgramID"},
+ {0x01010000, nullptr, "CheckNew3DSApp"},
+ {0x01020000, nullptr, "CheckNew3DS"}
};
APT_U_Interface::APT_U_Interface() {
diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp
index d38140f19..419ec976e 100644
--- a/src/core/hle/service/boss/boss.cpp
+++ b/src/core/hle/service/boss/boss.cpp
@@ -7,10 +7,6 @@
#include "core/hle/service/boss/boss_p.h"
#include "core/hle/service/boss/boss_u.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/hle.h"
-
namespace Service {
namespace BOSS {
diff --git a/src/core/hle/service/boss/boss.h b/src/core/hle/service/boss/boss.h
index a6942ada6..d3b5d7101 100644
--- a/src/core/hle/service/boss/boss.h
+++ b/src/core/hle/service/boss/boss.h
@@ -4,9 +4,6 @@
#pragma once
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/service.h"
-
namespace Service {
namespace BOSS {
diff --git a/src/core/hle/service/boss/boss_p.cpp b/src/core/hle/service/boss/boss_p.cpp
index 089f5f186..c498abe4e 100644
--- a/src/core/hle/service/boss/boss_p.cpp
+++ b/src/core/hle/service/boss/boss_p.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/boss/boss.h"
#include "core/hle/service/boss/boss_p.h"
namespace Service {
diff --git a/src/core/hle/service/boss/boss_u.cpp b/src/core/hle/service/boss/boss_u.cpp
index ed978b963..d59babe71 100644
--- a/src/core/hle/service/boss/boss_u.cpp
+++ b/src/core/hle/service/boss/boss_u.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/boss/boss.h"
#include "core/hle/service/boss/boss_u.h"
namespace Service {
@@ -11,6 +9,9 @@ namespace BOSS {
const Interface::FunctionInfo FunctionTable[] = {
{0x00020100, nullptr, "GetStorageInfo"},
+ {0x000C0082, nullptr, "UnregisterTask"},
+ {0x001E0042, nullptr, "CancelTask"},
+ {0x00330042, nullptr, "StartBgImmediate"},
};
BOSS_U_Interface::BOSS_U_Interface() {
diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp
index 4f34b699b..4d714037f 100644
--- a/src/core/hle/service/cam/cam.cpp
+++ b/src/core/hle/service/cam/cam.cpp
@@ -4,20 +4,287 @@
#include "common/logging/log.h"
-#include "core/hle/service/service.h"
+#include "core/hle/kernel/event.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cam/cam_c.h"
#include "core/hle/service/cam/cam_q.h"
#include "core/hle/service/cam/cam_s.h"
#include "core/hle/service/cam/cam_u.h"
-
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/hle.h"
+#include "core/hle/service/service.h"
namespace Service {
namespace CAM {
+static const u32 TRANSFER_BYTES = 5 * 1024;
+
+static Kernel::SharedPtr<Kernel::Event> completion_event_cam1;
+static Kernel::SharedPtr<Kernel::Event> completion_event_cam2;
+static Kernel::SharedPtr<Kernel::Event> interrupt_error_event;
+static Kernel::SharedPtr<Kernel::Event> vsync_interrupt_error_event;
+
+void StartCapture(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 port = cmd_buff[1] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port);
+}
+
+void StopCapture(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 port = cmd_buff[1] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port);
+}
+
+void GetVsyncInterruptEvent(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 port = cmd_buff[1] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = IPC::MoveHandleDesc();
+ cmd_buff[3] = Kernel::g_handle_table.Create(vsync_interrupt_error_event).MoveFrom();
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port);
+}
+
+void GetBufferErrorInterruptEvent(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 port = cmd_buff[1] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x6, 1, 2);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = IPC::MoveHandleDesc();
+ cmd_buff[3] = Kernel::g_handle_table.Create(interrupt_error_event).MoveFrom();
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port);
+}
+
+void SetReceiving(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ VAddr dest = cmd_buff[1];
+ u8 port = cmd_buff[2] & 0xFF;
+ u32 image_size = cmd_buff[3];
+ u16 trans_unit = cmd_buff[4] & 0xFFFF;
+
+ Kernel::Event* completion_event = (Port)port == Port::Cam2 ?
+ completion_event_cam2.get() : completion_event_cam1.get();
+
+ completion_event->Signal();
+
+ cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = IPC::MoveHandleDesc();
+ cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom();
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, addr=0x%X, port=%d, image_size=%d, trans_unit=%d",
+ dest, port, image_size, trans_unit);
+}
+
+void SetTransferLines(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 port = cmd_buff[1] & 0xFF;
+ u16 transfer_lines = cmd_buff[2] & 0xFFFF;
+ u16 width = cmd_buff[3] & 0xFFFF;
+ u16 height = cmd_buff[4] & 0xFFFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, lines=%d, width=%d, height=%d",
+ port, transfer_lines, width, height);
+}
+
+void GetMaxLines(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u16 width = cmd_buff[1] & 0xFFFF;
+ u16 height = cmd_buff[2] & 0xFFFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = TRANSFER_BYTES / (2 * width);
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, width=%d, height=%d, lines = %d",
+ width, height, cmd_buff[2]);
+}
+
+void GetTransferBytes(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 port = cmd_buff[1] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = TRANSFER_BYTES;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port);
+}
+
+void SetTrimming(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 port = cmd_buff[1] & 0xFF;
+ bool trim = (cmd_buff[2] & 0xFF) != 0;
+
+ cmd_buff[0] = IPC::MakeHeader(0xE, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, trim=%d", port, trim);
+}
+
+void SetTrimmingParamsCenter(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 port = cmd_buff[1] & 0xFF;
+ s16 trimW = cmd_buff[2] & 0xFFFF;
+ s16 trimH = cmd_buff[3] & 0xFFFF;
+ s16 camW = cmd_buff[4] & 0xFFFF;
+ s16 camH = cmd_buff[5] & 0xFFFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, trimW=%d, trimH=%d, camW=%d, camH=%d",
+ port, trimW, trimH, camW, camH);
+}
+
+void Activate(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 cam_select = cmd_buff[1] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d",
+ cam_select);
+}
+
+void FlipImage(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 cam_select = cmd_buff[1] & 0xFF;
+ u8 flip = cmd_buff[2] & 0xFF;
+ u8 context = cmd_buff[3] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x1D, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, flip=%d, context=%d",
+ cam_select, flip, context);
+}
+
+void SetSize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 cam_select = cmd_buff[1] & 0xFF;
+ u8 size = cmd_buff[2] & 0xFF;
+ u8 context = cmd_buff[3] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x1F, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, size=%d, context=%d",
+ cam_select, size, context);
+}
+
+void SetFrameRate(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 cam_select = cmd_buff[1] & 0xFF;
+ u8 frame_rate = cmd_buff[2] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, frame_rate=%d",
+ cam_select, frame_rate);
+}
+
+void GetStereoCameraCalibrationData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ // Default values taken from yuriks' 3DS. Valid data is required here or games using the
+ // calibration get stuck in an infinite CPU loop.
+ StereoCameraCalibrationData data = {};
+ data.isValidRotationXY = 0;
+ data.scale = 1.001776f;
+ data.rotationZ = 0.008322907f;
+ data.translationX = -87.70484f;
+ data.translationY = -7.640977f;
+ data.rotationX = 0.0f;
+ data.rotationY = 0.0f;
+ data.angleOfViewRight = 64.66875f;
+ data.angleOfViewLeft = 64.76067f;
+ data.distanceToChart = 250.0f;
+ data.distanceCameras = 35.0f;
+ data.imageWidth = 640;
+ data.imageHeight = 480;
+
+ cmd_buff[0] = IPC::MakeHeader(0x2B, 17, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ memcpy(&cmd_buff[2], &data, sizeof(data));
+
+ LOG_TRACE(Service_CAM, "called");
+}
+
+void GetSuitableY2rStandardCoefficient(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[0] = IPC::MakeHeader(0x36, 2, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 0;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called");
+}
+
+void PlayShutterSound(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u8 sound_id = cmd_buff[1] & 0xFF;
+
+ cmd_buff[0] = IPC::MakeHeader(0x38, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called, sound_id=%d", sound_id);
+}
+
+void DriverInitialize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ completion_event_cam1->Clear();
+ completion_event_cam2->Clear();
+ interrupt_error_event->Clear();
+ vsync_interrupt_error_event->Clear();
+
+ cmd_buff[0] = IPC::MakeHeader(0x39, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called");
+}
+
+void DriverFinalize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[0] = IPC::MakeHeader(0x3A, 1, 0);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_CAM, "(STUBBED) called");
+}
+
void Init() {
using namespace Kernel;
@@ -25,9 +292,18 @@ void Init() {
AddService(new CAM_Q_Interface);
AddService(new CAM_S_Interface);
AddService(new CAM_U_Interface);
+
+ completion_event_cam1 = Kernel::Event::Create(RESETTYPE_ONESHOT, "CAM_U::completion_event_cam1");
+ completion_event_cam2 = Kernel::Event::Create(RESETTYPE_ONESHOT, "CAM_U::completion_event_cam2");
+ interrupt_error_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "CAM_U::interrupt_error_event");
+ vsync_interrupt_error_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "CAM_U::vsync_interrupt_error_event");
}
void Shutdown() {
+ completion_event_cam1 = nullptr;
+ completion_event_cam2 = nullptr;
+ interrupt_error_event = nullptr;
+ vsync_interrupt_error_event = nullptr;
}
} // namespace CAM
diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h
index edd524841..2f4923728 100644
--- a/src/core/hle/service/cam/cam.h
+++ b/src/core/hle/service/cam/cam.h
@@ -4,12 +4,429 @@
#pragma once
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/service.h"
namespace Service {
namespace CAM {
+enum class Port : u8 {
+ None = 0,
+ Cam1 = 1,
+ Cam2 = 2,
+ Both = Cam1 | Cam2
+};
+
+enum class CameraSelect : u8 {
+ None = 0,
+ Out1 = 1,
+ In1 = 2,
+ Out2 = 4,
+ In1Out1 = Out1 | In1,
+ Out1Out2 = Out1 | Out2,
+ In1Out2 = In1 | Out2,
+ All = Out1 | In1 | Out2
+};
+
+enum class Effect : u8 {
+ None = 0,
+ Mono = 1,
+ Sepia = 2,
+ Negative = 3,
+ Negafilm = 4,
+ Sepia01 = 5
+};
+
+enum class Context : u8 {
+ None = 0,
+ A = 1,
+ B = 2,
+ Both = A | B
+};
+
+enum class Flip : u8 {
+ None = 0,
+ Horizontal = 1,
+ Vertical = 2,
+ Reverse = 3
+};
+
+enum class Size : u8 {
+ VGA = 0,
+ QVGA = 1,
+ QQVGA = 2,
+ CIF = 3,
+ QCIF = 4,
+ DS_LCD = 5,
+ DS_LCDx4 = 6,
+ CTR_TOP_LCD = 7,
+ CTR_BOTTOM_LCD = QVGA
+};
+
+enum class FrameRate : u8 {
+ Rate_15 = 0,
+ Rate_15_To_5 = 1,
+ Rate_15_To_2 = 2,
+ Rate_10 = 3,
+ Rate_8_5 = 4,
+ Rate_5 = 5,
+ Rate_20 = 6,
+ Rate_20_To_5 = 7,
+ Rate_30 = 8,
+ Rate_30_To_5 = 9,
+ Rate_15_To_10 = 10,
+ Rate_20_To_10 = 11,
+ Rate_30_To_10 = 12
+};
+
+enum class ShutterSoundType : u8 {
+ Normal = 0,
+ Movie = 1,
+ MovieEnd = 2
+};
+
+enum class WhiteBalance : u8 {
+ BalanceAuto = 0,
+ Balance3200K = 1,
+ Balance4150K = 2,
+ Balance5200K = 3,
+ Balance6000K = 4,
+ Balance7000K = 5,
+ BalanceMax = 6,
+ BalanceNormal = BalanceAuto,
+ BalanceTungsten = Balance3200K,
+ BalanceWhiteFluorescentLight = Balance4150K,
+ BalanceDaylight = Balance5200K,
+ BalanceCloudy = Balance6000K,
+ BalanceHorizon = Balance6000K,
+ BalanceShade = Balance7000K
+};
+
+enum class PhotoMode : u8 {
+ Normal = 0,
+ Portrait = 1,
+ Landscape = 2,
+ Nightview = 3,
+ Letter0 = 4
+};
+
+enum class LensCorrection : u8 {
+ Off = 0,
+ On70 = 1,
+ On90 = 2,
+ Dark = Off,
+ Normal = On70,
+ Bright = On90
+};
+
+enum class Contrast : u8 {
+ Pattern01 = 1,
+ Pattern02 = 2,
+ Pattern03 = 3,
+ Pattern04 = 4,
+ Pattern05 = 5,
+ Pattern06 = 6,
+ Pattern07 = 7,
+ Pattern08 = 8,
+ Pattern09 = 9,
+ Pattern10 = 10,
+ Pattern11 = 11,
+ Low = Pattern05,
+ Normal = Pattern06,
+ High = Pattern07
+};
+
+enum class OutputFormat : u8 {
+ YUV422 = 0,
+ RGB565 = 1
+};
+
+/// Stereo camera calibration data.
+struct StereoCameraCalibrationData {
+ u8 isValidRotationXY; ///< Bool indicating whether the X and Y rotation data is valid.
+ INSERT_PADDING_BYTES(3);
+ float_le scale; ///< Scale to match the left camera image with the right.
+ float_le rotationZ; ///< Z axis rotation to match the left camera image with the right.
+ float_le translationX; ///< X axis translation to match the left camera image with the right.
+ float_le translationY; ///< Y axis translation to match the left camera image with the right.
+ float_le rotationX; ///< X axis rotation to match the left camera image with the right.
+ float_le rotationY; ///< Y axis rotation to match the left camera image with the right.
+ float_le angleOfViewRight; ///< Right camera angle of view.
+ float_le angleOfViewLeft; ///< Left camera angle of view.
+ float_le distanceToChart; ///< Distance between cameras and measurement chart.
+ float_le distanceCameras; ///< Distance between left and right cameras.
+ s16_le imageWidth; ///< Image width.
+ s16_le imageHeight; ///< Image height.
+ INSERT_PADDING_BYTES(16);
+};
+static_assert(sizeof(StereoCameraCalibrationData) == 64, "StereoCameraCalibrationData structure size is wrong");
+
+struct PackageParameterCameraSelect {
+ CameraSelect camera;
+ s8 exposure;
+ WhiteBalance white_balance;
+ s8 sharpness;
+ bool auto_exposure;
+ bool auto_white_balance;
+ FrameRate frame_rate;
+ PhotoMode photo_mode;
+ Contrast contrast;
+ LensCorrection lens_correction;
+ bool noise_filter;
+ u8 padding;
+ s16 auto_exposure_window_x;
+ s16 auto_exposure_window_y;
+ s16 auto_exposure_window_width;
+ s16 auto_exposure_window_height;
+ s16 auto_white_balance_window_x;
+ s16 auto_white_balance_window_y;
+ s16 auto_white_balance_window_width;
+ s16 auto_white_balance_window_height;
+};
+
+static_assert(sizeof(PackageParameterCameraSelect) == 28, "PackageParameterCameraSelect structure size is wrong");
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00010040
+ * 1: u8 Camera port (`Port` enum)
+ * Outputs:
+ * 0: 0x00010040
+ * 1: ResultCode
+ */
+void StartCapture(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00020040
+ * 1: u8 Camera port (`Port` enum)
+ * Outputs:
+ * 0: 0x00020040
+ * 1: ResultCode
+ */
+void StopCapture(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00050040
+ * 1: u8 Camera port (`Port` enum)
+ * Outputs:
+ * 0: 0x00050042
+ * 1: ResultCode
+ * 2: Descriptor: Handle
+ * 3: Event handle
+ */
+void GetVsyncInterruptEvent(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00060040
+ * 1: u8 Camera port (`Port` enum)
+ * Outputs:
+ * 0: 0x00060042
+ * 1: ResultCode
+ * 2: Descriptor: Handle
+ * 3: Event handle
+ */
+void GetBufferErrorInterruptEvent(Service::Interface* self);
+
+/**
+ * Sets the target buffer to receive a frame of image data and starts the transfer. Each camera
+ * port has its own event to signal the end of the transfer.
+ *
+ * Inputs:
+ * 0: 0x00070102
+ * 1: Destination address in calling process
+ * 2: u8 Camera port (`Port` enum)
+ * 3: Image size (in bytes?)
+ * 4: u16 Transfer unit size (in bytes?)
+ * 5: Descriptor: Handle
+ * 6: Handle to destination process
+ * Outputs:
+ * 0: 0x00070042
+ * 1: ResultCode
+ * 2: Descriptor: Handle
+ * 3: Handle to event signalled when transfer finishes
+ */
+void SetReceiving(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00090100
+ * 1: u8 Camera port (`Port` enum)
+ * 2: u16 Number of lines to transfer
+ * 3: u16 Width
+ * 4: u16 Height
+ * Outputs:
+ * 0: 0x00090040
+ * 1: ResultCode
+ */
+void SetTransferLines(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x000A0080
+ * 1: u16 Width
+ * 2: u16 Height
+ * Outputs:
+ * 0: 0x000A0080
+ * 1: ResultCode
+ * 2: Maximum number of lines that fit in the buffer(?)
+ */
+void GetMaxLines(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x000C0040
+ * 1: u8 Camera port (`Port` enum)
+ * Outputs:
+ * 0: 0x000C0080
+ * 1: ResultCode
+ * 2: Total number of bytes for each frame with current settings(?)
+ */
+void GetTransferBytes(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x000E0080
+ * 1: u8 Camera port (`Port` enum)
+ * 2: u8 bool Enable trimming if true
+ * Outputs:
+ * 0: 0x000E0040
+ * 1: ResultCode
+ */
+void SetTrimming(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00120140
+ * 1: u8 Camera port (`Port` enum)
+ * 2: s16 Trim width(?)
+ * 3: s16 Trim height(?)
+ * 4: s16 Camera width(?)
+ * 5: s16 Camera height(?)
+ * Outputs:
+ * 0: 0x00120040
+ * 1: ResultCode
+ */
+void SetTrimmingParamsCenter(Service::Interface* self);
+
+/**
+ * Selects up to two physical cameras to enable.
+ * Inputs:
+ * 0: 0x00130040
+ * 1: u8 Cameras to activate (`CameraSelect` enum)
+ * Outputs:
+ * 0: 0x00130040
+ * 1: ResultCode
+ */
+void Activate(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x001D00C0
+ * 1: u8 Camera select (`CameraSelect` enum)
+ * 2: u8 Type of flipping to perform (`Flip` enum)
+ * 3: u8 Context (`Context` enum)
+ * Outputs:
+ * 0: 0x001D0040
+ * 1: ResultCode
+ */
+void FlipImage(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x001F00C0
+ * 1: u8 Camera select (`CameraSelect` enum)
+ * 2: u8 Camera frame resolution (`Size` enum)
+ * 3: u8 Context id (`Context` enum)
+ * Outputs:
+ * 0: 0x001F0040
+ * 1: ResultCode
+ */
+void SetSize(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00200080
+ * 1: u8 Camera select (`CameraSelect` enum)
+ * 2: u8 Camera framerate (`FrameRate` enum)
+ * Outputs:
+ * 0: 0x00200040
+ * 1: ResultCode
+ */
+void SetFrameRate(Service::Interface* self);
+
+/**
+ * Returns calibration data relating the outside cameras to eachother, for use in AR applications.
+ *
+ * Inputs:
+ * 0: 0x002B0000
+ * Outputs:
+ * 0: 0x002B0440
+ * 1: ResultCode
+ * 2-17: `StereoCameraCalibrationData` structure with calibration values
+ */
+void GetStereoCameraCalibrationData(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00360000
+ * Outputs:
+ * 0: 0x00360080
+ * 1: ResultCode
+ * 2: ?
+ */
+void GetSuitableY2rStandardCoefficient(Service::Interface* self);
+
+/**
+ * Unknown
+ * Inputs:
+ * 0: 0x00380040
+ * 1: u8 Sound ID
+ * Outputs:
+ * 0: 0x00380040
+ * 1: ResultCode
+ */
+void PlayShutterSound(Service::Interface* self);
+
+/**
+ * Initializes the camera driver. Must be called before using other functions.
+ * Inputs:
+ * 0: 0x00390000
+ * Outputs:
+ * 0: 0x00390040
+ * 1: ResultCode
+ */
+void DriverInitialize(Service::Interface* self);
+
+/**
+ * Shuts down the camera driver.
+ * Inputs:
+ * 0: 0x003A0000
+ * Outputs:
+ * 0: 0x003A0040
+ * 1: ResultCode
+ */
+void DriverFinalize(Service::Interface* self);
+
/// Initialize CAM service(s)
void Init();
diff --git a/src/core/hle/service/cam/cam_c.cpp b/src/core/hle/service/cam/cam_c.cpp
index d35adcb9f..8fa7abc85 100644
--- a/src/core/hle/service/cam/cam_c.cpp
+++ b/src/core/hle/service/cam/cam_c.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cam/cam_c.h"
namespace Service {
diff --git a/src/core/hle/service/cam/cam_q.cpp b/src/core/hle/service/cam/cam_q.cpp
index c2760a102..d3ba91e9d 100644
--- a/src/core/hle/service/cam/cam_q.cpp
+++ b/src/core/hle/service/cam/cam_q.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cam/cam_q.h"
namespace Service {
diff --git a/src/core/hle/service/cam/cam_s.cpp b/src/core/hle/service/cam/cam_s.cpp
index aefbf7df4..2a13984d8 100644
--- a/src/core/hle/service/cam/cam_s.cpp
+++ b/src/core/hle/service/cam/cam_s.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cam/cam_s.h"
namespace Service {
diff --git a/src/core/hle/service/cam/cam_u.cpp b/src/core/hle/service/cam/cam_u.cpp
index 55083e0c7..a1070ebb2 100644
--- a/src/core/hle/service/cam/cam_u.cpp
+++ b/src/core/hle/service/cam/cam_u.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/cam/cam.h"
#include "core/hle/service/cam/cam_u.h"
@@ -10,25 +9,25 @@ namespace Service {
namespace CAM {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, nullptr, "StartCapture"},
- {0x00020040, nullptr, "StopCapture"},
+ {0x00010040, StartCapture, "StartCapture"},
+ {0x00020040, StopCapture, "StopCapture"},
{0x00030040, nullptr, "IsBusy"},
{0x00040040, nullptr, "ClearBuffer"},
- {0x00050040, nullptr, "GetVsyncInterruptEvent"},
- {0x00060040, nullptr, "GetBufferErrorInterruptEvent"},
- {0x00070102, nullptr, "SetReceiving"},
+ {0x00050040, GetVsyncInterruptEvent, "GetVsyncInterruptEvent"},
+ {0x00060040, GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"},
+ {0x00070102, SetReceiving, "SetReceiving"},
{0x00080040, nullptr, "IsFinishedReceiving"},
- {0x00090100, nullptr, "SetTransferLines"},
- {0x000A0080, nullptr, "GetMaxLines"},
+ {0x00090100, SetTransferLines, "SetTransferLines"},
+ {0x000A0080, GetMaxLines, "GetMaxLines"},
{0x000B0100, nullptr, "SetTransferBytes"},
- {0x000C0040, nullptr, "GetTransferBytes"},
+ {0x000C0040, GetTransferBytes, "GetTransferBytes"},
{0x000D0080, nullptr, "GetMaxBytes"},
- {0x000E0080, nullptr, "SetTrimming"},
+ {0x000E0080, SetTrimming, "SetTrimming"},
{0x000F0040, nullptr, "IsTrimming"},
{0x00100140, nullptr, "SetTrimmingParams"},
{0x00110040, nullptr, "GetTrimmingParams"},
- {0x00120140, nullptr, "SetTrimmingParamsCenter"},
- {0x00130040, nullptr, "Activate"},
+ {0x00120140, SetTrimmingParamsCenter, "SetTrimmingParamsCenter"},
+ {0x00130040, Activate, "Activate"},
{0x00140080, nullptr, "SwitchContext"},
{0x00150080, nullptr, "SetExposure"},
{0x00160080, nullptr, "SetWhiteBalance"},
@@ -38,10 +37,10 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x001A0040, nullptr, "IsAutoExposure"},
{0x001B0080, nullptr, "SetAutoWhiteBalance"},
{0x001C0040, nullptr, "IsAutoWhiteBalance"},
- {0x001D00C0, nullptr, "FlipImage"},
+ {0x001D00C0, FlipImage, "FlipImage"},
{0x001E0200, nullptr, "SetDetailSize"},
- {0x001F00C0, nullptr, "SetSize"},
- {0x00200080, nullptr, "SetFrameRate"},
+ {0x001F00C0, SetSize, "SetSize"},
+ {0x00200080, SetFrameRate, "SetFrameRate"},
{0x00210080, nullptr, "SetPhotoMode"},
{0x002200C0, nullptr, "SetEffect"},
{0x00230080, nullptr, "SetContrast"},
@@ -52,17 +51,22 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00280080, nullptr, "SetNoiseFilter"},
{0x00290080, nullptr, "SynchronizeVsyncTiming"},
{0x002A0080, nullptr, "GetLatestVsyncTiming"},
- {0x002B0000, nullptr, "GetStereoCameraCalibrationData"},
+ {0x002B0000, GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
{0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
+ {0x002D00C0, nullptr, "WriteRegisterI2c"},
+ {0x002E00C0, nullptr, "WriteMcuVariableI2c"},
+ {0x002F0080, nullptr, "ReadRegisterI2cExclusive"},
+ {0x00300080, nullptr, "ReadMcuVariableI2cExclusive"},
{0x00310180, nullptr, "SetImageQualityCalibrationData"},
{0x00320000, nullptr, "GetImageQualityCalibrationData"},
{0x003302C0, nullptr, "SetPackageParameterWithoutContext"},
{0x00340140, nullptr, "SetPackageParameterWithContext"},
{0x003501C0, nullptr, "SetPackageParameterWithContextDetail"},
- {0x00360000, nullptr, "GetSuitableY2rStandardCoefficient"},
- {0x00380040, nullptr, "PlayShutterSound"},
- {0x00390000, nullptr, "DriverInitialize"},
- {0x003A0000, nullptr, "DriverFinalize"},
+ {0x00360000, GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"},
+ {0x00370202, nullptr, "PlayShutterSoundWithWave"},
+ {0x00380040, PlayShutterSound, "PlayShutterSound"},
+ {0x00390000, DriverInitialize, "DriverInitialize"},
+ {0x003A0000, DriverFinalize, "DriverFinalize"},
{0x003B0000, nullptr, "GetActivatedCamera"},
{0x003C0000, nullptr, "GetSleepCamera"},
{0x003D0040, nullptr, "SetSleepCamera"},
diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp
index db0e52b79..6d79ce9b4 100644
--- a/src/core/hle/service/cecd/cecd.cpp
+++ b/src/core/hle/service/cecd/cecd.cpp
@@ -9,10 +9,6 @@
#include "core/hle/service/cecd/cecd_s.h"
#include "core/hle/service/cecd/cecd_u.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/hle.h"
-
namespace Service {
namespace CECD {
diff --git a/src/core/hle/service/cecd/cecd.h b/src/core/hle/service/cecd/cecd.h
index 32fd2045d..9e158521b 100644
--- a/src/core/hle/service/cecd/cecd.h
+++ b/src/core/hle/service/cecd/cecd.h
@@ -4,9 +4,6 @@
#pragma once
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/service.h"
-
namespace Service {
namespace CECD {
diff --git a/src/core/hle/service/cecd/cecd_s.cpp b/src/core/hle/service/cecd/cecd_s.cpp
index 72d7e8d44..bfd821c07 100644
--- a/src/core/hle/service/cecd/cecd_s.cpp
+++ b/src/core/hle/service/cecd/cecd_s.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/cecd/cecd.h"
#include "core/hle/service/cecd/cecd_s.h"
namespace Service {
diff --git a/src/core/hle/service/cecd/cecd_u.cpp b/src/core/hle/service/cecd/cecd_u.cpp
index 0a23bafbc..9b720a738 100644
--- a/src/core/hle/service/cecd/cecd_u.cpp
+++ b/src/core/hle/service/cecd/cecd_u.cpp
@@ -2,18 +2,17 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/cecd/cecd.h"
#include "core/hle/service/cecd/cecd_u.h"
namespace Service {
namespace CECD {
-// Empty arrays are illegal -- commented out until an entry is added.
-//const Interface::FunctionInfo FunctionTable[] = { };
+static const Interface::FunctionInfo FunctionTable[] = {
+ { 0x00120104, nullptr, "ReadSavedData" },
+};
CECD_U_Interface::CECD_U_Interface() {
- //Register(FunctionTable);
+ Register(FunctionTable);
}
} // namespace CECD
diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp
index 56986a49e..4c82a58e4 100644
--- a/src/core/hle/service/cfg/cfg.cpp
+++ b/src/core/hle/service/cfg/cfg.cpp
@@ -4,13 +4,15 @@
#include <algorithm>
+#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "common/file_util.h"
+#include "common/swap.h"
#include "core/file_sys/archive_systemsavedata.h"
#include "core/file_sys/file_backend.h"
#include "core/settings.h"
+#include "core/hle/result.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_i.h"
#include "core/hle/service/cfg/cfg_s.h"
@@ -292,8 +294,8 @@ ResultCode DeleteConfigNANDSaveFile() {
ResultCode UpdateConfigNANDSavegame() {
FileSys::Mode mode = {};
- mode.write_flag = 1;
- mode.create_flag = 1;
+ mode.write_flag.Assign(1);
+ mode.create_flag.Assign(1);
FileSys::Path path("config");
@@ -333,6 +335,18 @@ ResultCode FormatConfig() {
res = CreateConfigInfoBlk(0x000A0000, sizeof(CONSOLE_USERNAME_BLOCK), 0xE, &CONSOLE_USERNAME_BLOCK);
if (!res.IsSuccess()) return res;
+ // 0x000A0000 - Profile username
+ struct {
+ u16_le username[10];
+ u8 unused[4];
+ u32_le wordfilter_version; // Unused by Citra
+ } profile_username = {};
+
+ std::u16string username_string = Common::UTF8ToUTF16("Citra");
+ std::copy(username_string.cbegin(), username_string.cend(), profile_username.username);
+ res = CreateConfigInfoBlk(0x000A0000, sizeof(profile_username), 0xE, &profile_username);
+ if (!res.IsSuccess()) return res;
+
// 0x000A0001 - Profile birthday
const u8 profile_birthday[2] = {3, 25}; // March 25th, 2014
res = CreateConfigInfoBlk(0x000A0001, sizeof(profile_birthday), 0xE, profile_birthday);
@@ -343,9 +357,10 @@ ResultCode FormatConfig() {
res = CreateConfigInfoBlk(0x000B0000, sizeof(COUNTRY_INFO), 0xE, &COUNTRY_INFO);
if (!res.IsSuccess()) return res;
- char16_t country_name_buffer[16][0x40] = {};
+ u16_le country_name_buffer[16][0x40] = {};
+ std::u16string region_name = Common::UTF8ToUTF16("Gensokyo");
for (size_t i = 0; i < 16; ++i) {
- Common::UTF8ToUTF16("Gensokyo").copy(country_name_buffer[i], 0x40);
+ std::copy(region_name.cbegin(), region_name.cend(), country_name_buffer[i]);
}
// 0x000B0001 - Localized names for the profile Country
res = CreateConfigInfoBlk(0x000B0001, sizeof(country_name_buffer), 0xE, country_name_buffer);
@@ -404,7 +419,7 @@ void Init() {
FileSys::Path config_path("config");
FileSys::Mode open_mode = {};
- open_mode.read_flag = 1;
+ open_mode.read_flag.Assign(1);
auto config_result = Service::FS::OpenFileFromArchive(*archive_result, config_path, open_mode);
diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h
index fc2a16a04..606ab99cf 100644
--- a/src/core/hle/service/cfg/cfg.h
+++ b/src/core/hle/service/cfg/cfg.h
@@ -5,10 +5,15 @@
#pragma once
#include <array>
-#include "core/hle/result.h"
-#include "core/hle/service/service.h"
+
+#include "common/common_types.h"
+
+union ResultCode;
namespace Service {
+
+class Interface;
+
namespace CFG {
enum SystemModel {
diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp
index 5aeadc084..0559a07b2 100644
--- a/src/core/hle/service/cfg/cfg_i.cpp
+++ b/src/core/hle/service/cfg/cfg_i.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_i.h"
diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp
index a329514a6..b03d290e5 100644
--- a/src/core/hle/service/cfg/cfg_s.cpp
+++ b/src/core/hle/service/cfg/cfg_s.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_s.h"
diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp
index 18939750b..89ae96c9e 100644
--- a/src/core/hle/service/cfg/cfg_u.cpp
+++ b/src/core/hle/service/cfg/cfg_u.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/cfg/cfg.h"
#include "core/hle/service/cfg/cfg_u.h"
diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp
index 669659510..6318bf2a7 100644
--- a/src/core/hle/service/csnd_snd.cpp
+++ b/src/core/hle/service/csnd_snd.cpp
@@ -22,9 +22,10 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00060000, nullptr, "ReleaseSoundChannels"},
{0x00070000, nullptr, "AcquireCaptureDevice"},
{0x00080040, nullptr, "ReleaseCaptureDevice"},
- {0x00090082, nullptr, "FlushDCache"},
- {0x000A0082, nullptr, "StoreDCache"},
- {0x000B0082, nullptr, "InvalidateDCache"},
+ {0x00090082, nullptr, "FlushDataCache"},
+ {0x000A0082, nullptr, "StoreDataCache"},
+ {0x000B0082, nullptr, "InvalidateDataCache"},
+ {0x000C0000, nullptr, "Reset"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp
index ce5619069..3ba24d466 100644
--- a/src/core/hle/service/dsp_dsp.cpp
+++ b/src/core/hle/service/dsp_dsp.cpp
@@ -2,9 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cinttypes>
+
+#include "audio_core/hle/pipe.h"
+
+#include "common/hash.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/dsp_dsp.h"
@@ -15,17 +19,30 @@ namespace DSP_DSP {
static u32 read_pipe_count;
static Kernel::SharedPtr<Kernel::Event> semaphore_event;
-static Kernel::SharedPtr<Kernel::Event> interrupt_event;
-void SignalInterrupt() {
- // TODO(bunnei): This is just a stub, it does not do anything other than signal to the emulated
- // application that a DSP interrupt occurred, without specifying which one. Since we do not
- // emulate the DSP yet (and how it works is largely unknown), this is a work around to get games
- // 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.
+struct PairHash {
+ template <typename T, typename U>
+ std::size_t operator()(const std::pair<T, U> &x) const {
+ // TODO(yuriks): Replace with better hash combining function.
+ return std::hash<T>()(x.first) ^ std::hash<U>()(x.second);
+ }
+};
+
+/// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents
+static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events;
+
+// DSP Interrupts:
+// Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting
+// for an interrupt event. Immediately after this interrupt event, userland normally updates the
+// state in the next region and increments the relevant frame counter by two.
+void SignalAllInterrupts() {
+ // HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case.
+ for (auto& interrupt_event : interrupt_events)
+ interrupt_event.second->Signal();
+}
- if (interrupt_event != 0)
- interrupt_event->Signal();
+void SignalInterrupt(u32 interrupt, u32 channel) {
+ interrupt_events[std::make_pair(interrupt, channel)]->Signal();
}
/**
@@ -41,18 +58,18 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) {
u32 addr = cmd_buff[1];
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000);
- LOG_WARNING(Service_DSP, "(STUBBED) called with address 0x%08X", addr);
+ LOG_DEBUG(Service_DSP, "addr=0x%08X", addr);
}
/**
* DSP_DSP::LoadComponent service function
* Inputs:
* 1 : Size
- * 2 : Unknown (observed only half word used)
- * 3 : Unknown (observed only half word used)
+ * 2 : Program mask (observed only half word used)
+ * 3 : Data mask (observed only half word used)
* 4 : (size << 4) | 0xA
* 5 : Buffer address
* Outputs:
@@ -63,18 +80,28 @@ static void LoadComponent(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 size = cmd_buff[1];
- u32 unk1 = cmd_buff[2];
- u32 unk2 = cmd_buff[3];
- u32 new_size = cmd_buff[4];
+ u32 prog_mask = cmd_buff[2];
+ u32 data_mask = cmd_buff[3];
+ u32 desc = cmd_buff[4];
u32 buffer = cmd_buff[5];
- cmd_buff[1] = 0; // No error
+ cmd_buff[0] = IPC::MakeHeader(0x11, 2, 2);
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware
+ cmd_buff[3] = desc;
+ cmd_buff[4] = buffer;
// TODO(bunnei): Implement real DSP firmware loading
- LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, unk1=0x%08X, unk2=0x%08X, new_size=0x%X, buffer=0x%08X",
- size, unk1, unk2, new_size, buffer);
+ ASSERT(Memory::GetPointer(buffer) != nullptr);
+ ASSERT(size > 0x37C);
+
+ LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer), size));
+ // Some versions of the firmware have the location of DSP structures listed here.
+ LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, Common::ComputeHash64(Memory::GetPointer(buffer) + 0x340, 60));
+
+ LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X",
+ size, prog_mask, data_mask, buffer);
}
/**
@@ -115,15 +142,14 @@ static void FlushDataCache(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_DEBUG(Service_DSP, "(STUBBED) called address=0x%08X, size=0x%X, process=0x%08X",
- address, size, process);
+ LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, process);
}
/**
* DSP_DSP::RegisterInterruptEvents service function
* Inputs:
- * 1 : Parameter 0 (purpose unknown)
- * 2 : Parameter 1 (purpose unknown)
+ * 1 : Interrupt Number
+ * 2 : Channel Number
* 4 : Interrupt event handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
@@ -131,37 +157,37 @@ static void FlushDataCache(Service::Interface* self) {
static void RegisterInterruptEvents(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 param0 = cmd_buff[1];
- u32 param1 = cmd_buff[2];
+ u32 interrupt = cmd_buff[1];
+ u32 channel = cmd_buff[2];
u32 event_handle = cmd_buff[4];
- auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
- if (evt != nullptr) {
- interrupt_event = evt;
- cmd_buff[1] = 0; // No error
+ if (event_handle) {
+ auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]);
+ if (evt) {
+ interrupt_events[std::make_pair(interrupt, channel)] = evt;
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ LOG_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
+ } else {
+ LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
+ ASSERT(false); // This should really be handled at a IPC translation layer.
+ }
} else {
- LOG_ERROR(Service_DSP, "called with invalid handle=%08X", cmd_buff[4]);
-
- // TODO(yuriks): An error should be returned from SendSyncRequest, not in the cmdbuf
- cmd_buff[1] = -1;
+ interrupt_events.erase(std::make_pair(interrupt, channel));
+ LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle);
}
-
- LOG_WARNING(Service_DSP, "(STUBBED) called param0=%u, param1=%u, event_handle=0x%08X", param0, param1, event_handle);
}
/**
- * DSP_DSP::WriteReg0x10 service function
+ * DSP_DSP::SetSemaphore service function
* Inputs:
* 1 : Unknown (observed only half word used)
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
-static void WriteReg0x10(Service::Interface* self) {
+static void SetSemaphore(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- SignalInterrupt();
-
- cmd_buff[1] = 0; // No error
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
LOG_WARNING(Service_DSP, "(STUBBED) called");
}
@@ -169,9 +195,9 @@ static void WriteReg0x10(Service::Interface* self) {
/**
* DSP_DSP::WriteProcessPipe service function
* Inputs:
- * 1 : Number
+ * 1 : Channel
* 2 : Size
- * 3 : (size <<14) | 0x402
+ * 3 : (size << 14) | 0x402
* 4 : Buffer
* Outputs:
* 0 : Return header
@@ -180,24 +206,36 @@ static void WriteReg0x10(Service::Interface* self) {
static void WriteProcessPipe(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 number = cmd_buff[1];
- u32 size = cmd_buff[2];
- u32 new_size = cmd_buff[3];
- u32 buffer = cmd_buff[4];
+ DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]);
+ u32 size = cmd_buff[2];
+ u32 buffer = cmd_buff[4];
+
+ ASSERT_MSG(IPC::StaticBufferDesc(size, 1) == cmd_buff[3], "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer);
+ ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
+
+ std::vector<u8> message(size);
+
+ for (size_t i = 0; i < size; i++) {
+ message[i] = Memory::Read8(buffer + i);
+ }
+
+ DSP::HLE::PipeWrite(pipe, message);
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_WARNING(Service_DSP, "(STUBBED) called number=%u, size=0x%X, new_size=0x%X, buffer=0x%08X",
- number, size, new_size, buffer);
+ LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer);
}
/**
* DSP_DSP::ReadPipeIfPossible service function
+ * A pipe is a means of communication between the ARM11 and DSP that occurs on
+ * hardware by writing to/reading from the DSP registers at 0x10203000.
+ * Pipes are used for initialisation. See also DSP::HLE::PipeRead.
* Inputs:
- * 1 : Unknown
+ * 1 : Pipe Number
* 2 : Unknown
* 3 : Size in bytes of read (observed only lower half word used)
- * 0x41 : Virtual address to read from DSP pipe to in memory
+ * 0x41 : Virtual address of memory buffer to write pipe contents to
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
* 2 : Number of bytes read from pipe
@@ -205,35 +243,82 @@ static void WriteProcessPipe(Service::Interface* self) {
static void ReadPipeIfPossible(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 unk1 = cmd_buff[1];
- u32 unk2 = cmd_buff[2];
- u32 size = cmd_buff[3] & 0xFFFF;// Lower 16 bits are size
+ DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]);
+ u32 unknown = cmd_buff[2];
+ u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
VAddr addr = cmd_buff[0x41];
- // Canned DSP responses that games expect. These were taken from HW by 3dmoo team.
- // TODO: Remove this hack :)
- static const std::array<u16, 16> canned_read_pipe = {{
- 0x000F, 0xBFFF, 0x9E8E, 0x8680, 0xA78E, 0x9430, 0x8400, 0x8540,
- 0x948E, 0x8710, 0x8410, 0xA90E, 0xAA0E, 0xAACE, 0xAC4E, 0xAC58
- }};
+ ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
- u32 initial_size = read_pipe_count;
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
+ std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
- for (unsigned offset = 0; offset < size; offset += sizeof(u16)) {
- if (read_pipe_count < canned_read_pipe.size()) {
- Memory::Write16(addr + offset, canned_read_pipe[read_pipe_count]);
- read_pipe_count++;
- } else {
- LOG_ERROR(Service_DSP, "canned read pipe log exceeded!");
- break;
- }
+ Memory::WriteBlock(addr, response.data(), response.size());
+
+ cmd_buff[2] = static_cast<u32>(response.size());
+ } else {
+ cmd_buff[2] = 0; // Return no data
}
- cmd_buff[1] = 0; // No error
- cmd_buff[2] = (read_pipe_count - initial_size) * sizeof(u16);
+ LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]);
+}
- LOG_WARNING(Service_DSP, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, size=0x%X, buffer=0x%08X",
- unk1, unk2, size, addr);
+/**
+ * DSP_DSP::ReadPipe service function
+ * Inputs:
+ * 1 : Pipe Number
+ * 2 : Unknown
+ * 3 : Size in bytes of read (observed only lower half word used)
+ * 0x41 : Virtual address of memory buffer to write pipe contents to
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Number of bytes read from pipe
+ */
+static void ReadPipe(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]);
+ u32 unknown = cmd_buff[2];
+ u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size
+ VAddr addr = cmd_buff[0x41];
+
+ ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr);
+
+ if (DSP::HLE::GetPipeReadableSize(pipe) >= size) {
+ std::vector<u8> response = DSP::HLE::PipeRead(pipe, size);
+
+ Memory::WriteBlock(addr, response.data(), response.size());
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = static_cast<u32>(response.size());
+ } else {
+ // No more data is in pipe. Hardware hangs in this case; this should never happen.
+ UNREACHABLE();
+ }
+
+ LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]);
+}
+
+/**
+ * DSP_DSP::GetPipeReadableSize service function
+ * Inputs:
+ * 1 : Pipe Number
+ * 2 : Unknown
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Number of bytes readable from pipe
+ */
+static void GetPipeReadableSize(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]);
+ u32 unknown = cmd_buff[2];
+
+ cmd_buff[1] = RESULT_SUCCESS.raw; // No error
+ cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe);
+
+ LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, cmd_buff[2]);
}
/**
@@ -268,20 +353,86 @@ static void GetHeadphoneStatus(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
cmd_buff[2] = 0; // Not using headphones?
- LOG_DEBUG(Service_DSP, "(STUBBED) called");
+ LOG_WARNING(Service_DSP, "(STUBBED) called");
+}
+
+/**
+ * DSP_DSP::RecvData service function
+ * This function reads a value out of a DSP register.
+ * Inputs:
+ * 1 : Register Number
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Value in the register
+ * Notes:
+ * This function has only been observed being called with a register number of 0.
+ */
+static void RecvData(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 register_number = cmd_buff[1];
+
+ ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number);
+
+ // Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept.
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ switch (DSP::HLE::GetDspState()) {
+ case DSP::HLE::DspState::On:
+ cmd_buff[2] = 0;
+ break;
+ case DSP::HLE::DspState::Off:
+ case DSP::HLE::DspState::Sleeping:
+ cmd_buff[2] = 1;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ LOG_DEBUG(Service_DSP, "register_number=%u", register_number);
+}
+
+/**
+ * DSP_DSP::RecvDataIsReady service function
+ * This function checks whether a DSP register is ready to be read.
+ * Inputs:
+ * 1 : Register Number
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : non-zero == ready
+ * Note:
+ * This function has only been observed being called with a register number of 0.
+ */
+static void RecvDataIsReady(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 register_number = cmd_buff[1];
+
+ ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number);
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[2] = 1; // Ready to read
+
+ LOG_DEBUG(Service_DSP, "register_number=%u", register_number);
}
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010040, nullptr, "RecvData"},
- {0x00020040, nullptr, "RecvDataIsReady"},
+ {0x00010040, RecvData, "RecvData"},
+ {0x00020040, RecvDataIsReady, "RecvDataIsReady"},
{0x00030080, nullptr, "SendData"},
{0x00040040, nullptr, "SendDataIsEmpty"},
- {0x00070040, WriteReg0x10, "WriteReg0x10"},
+ {0x000500C2, nullptr, "SendFifoEx"},
+ {0x000600C0, nullptr, "RecvFifoEx"},
+ {0x00070040, SetSemaphore, "SetSemaphore"},
{0x00080000, nullptr, "GetSemaphore"},
{0x00090040, nullptr, "ClearSemaphore"},
+ {0x000A0040, nullptr, "MaskSemaphore"},
{0x000B0000, nullptr, "CheckSemaphoreRequest"},
{0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"},
{0x000D0082, WriteProcessPipe, "WriteProcessPipe"},
+ {0x000E00C0, ReadPipe, "ReadPipe"},
+ {0x000F0080, GetPipeReadableSize, "GetPipeReadableSize"},
{0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"},
{0x001100C2, LoadComponent, "LoadComponent"},
{0x00120000, nullptr, "UnloadComponent"},
@@ -295,7 +446,10 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"},
{0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"},
{0x001C0082, nullptr, "SetIirFilterEQ"},
+ {0x001D00C0, nullptr, "ReadMultiEx_SPI2"},
+ {0x001E00C2, nullptr, "WriteMultiEx_SPI2"},
{0x001F0000, GetHeadphoneStatus, "GetHeadphoneStatus"},
+ {0x00200040, nullptr, "ForceHeadphoneOut"},
{0x00210000, nullptr, "GetIsDspOccupied"},
};
@@ -304,7 +458,6 @@ const Interface::FunctionInfo FunctionTable[] = {
Interface::Interface() {
semaphore_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event");
- interrupt_event = nullptr;
read_pipe_count = 0;
Register(FunctionTable);
@@ -312,7 +465,7 @@ Interface::Interface() {
Interface::~Interface() {
semaphore_event = nullptr;
- interrupt_event = nullptr;
+ interrupt_events.clear();
}
} // namespace
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h
index b6f611db5..32b89e9bb 100644
--- a/src/core/hle/service/dsp_dsp.h
+++ b/src/core/hle/service/dsp_dsp.h
@@ -23,7 +23,15 @@ public:
}
};
-/// Signals that a DSP interrupt has occurred to userland code
-void SignalInterrupt();
+/// Signal all audio related interrupts.
+void SignalAllInterrupts();
+
+/**
+ * Signal a specific audio related interrupt based on interrupt id and channel id.
+ * @param interrupt_id The interrupt id
+ * @param channel_id The channel id
+ * The significance of various values of interrupt_id and channel_id is not yet known.
+ */
+void SignalInterrupt(u32 interrupt_id, u32 channel_id);
} // namespace
diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp
index e8c06c1cf..5f9cf6e94 100644
--- a/src/core/hle/service/err_f.cpp
+++ b/src/core/hle/service/err_f.cpp
@@ -2,9 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/bit_field.h"
+#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
#include "core/hle/service/err_f.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp
index 2911ab402..c13ffd9d2 100644
--- a/src/core/hle/service/frd/frd.cpp
+++ b/src/core/hle/service/frd/frd.cpp
@@ -7,10 +7,6 @@
#include "core/hle/service/frd/frd_a.h"
#include "core/hle/service/frd/frd_u.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/hle.h"
-
namespace Service {
namespace FRD {
diff --git a/src/core/hle/service/frd/frd.h b/src/core/hle/service/frd/frd.h
index 41f7a2f6b..f9f88b444 100644
--- a/src/core/hle/service/frd/frd.h
+++ b/src/core/hle/service/frd/frd.h
@@ -4,9 +4,6 @@
#pragma once
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/service.h"
-
namespace Service {
namespace FRD {
diff --git a/src/core/hle/service/frd/frd_a.cpp b/src/core/hle/service/frd/frd_a.cpp
index 1c438a337..818d610f3 100644
--- a/src/core/hle/service/frd/frd_a.cpp
+++ b/src/core/hle/service/frd/frd_a.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/frd/frd.h"
#include "core/hle/service/frd/frd_a.h"
namespace Service {
diff --git a/src/core/hle/service/frd/frd_u.cpp b/src/core/hle/service/frd/frd_u.cpp
index 3a5897d06..2c6885377 100644
--- a/src/core/hle/service/frd/frd_u.cpp
+++ b/src/core/hle/service/frd/frd_u.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/frd/frd.h"
#include "core/hle/service/frd/frd_u.h"
namespace Service {
@@ -11,25 +9,58 @@ namespace FRD {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010000, nullptr, "HasLoggedIn"},
+ {0x00020000, nullptr, "IsOnline"},
{0x00030000, nullptr, "Login"},
{0x00040000, nullptr, "Logout"},
- {0x00050000, nullptr, "GetFriendKey"},
+ {0x00050000, nullptr, "GetMyFriendKey"},
+ {0x00060000, nullptr, "GetMyPreference"},
+ {0x00070000, nullptr, "GetMyProfile"},
{0x00080000, nullptr, "GetMyPresence"},
{0x00090000, nullptr, "GetMyScreenName"},
- {0x00100040, nullptr, "GetPassword"},
+ {0x000A0000, nullptr, "GetMyMii"},
+ {0x000B0000, nullptr, "GetMyLocalAccountId"},
+ {0x000C0000, nullptr, "GetMyPlayingGame"},
+ {0x000D0000, nullptr, "GetMyFavoriteGame"},
+ {0x000E0000, nullptr, "GetMyNcPrincipalId"},
+ {0x000F0000, nullptr, "GetMyComment"},
+ {0x00100040, nullptr, "GetMyPassword"},
{0x00110080, nullptr, "GetFriendKeyList"},
+ {0x00120042, nullptr, "GetFriendPresence"},
+ {0x00130142, nullptr, "GetFriendScreenName"},
+ {0x00140044, nullptr, "GetFriendMii"},
+ {0x00150042, nullptr, "GetFriendProfile"},
+ {0x00160042, nullptr, "GetFriendRelationship"},
+ {0x00170042, nullptr, "GetFriendAttributeFlags"},
+ {0x00180044, nullptr, "GetFriendPlayingGame"},
{0x00190042, nullptr, "GetFriendFavoriteGame"},
{0x001A00C4, nullptr, "GetFriendInfo"},
- {0x001B0080, nullptr, "IsOnFriendList"},
- {0x001C0042, nullptr, "DecodeLocalFriendCode"},
- {0x001D0002, nullptr, "SetCurrentlyPlayingText"},
+ {0x001B0080, nullptr, "IsIncludedInFriendList"},
+ {0x001C0042, nullptr, "UnscrambleLocalFriendCode"},
+ {0x001D0002, nullptr, "UpdateGameModeDescription"},
+ {0x001E02C2, nullptr, "UpdateGameMode"},
+ {0x001F0042, nullptr, "SendInvitation"},
+ {0x00200002, nullptr, "AttachToEventNotification"},
+ {0x00210040, nullptr, "SetNotificationMask"},
+ {0x00220040, nullptr, "GetEventNotification"},
{0x00230000, nullptr, "GetLastResponseResult"},
+ {0x00240040, nullptr, "PrincipalIdToFriendCode"},
+ {0x00250080, nullptr, "FriendCodeToPrincipalId"},
+ {0x00260080, nullptr, "IsValidFriendCode"},
{0x00270040, nullptr, "ResultToErrorCode"},
{0x00280244, nullptr, "RequestGameAuthentication"},
{0x00290000, nullptr, "GetGameAuthenticationData"},
{0x002A0204, nullptr, "RequestServiceLocator"},
{0x002B0000, nullptr, "GetServiceLocatorData"},
+ {0x002C0002, nullptr, "DetectNatProperties"},
+ {0x002D0000, nullptr, "GetNatProperties"},
+ {0x002E0000, nullptr, "GetServerTimeInterval"},
+ {0x002F0040, nullptr, "AllowHalfAwake"},
+ {0x00300000, nullptr, "GetServerTypes"},
+ {0x00310082, nullptr, "GetFriendComment"},
{0x00320042, nullptr, "SetClientSdkVersion"},
+ {0x00330000, nullptr, "GetMyApproachContext"},
+ {0x00340046, nullptr, "AddFriendWithApproach"},
+ {0x00350082, nullptr, "DecryptApproachContext"},
};
FRD_U_Interface::FRD_U_Interface() {
diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp
index b3fa89302..632620a56 100644
--- a/src/core/hle/service/fs/fs_user.cpp
+++ b/src/core/hle/service/fs/fs_user.cpp
@@ -708,96 +708,114 @@ static void GetPriority(Service::Interface* self) {
}
const Interface::FunctionInfo FunctionTable[] = {
- {0x000100C6, nullptr, "Dummy1"},
- {0x040100C4, nullptr, "Control"},
- {0x08010002, Initialize, "Initialize"},
- {0x080201C2, OpenFile, "OpenFile"},
- {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
- {0x08040142, DeleteFile, "DeleteFile"},
- {0x08050244, RenameFile, "RenameFile"},
- {0x08060142, DeleteDirectory, "DeleteDirectory"},
- {0x08070142, nullptr, "DeleteDirectoryRecursively"},
- {0x08080202, CreateFile, "CreateFile"},
- {0x08090182, CreateDirectory, "CreateDirectory"},
- {0x080A0244, RenameDirectory, "RenameDirectory"},
- {0x080B0102, OpenDirectory, "OpenDirectory"},
- {0x080C00C2, OpenArchive, "OpenArchive"},
- {0x080D0144, nullptr, "ControlArchive"},
- {0x080E0080, CloseArchive, "CloseArchive"},
- {0x080F0180, FormatThisUserSaveData,"FormatThisUserSaveData"},
- {0x08100200, nullptr, "CreateSystemSaveData"},
- {0x08110040, nullptr, "DeleteSystemSaveData"},
- {0x08120080, GetFreeBytes, "GetFreeBytes"},
- {0x08130000, nullptr, "GetCardType"},
- {0x08140000, nullptr, "GetSdmcArchiveResource"},
- {0x08150000, nullptr, "GetNandArchiveResource"},
- {0x08160000, nullptr, "GetSdmcFatfsErro"},
- {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
- {0x08180000, IsSdmcWriteable, "IsSdmcWritable"},
- {0x08190042, nullptr, "GetSdmcCid"},
- {0x081A0042, nullptr, "GetNandCid"},
- {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
- {0x081C0000, nullptr, "GetNandSpeedInfo"},
- {0x081D0042, nullptr, "GetSdmcLog"},
- {0x081E0042, nullptr, "GetNandLog"},
- {0x081F0000, nullptr, "ClearSdmcLog"},
- {0x08200000, nullptr, "ClearNandLog"},
- {0x08210000, CardSlotIsInserted, "CardSlotIsInserted"},
- {0x08220000, nullptr, "CardSlotPowerOn"},
- {0x08230000, nullptr, "CardSlotPowerOff"},
- {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
- {0x08250040, nullptr, "CardNorDirectCommand"},
- {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
- {0x08270082, nullptr, "CardNorDirectRead"},
- {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
- {0x08290082, nullptr, "CardNorDirectWrite"},
- {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
- {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
- {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
- {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
- {0x082E0040, nullptr, "GetProductInfo"},
- {0x082F0040, nullptr, "GetProgramLaunchInfo"},
- {0x08300182, nullptr, "CreateExtSaveData"},
- {0x08310180, nullptr, "CreateSharedExtSaveData"},
- {0x08320102, nullptr, "ReadExtSaveDataIcon"},
- {0x08330082, nullptr, "EnumerateExtSaveData"},
- {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
- {0x08350080, nullptr, "DeleteExtSaveData"},
- {0x08360080, nullptr, "DeleteSharedExtSaveData"},
- {0x08370040, nullptr, "SetCardSpiBaudRate"},
- {0x08380040, nullptr, "SetCardSpiBusMode"},
- {0x08390000, nullptr, "SendInitializeInfoTo9"},
- {0x083A0100, nullptr, "GetSpecialContentIndex"},
- {0x083B00C2, nullptr, "GetLegacyRomHeader"},
- {0x083C00C2, nullptr, "GetLegacyBannerData"},
- {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
- {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
- {0x083F00C0, nullptr, "GetExtDataBlockSize"},
- {0x08400040, nullptr, "AbnegateAccessRight"},
- {0x08410000, nullptr, "DeleteSdmcRoot"},
- {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
- {0x08430000, nullptr, "InitializeCtrFileSystem"},
- {0x08440000, nullptr, "CreateSeed"},
- {0x084500C2, nullptr, "GetFormatInfo"},
- {0x08460102, nullptr, "GetLegacyRomHeader2"},
- {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
- {0x08480042, nullptr, "GetSdmcCtrRootPath"},
- {0x08490040, nullptr, "GetArchiveResource"},
- {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
- {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
- {0x084C0242, FormatSaveData, "FormatSaveData"},
- {0x084D0102, nullptr, "GetLegacySubBannerData"},
- {0x084E0342, nullptr, "UpdateSha256Context"},
- {0x084F0102, nullptr, "ReadSpecialFile"},
- {0x08500040, nullptr, "GetSpecialFileSize"},
- {0x08510242, CreateExtSaveData, "CreateExtSaveData"},
- {0x08520100, DeleteExtSaveData, "DeleteExtSaveData"},
- {0x08560240, CreateSystemSaveData, "CreateSystemSaveData"},
- {0x08570080, DeleteSystemSaveData, "DeleteSystemSaveData"},
- {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"},
+ {0x000100C6, nullptr, "Dummy1"},
+ {0x040100C4, nullptr, "Control"},
+ {0x08010002, Initialize, "Initialize"},
+ {0x080201C2, OpenFile, "OpenFile"},
+ {0x08030204, OpenFileDirectly, "OpenFileDirectly"},
+ {0x08040142, DeleteFile, "DeleteFile"},
+ {0x08050244, RenameFile, "RenameFile"},
+ {0x08060142, DeleteDirectory, "DeleteDirectory"},
+ {0x08070142, nullptr, "DeleteDirectoryRecursively"},
+ {0x08080202, CreateFile, "CreateFile"},
+ {0x08090182, CreateDirectory, "CreateDirectory"},
+ {0x080A0244, RenameDirectory, "RenameDirectory"},
+ {0x080B0102, OpenDirectory, "OpenDirectory"},
+ {0x080C00C2, OpenArchive, "OpenArchive"},
+ {0x080D0144, nullptr, "ControlArchive"},
+ {0x080E0080, CloseArchive, "CloseArchive"},
+ {0x080F0180, FormatThisUserSaveData, "FormatThisUserSaveData"},
+ {0x08100200, nullptr, "CreateSystemSaveData"},
+ {0x08110040, nullptr, "DeleteSystemSaveData"},
+ {0x08120080, GetFreeBytes, "GetFreeBytes"},
+ {0x08130000, nullptr, "GetCardType"},
+ {0x08140000, nullptr, "GetSdmcArchiveResource"},
+ {0x08150000, nullptr, "GetNandArchiveResource"},
+ {0x08160000, nullptr, "GetSdmcFatfsError"},
+ {0x08170000, IsSdmcDetected, "IsSdmcDetected"},
+ {0x08180000, IsSdmcWriteable, "IsSdmcWritable"},
+ {0x08190042, nullptr, "GetSdmcCid"},
+ {0x081A0042, nullptr, "GetNandCid"},
+ {0x081B0000, nullptr, "GetSdmcSpeedInfo"},
+ {0x081C0000, nullptr, "GetNandSpeedInfo"},
+ {0x081D0042, nullptr, "GetSdmcLog"},
+ {0x081E0042, nullptr, "GetNandLog"},
+ {0x081F0000, nullptr, "ClearSdmcLog"},
+ {0x08200000, nullptr, "ClearNandLog"},
+ {0x08210000, CardSlotIsInserted, "CardSlotIsInserted"},
+ {0x08220000, nullptr, "CardSlotPowerOn"},
+ {0x08230000, nullptr, "CardSlotPowerOff"},
+ {0x08240000, nullptr, "CardSlotGetCardIFPowerStatus"},
+ {0x08250040, nullptr, "CardNorDirectCommand"},
+ {0x08260080, nullptr, "CardNorDirectCommandWithAddress"},
+ {0x08270082, nullptr, "CardNorDirectRead"},
+ {0x082800C2, nullptr, "CardNorDirectReadWithAddress"},
+ {0x08290082, nullptr, "CardNorDirectWrite"},
+ {0x082A00C2, nullptr, "CardNorDirectWriteWithAddress"},
+ {0x082B00C2, nullptr, "CardNorDirectRead_4xIO"},
+ {0x082C0082, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
+ {0x082D0040, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
+ {0x082E0040, nullptr, "GetProductInfo"},
+ {0x082F0040, nullptr, "GetProgramLaunchInfo"},
+ {0x08300182, nullptr, "CreateExtSaveData"},
+ {0x08310180, nullptr, "CreateSharedExtSaveData"},
+ {0x08320102, nullptr, "ReadExtSaveDataIcon"},
+ {0x08330082, nullptr, "EnumerateExtSaveData"},
+ {0x08340082, nullptr, "EnumerateSharedExtSaveData"},
+ {0x08350080, nullptr, "DeleteExtSaveData"},
+ {0x08360080, nullptr, "DeleteSharedExtSaveData"},
+ {0x08370040, nullptr, "SetCardSpiBaudRate"},
+ {0x08380040, nullptr, "SetCardSpiBusMode"},
+ {0x08390000, nullptr, "SendInitializeInfoTo9"},
+ {0x083A0100, nullptr, "GetSpecialContentIndex"},
+ {0x083B00C2, nullptr, "GetLegacyRomHeader"},
+ {0x083C00C2, nullptr, "GetLegacyBannerData"},
+ {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
+ {0x083E00C2, nullptr, "QueryTotalQuotaSize"},
+ {0x083F00C0, nullptr, "GetExtDataBlockSize"},
+ {0x08400040, nullptr, "AbnegateAccessRight"},
+ {0x08410000, nullptr, "DeleteSdmcRoot"},
+ {0x08420040, nullptr, "DeleteAllExtSaveDataOnNand"},
+ {0x08430000, nullptr, "InitializeCtrFileSystem"},
+ {0x08440000, nullptr, "CreateSeed"},
+ {0x084500C2, nullptr, "GetFormatInfo"},
+ {0x08460102, nullptr, "GetLegacyRomHeader2"},
+ {0x08470180, nullptr, "FormatCtrCardUserSaveData"},
+ {0x08480042, nullptr, "GetSdmcCtrRootPath"},
+ {0x08490040, nullptr, "GetArchiveResource"},
+ {0x084A0002, nullptr, "ExportIntegrityVerificationSeed"},
+ {0x084B0002, nullptr, "ImportIntegrityVerificationSeed"},
+ {0x084C0242, FormatSaveData, "FormatSaveData"},
+ {0x084D0102, nullptr, "GetLegacySubBannerData"},
+ {0x084E0342, nullptr, "UpdateSha256Context"},
+ {0x084F0102, nullptr, "ReadSpecialFile"},
+ {0x08500040, nullptr, "GetSpecialFileSize"},
+ {0x08510242, CreateExtSaveData, "CreateExtSaveData"},
+ {0x08520100, DeleteExtSaveData, "DeleteExtSaveData"},
+ {0x08530142, nullptr, "ReadExtSaveDataIcon"},
+ {0x085400C0, nullptr, "GetExtDataBlockSize"},
+ {0x08550102, nullptr, "EnumerateExtSaveData"},
+ {0x08560240, CreateSystemSaveData, "CreateSystemSaveData"},
+ {0x08570080, DeleteSystemSaveData, "DeleteSystemSaveData"},
+ {0x08580000, nullptr, "StartDeviceMoveAsSource"},
+ {0x08590200, nullptr, "StartDeviceMoveAsDestination"},
+ {0x085A00C0, nullptr, "SetArchivePriority"},
+ {0x085B0080, nullptr, "GetArchivePriority"},
+ {0x085C00C0, nullptr, "SetCtrCardLatencyParameter"},
+ {0x085D01C0, nullptr, "SetFsCompatibilityInfo"},
+ {0x085E0040, nullptr, "ResetCardCompatibilityParameter"},
+ {0x085F0040, nullptr, "SwitchCleanupInvalidSaveData"},
+ {0x08600042, nullptr, "EnumerateSystemSaveData"},
{0x08610042, InitializeWithSdkVersion, "InitializeWithSdkVersion"},
- {0x08620040, SetPriority, "SetPriority"},
- {0x08630000, GetPriority, "GetPriority"},
+ {0x08620040, SetPriority, "SetPriority"},
+ {0x08630000, GetPriority, "GetPriority"},
+ {0x08640000, nullptr, "GetNandInfo"},
+ {0x08650140, nullptr, "SetSaveDataSecureValue"},
+ {0x086600C0, nullptr, "GetSaveDataSecureValue"},
+ {0x086700C4, nullptr, "ControlSecureSave"},
+ {0x08680000, nullptr, "GetMediaType"},
+ {0x08690000, nullptr, "GetNandEraseCount"},
+ {0x086A0082, nullptr, "ReadNandReport"}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp
index 481da0c9f..2ace2cade 100644
--- a/src/core/hle/service/gsp_gpu.cpp
+++ b/src/core/hle/service/gsp_gpu.cpp
@@ -275,7 +275,7 @@ static void FlushDataCache(Service::Interface* self) {
u32 size = cmd_buff[2];
u32 process = cmd_buff[4];
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(address), size);
+ VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size);
// TODO(purpasmart96): Verify return header on HW
@@ -320,7 +320,7 @@ static void RegisterInterruptRelayQueue(Service::Interface* self) {
* @todo This probably does not belong in the GSP module, instead move to video_core
*/
void SignalInterrupt(InterruptId interrupt_id) {
- if (0 == g_interrupt_event) {
+ if (nullptr == g_interrupt_event) {
LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!");
return;
}
@@ -347,7 +347,7 @@ void SignalInterrupt(InterruptId interrupt_id) {
FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id);
if (info->is_dirty) {
SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
- info->is_dirty = false;
+ info->is_dirty.Assign(false);
}
}
}
@@ -365,7 +365,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
// GX request DMA - typically used for copying memory from GSP heap to VRAM
case CommandId::REQUEST_DMA:
- VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
+ VideoCore::g_renderer->Rasterizer()->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address),
command.dma_request.size);
memcpy(Memory::GetPointer(command.dma_request.dest_address),
@@ -373,7 +373,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
command.dma_request.size);
SignalInterrupt(InterruptId::DMA);
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
+ VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address),
command.dma_request.size);
break;
@@ -467,7 +467,7 @@ static void ExecuteCommand(const Command& command, u32 thread_id) {
if (region.size == 0)
break;
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(
+ VideoCore::g_renderer->Rasterizer()->InvalidateRegion(
Memory::VirtualToPhysicalAddress(region.address), region.size);
}
break;
@@ -499,7 +499,7 @@ static void SetLcdForceBlack(Service::Interface* self) {
// Since data is already zeroed, there is no need to explicitly set
// the color to black (all zero).
- data.is_enabled = enable_black;
+ data.is_enabled.Assign(enable_black);
LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_top), data.raw); // Top LCD
LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_bottom), data.raw); // Bottom LCD
@@ -521,7 +521,7 @@ static void TriggerCmdReqQueue(Service::Interface* self) {
ExecuteCommand(command_buffer->commands[i], thread_id);
// Indicates that command has completed
- command_buffer->number_commands = command_buffer->number_commands - 1;
+ command_buffer->number_commands.Assign(command_buffer->number_commands - 1);
}
}
diff --git a/src/core/hle/service/gsp_lcd.cpp b/src/core/hle/service/gsp_lcd.cpp
index 9e36732b4..c700c21c5 100644
--- a/src/core/hle/service/gsp_lcd.cpp
+++ b/src/core/hle/service/gsp_lcd.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/bit_field.h"
-
#include "core/hle/service/gsp_lcd.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,14 +9,19 @@
namespace GSP_LCD {
-/*const Interface::FunctionInfo FunctionTable[] = {
-};*/
+const Interface::FunctionInfo FunctionTable[] = {
+ {0x000F0000, nullptr, "PowerOnAllBacklights"},
+ {0x00100000, nullptr, "PowerOffAllBacklights"},
+ {0x00110040, nullptr, "PowerOnBacklight"},
+ {0x00120040, nullptr, "PowerOffBacklight"},
+ {0x00130040, nullptr, "SetLedForceOff"}
+};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Interface class
Interface::Interface() {
- //Register(FunctionTable);
+ Register(FunctionTable);
}
} // namespace
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2e1e5c3e9..11d7e69a1 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -13,7 +13,6 @@
#include "core/core_timing.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/hle.h"
#include "video_core/video_core.h"
@@ -106,7 +105,7 @@ void Update() {
bool pressed = false;
std::tie(touch_entry->x, touch_entry->y, pressed) = VideoCore::g_emu_window->GetTouchState();
- touch_entry->valid = pressed ? 1 : 0;
+ touch_entry->valid.Assign(pressed ? 1 : 0);
// TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which
// supposedly is "Touch-screen entry, which contains the raw coordinate data prior to being
diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp
index 532931ae0..c50f597eb 100644
--- a/src/core/hle/service/hid/hid_spvr.cpp
+++ b/src/core/hle/service/hid/hid_spvr.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_spvr.h"
diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp
index fbfb9e885..bbdde2abb 100644
--- a/src/core/hle/service/hid/hid_user.cpp
+++ b/src/core/hle/service/hid/hid_user.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/hid_user.h"
@@ -11,6 +10,8 @@ namespace HID {
const Interface::FunctionInfo FunctionTable[] = {
{0x000A0000, GetIPCHandles, "GetIPCHandles"},
+ {0x000B0000, nullptr, "StartAnalogStickCalibration"},
+ {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"},
{0x00110000, EnableAccelerometer, "EnableAccelerometer"},
{0x00120000, DisableAccelerometer, "DisableAccelerometer"},
{0x00130000, EnableGyroscopeLow, "EnableGyroscopeLow"},
diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp
index 0a3aba0a0..0855ab227 100644
--- a/src/core/hle/service/http_c.cpp
+++ b/src/core/hle/service/http_c.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/http_c.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -47,10 +46,20 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00220040, nullptr, "GetResponseStatusCode"},
{0x002300C0, nullptr, "GetResponseStatusCodeTimeout"},
{0x00240082, nullptr, "AddTrustedRootCA"},
+ {0x00250080, nullptr, "AddDefaultCert"},
+ {0x00260080, nullptr, "SelectRootCertChain"},
+ {0x002700C4, nullptr, "SetClientCert"},
+ {0x002B0080, nullptr, "SetSSLOpt"},
+ {0x002C0080, nullptr, "SetSSLClearOpt"},
+ {0x002D0000, nullptr, "CreateRootCertChain"},
+ {0x002E0040, nullptr, "DestroyRootCertChain"},
+ {0x002F0082, nullptr, "RootCertChainAddCert"},
+ {0x00300080, nullptr, "RootCertChainAddDefaultCert"},
{0x00350186, nullptr, "SetDefaultProxy"},
{0x00360000, nullptr, "ClearDNSCache"},
{0x00370080, nullptr, "SetKeepAlive"},
- {0x003800C0, nullptr, "Finalize"},
+ {0x003800C0, nullptr, "SetPostDataTypeSize"},
+ {0x00390000, nullptr, "Finalize"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp
index adfbb258d..c2121cb2e 100644
--- a/src/core/hle/service/ir/ir.cpp
+++ b/src/core/hle/service/ir/ir.cpp
@@ -2,21 +2,22 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/shared_memory.h"
+
#include "core/hle/service/service.h"
#include "core/hle/service/ir/ir.h"
#include "core/hle/service/ir/ir_rst.h"
#include "core/hle/service/ir/ir_u.h"
#include "core/hle/service/ir/ir_user.h"
-#include "core/hle/hle.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-
namespace Service {
namespace IR {
static Kernel::SharedPtr<Kernel::Event> handle_event;
+static Kernel::SharedPtr<Kernel::Event> conn_status_event;
static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory;
+static Kernel::SharedPtr<Kernel::SharedMemory> transfer_shared_memory;
void GetHandles(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
@@ -27,6 +28,64 @@ void GetHandles(Service::Interface* self) {
cmd_buff[4] = Kernel::g_handle_table.Create(Service::IR::handle_event).MoveFrom();
}
+void InitializeIrNopShared(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ u32 transfer_buff_size = cmd_buff[1];
+ u32 recv_buff_size = cmd_buff[2];
+ u32 unk1 = cmd_buff[3];
+ u32 send_buff_size = cmd_buff[4];
+ u32 unk2 = cmd_buff[5];
+ u8 baud_rate = cmd_buff[6] & 0xFF;
+ Handle handle = cmd_buff[8];
+
+ if(Kernel::g_handle_table.IsValid(handle)) {
+ transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle);
+ transfer_shared_memory->name = "IR:TransferSharedMemory";
+ }
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_IR, "(STUBBED) called, transfer_buff_size=%d, recv_buff_size=%d, "
+ "unk1=%d, send_buff_size=%d, unk2=%d, baud_rate=%u, handle=0x%08X",
+ transfer_buff_size, recv_buff_size, unk1, send_buff_size, unk2, baud_rate, handle);
+}
+
+void RequireConnection(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ conn_status_event->Signal();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_IR, "(STUBBED) called");
+}
+
+void Disconnect(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_IR, "(STUBBED) called");
+}
+
+void GetConnectionStatusEvent(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom();
+
+ LOG_WARNING(Service_IR, "(STUBBED) called");
+}
+
+void FinalizeIrNop(Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+
+ LOG_WARNING(Service_IR, "(STUBBED) called");
+}
+
void Init() {
using namespace Kernel;
@@ -36,15 +95,19 @@ void Init() {
using Kernel::MemoryPermission;
shared_memory = SharedMemory::Create(0x1000, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory");
+ Kernel::MemoryPermission::ReadWrite, "IR:SharedMemory");
+ transfer_shared_memory = nullptr;
// Create event handle(s)
handle_event = Event::Create(RESETTYPE_ONESHOT, "IR:HandleEvent");
+ conn_status_event = Event::Create(RESETTYPE_ONESHOT, "IR:ConnectionStatusEvent");
}
void Shutdown() {
+ transfer_shared_memory = nullptr;
shared_memory = nullptr;
handle_event = nullptr;
+ conn_status_event = nullptr;
}
} // namespace IR
diff --git a/src/core/hle/service/ir/ir.h b/src/core/hle/service/ir/ir.h
index c16d963e7..72d44ce60 100644
--- a/src/core/hle/service/ir/ir.h
+++ b/src/core/hle/service/ir/ir.h
@@ -4,10 +4,10 @@
#pragma once
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/service.h"
-
namespace Service {
+
+class Interface;
+
namespace IR {
/**
@@ -20,6 +20,53 @@ namespace IR {
*/
void GetHandles(Interface* self);
+/**
+ * IR::InitializeIrNopShared service function
+ * Inputs:
+ * 1 : Size of transfer buffer
+ * 2 : Recv buffer size
+ * 3 : unknown
+ * 4 : Send buffer size
+ * 5 : unknown
+ * 6 : BaudRate (u8)
+ * 7 : 0
+ * 8 : Handle of transfer shared memory
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void InitializeIrNopShared(Interface* self);
+
+/**
+ * IR::FinalizeIrNop service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void FinalizeIrNop(Interface* self);
+
+/**
+ * IR::GetConnectionStatusEvent service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Connection Status Event handle
+ */
+void GetConnectionStatusEvent(Interface* self);
+
+/**
+ * IR::Disconnect service function
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void Disconnect(Interface* self);
+
+/**
+ * IR::RequireConnection service function
+ * Inputs:
+ * 1 : unknown (u8), looks like always 1
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void RequireConnection(Interface* self);
+
/// Initialize IR service
void Init();
diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp
index 96ae63420..c0300f109 100644
--- a/src/core/hle/service/ir/ir_rst.cpp
+++ b/src/core/hle/service/ir/ir_rst.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/ir/ir.h"
#include "core/hle/service/ir/ir_rst.h"
diff --git a/src/core/hle/service/ir/ir_u.cpp b/src/core/hle/service/ir/ir_u.cpp
index 1b1e3078b..96f76cb83 100644
--- a/src/core/hle/service/ir/ir_u.cpp
+++ b/src/core/hle/service/ir/ir_u.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/ir/ir.h"
#include "core/hle/service/ir/ir_u.h"
namespace Service {
diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp
index 8e3ff140f..06a601029 100644
--- a/src/core/hle/service/ir/ir_user.cpp
+++ b/src/core/hle/service/ir/ir_user.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/ir/ir.h"
#include "core/hle/service/ir/ir_user.h"
@@ -10,20 +9,32 @@ namespace Service {
namespace IR {
const Interface::FunctionInfo FunctionTable[] = {
- {0x00010182, nullptr, "InitializeIrNop"},
- {0x00020000, nullptr, "FinalizeIrNop"},
- {0x00030000, nullptr, "ClearReceiveBuffer"},
- {0x00040000, nullptr, "ClearSendBuffer"},
- {0x00060040, nullptr, "RequireConnection"},
- {0x00090000, nullptr, "Disconnect"},
- {0x000A0000, nullptr, "GetReceiveEvent"},
- {0x000B0000, nullptr, "GetSendEvent"},
- {0x000C0000, nullptr, "GetConnectionStatusEvent"},
- {0x000D0042, nullptr, "SendIrNop"},
- {0x000E0042, nullptr, "SendIrNopLarge"},
- {0x00180182, nullptr, "InitializeIrNopShared"},
- {0x00190040, nullptr, "ReleaseReceivedData"},
- {0x001A0040, nullptr, "SetOwnMachineId"},
+ {0x00010182, nullptr, "InitializeIrNop"},
+ {0x00020000, FinalizeIrNop, "FinalizeIrNop"},
+ {0x00030000, nullptr, "ClearReceiveBuffer"},
+ {0x00040000, nullptr, "ClearSendBuffer"},
+ {0x000500C0, nullptr, "WaitConnection"},
+ {0x00060040, RequireConnection, "RequireConnection"},
+ {0x000702C0, nullptr, "AutoConnection"},
+ {0x00080000, nullptr, "AnyConnection"},
+ {0x00090000, Disconnect, "Disconnect"},
+ {0x000A0000, nullptr, "GetReceiveEvent"},
+ {0x000B0000, nullptr, "GetSendEvent"},
+ {0x000C0000, GetConnectionStatusEvent, "GetConnectionStatusEvent"},
+ {0x000D0042, nullptr, "SendIrNop"},
+ {0x000E0042, nullptr, "SendIrNopLarge"},
+ {0x000F0040, nullptr, "ReceiveIrnop"},
+ {0x00100042, nullptr, "ReceiveIrnopLarge"},
+ {0x00110040, nullptr, "GetLatestReceiveErrorResult"},
+ {0x00120040, nullptr, "GetLatestSendErrorResult"},
+ {0x00130000, nullptr, "GetConnectionStatus"},
+ {0x00140000, nullptr, "GetTryingToConnectStatus"},
+ {0x00150000, nullptr, "GetReceiveSizeFreeAndUsed"},
+ {0x00160000, nullptr, "GetSendSizeFreeAndUsed"},
+ {0x00170000, nullptr, "GetConnectionRole"},
+ {0x00180182, InitializeIrNopShared, "InitializeIrNopShared"},
+ {0x00190040, nullptr, "ReleaseReceivedData"},
+ {0x001A0040, nullptr, "SetOwnMachineId"},
};
IR_User_Interface::IR_User_Interface() {
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp
index f84ce4d72..ecec2ce32 100644
--- a/src/core/hle/service/ldr_ro.cpp
+++ b/src/core/hle/service/ldr_ro.cpp
@@ -2,9 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
#include "core/hle/service/ldr_ro.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index 25e70d321..f792bc9cd 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/mic_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -18,14 +17,14 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00050000, nullptr, "StopSampling"},
{0x00060000, nullptr, "IsSampling"},
{0x00070000, nullptr, "GetEventHandle"},
- {0x00080040, nullptr, "SetControl"},
- {0x00090000, nullptr, "GetControl"},
- {0x000A0040, nullptr, "SetBias"},
- {0x000B0000, nullptr, "GetBias"},
+ {0x00080040, nullptr, "SetGain"},
+ {0x00090000, nullptr, "GetGain"},
+ {0x000A0040, nullptr, "SetPower"},
+ {0x000B0000, nullptr, "GetPower"},
{0x000C0042, nullptr, "size"},
{0x000D0040, nullptr, "SetClamp"},
{0x000E0000, nullptr, "GetClamp"},
- {0x000F0040, nullptr, "unknown_input1"},
+ {0x000F0040, nullptr, "SetAllowShellClosed"},
{0x00100040, nullptr, "unknown_input2"},
};
diff --git a/src/core/hle/service/ndm_u.cpp b/src/core/hle/service/ndm_u.cpp
index df3c97193..8fdf1ef90 100644
--- a/src/core/hle/service/ndm_u.cpp
+++ b/src/core/hle/service/ndm_u.cpp
@@ -2,8 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "ndm_u.h"
+#include "core/hle/service/ndm_u.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace NDM_U
@@ -14,10 +13,26 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00010042, nullptr, "EnterExclusiveState"},
{0x00020002, nullptr, "LeaveExclusiveState"},
{0x00030000, nullptr, "QueryExclusiveMode"},
+ {0x00040002, nullptr, "LockState"},
+ {0x00050002, nullptr, "UnlockState"},
{0x00060040, nullptr, "SuspendDaemons"},
+ {0x00070040, nullptr, "ResumeDaemons"},
{0x00080040, nullptr, "DisableWifiUsage"},
{0x00090000, nullptr, "EnableWifiUsage"},
+ {0x000A0000, nullptr, "GetCurrentState"},
+ {0x000B0000, nullptr, "GetTargetState"},
+ {0x000C0000, nullptr, "<Stubbed>"},
+ {0x000D0040, nullptr, "QueryStatus"},
+ {0x000E0040, nullptr, "GetDaemonDisableCount"},
+ {0x000F0000, nullptr, "GetSchedulerDisableCount"},
+ {0x00100040, nullptr, "SetScanInterval"},
+ {0x00110000, nullptr, "GetScanInterval"},
+ {0x00120040, nullptr, "SetRetryInterval"},
+ {0x00130000, nullptr, "GetRetryInterval"},
{0x00140040, nullptr, "OverrideDefaultDaemons"},
+ {0x00150000, nullptr, "ResetDefaultDaemons"},
+ {0x00160000, nullptr, "GetDefaultDaemons"},
+ {0x00170000, nullptr, "ClearHalfAwakeMacFilter"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/news/news.cpp b/src/core/hle/service/news/news.cpp
index 63cbd3850..b3f500694 100644
--- a/src/core/hle/service/news/news.cpp
+++ b/src/core/hle/service/news/news.cpp
@@ -9,10 +9,6 @@
#include "core/hle/service/news/news_s.h"
#include "core/hle/service/news/news_u.h"
-#include "core/hle/hle.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-
namespace Service {
namespace NEWS {
diff --git a/src/core/hle/service/news/news.h b/src/core/hle/service/news/news.h
index b31ade255..46a3ffcb5 100644
--- a/src/core/hle/service/news/news.h
+++ b/src/core/hle/service/news/news.h
@@ -4,9 +4,6 @@
#pragma once
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/service.h"
-
namespace Service {
namespace NEWS {
diff --git a/src/core/hle/service/news/news_s.cpp b/src/core/hle/service/news/news_s.cpp
index 2f8c37d9e..39b5a50f8 100644
--- a/src/core/hle/service/news/news_s.cpp
+++ b/src/core/hle/service/news/news_s.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/news/news.h"
#include "core/hle/service/news/news_s.h"
@@ -11,6 +10,18 @@ namespace NEWS {
const Interface::FunctionInfo FunctionTable[] = {
{0x000100C6, nullptr, "AddNotification"},
+ {0x00050000, nullptr, "GetTotalNotifications"},
+ {0x00060042, nullptr, "SetNewsDBHeader"},
+ {0x00070082, nullptr, "SetNotificationHeader"},
+ {0x00080082, nullptr, "SetNotificationMessage"},
+ {0x00090082, nullptr, "SetNotificationImage"},
+ {0x000A0042, nullptr, "GetNewsDBHeader"},
+ {0x000B0082, nullptr, "GetNotificationHeader"},
+ {0x000C0082, nullptr, "GetNotificationMessage"},
+ {0x000D0082, nullptr, "GetNotificationImage"},
+ {0x000E0040, nullptr, "SetInfoLEDPattern"},
+ {0x00120082, nullptr, "GetNotificationHeaderOther"},
+ {0x00130000, nullptr, "WriteNewsDBSavedata"},
};
NEWS_S_Interface::NEWS_S_Interface() {
diff --git a/src/core/hle/service/news/news_u.cpp b/src/core/hle/service/news/news_u.cpp
index 81f45a244..6b75cc24e 100644
--- a/src/core/hle/service/news/news_u.cpp
+++ b/src/core/hle/service/news/news_u.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/news/news.h"
#include "core/hle/service/news/news_u.h"
namespace Service {
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 73b0ee52a..ed42464ce 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/service/service.h"
@@ -10,10 +11,6 @@
#include "core/hle/service/nim/nim_s.h"
#include "core/hle/service/nim/nim_u.h"
-#include "core/hle/kernel/event.h"
-#include "core/hle/kernel/shared_memory.h"
-#include "core/hle/hle.h"
-
namespace Service {
namespace NIM {
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h
index f7635c747..c3106f18b 100644
--- a/src/core/hle/service/nim/nim.h
+++ b/src/core/hle/service/nim/nim.h
@@ -4,10 +4,10 @@
#pragma once
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/service.h"
-
namespace Service {
+
+class Interface;
+
namespace NIM {
/**
diff --git a/src/core/hle/service/nim/nim_aoc.cpp b/src/core/hle/service/nim/nim_aoc.cpp
index e6b1b6145..4a4818d57 100644
--- a/src/core/hle/service/nim/nim_aoc.cpp
+++ b/src/core/hle/service/nim/nim_aoc.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/nim/nim.h"
#include "core/hle/service/nim/nim_aoc.h"
namespace Service {
diff --git a/src/core/hle/service/nim/nim_s.cpp b/src/core/hle/service/nim/nim_s.cpp
index 5d8bc059f..dcaa0255a 100644
--- a/src/core/hle/service/nim/nim_s.cpp
+++ b/src/core/hle/service/nim/nim_s.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
-#include "core/hle/service/nim/nim.h"
#include "core/hle/service/nim/nim_s.h"
namespace Service {
@@ -11,6 +9,9 @@ namespace NIM {
const Interface::FunctionInfo FunctionTable[] = {
{0x000A0000, nullptr, "CheckSysupdateAvailableSOAP"},
+ {0x0016020A, nullptr, "ListTitles"},
+ {0x002D0042, nullptr, "DownloadTickets"},
+ {0x00420240, nullptr, "StartDownload"},
};
NIM_S_Interface::NIM_S_Interface() {
diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp
index 066570a85..eae45ebc0 100644
--- a/src/core/hle/service/nim/nim_u.cpp
+++ b/src/core/hle/service/nim/nim_u.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/nim/nim.h"
#include "core/hle/service/nim/nim_u.h"
diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp
index 6b3ef6ece..072918d62 100644
--- a/src/core/hle/service/ns_s.cpp
+++ b/src/core/hle/service/ns_s.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-
-#include "core/hle/hle.h"
#include "core/hle/service/ns_s.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -12,7 +10,21 @@
namespace NS_S {
const Interface::FunctionInfo FunctionTable[] = {
+ {0x000100C0, nullptr, "LaunchFIRM"},
{0x000200C0, nullptr, "LaunchTitle"},
+ {0x00030000, nullptr, "TerminateApplication"},
+ {0x00040040, nullptr, "TerminateProcess"},
+ {0x000500C0, nullptr, "LaunchApplicationFIRM"},
+ {0x00060042, nullptr, "SetFIRMParams4A0"},
+ {0x00070042, nullptr, "CardUpdateInitialize"},
+ {0x00080000, nullptr, "CardUpdateShutdown"},
+ {0x000D0140, nullptr, "SetTWLBannerHMAC"},
+ {0x000E0000, nullptr, "ShutdownAsync"},
+ {0x00100180, nullptr, "RebootSystem"},
+ {0x00110100, nullptr, "TerminateTitle"},
+ {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"},
+ {0x00150140, nullptr, "LaunchApplication"},
+ {0x00160000, nullptr, "RebootSystemClean"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp
index 18b22956f..dc80984b2 100644
--- a/src/core/hle/service/nwm_uds.cpp
+++ b/src/core/hle/service/nwm_uds.cpp
@@ -2,9 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nwm_uds.h"
@@ -106,14 +106,32 @@ static void Initialize(Service::Interface* self) {
}
const Interface::FunctionInfo FunctionTable[] = {
+ {0x00020000, nullptr, "Scrap"},
{0x00030000, Shutdown, "Shutdown"},
+ {0x00040402, nullptr, "CreateNetwork"},
+ {0x00050040, nullptr, "EjectClient"},
+ {0x00060000, nullptr, "EjectSpectator"},
+ {0x00070080, nullptr, "UpdateNetworkAttribute"},
+ {0x00080000, nullptr, "DestroyNetwork"},
+ {0x000A0000, nullptr, "DisconnectNetwork"},
+ {0x000B0000, nullptr, "GetConnectionStatus"},
+ {0x000D0040, nullptr, "GetNodeInformation"},
{0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"},
{0x00100042, nullptr, "SetBeaconAdditionalData"},
+ {0x00110040, nullptr, "GetApplicationData"},
+ {0x00120100, nullptr, "Bind"},
+ {0x00130040, nullptr, "Unbind"},
{0x001400C0, nullptr, "RecvBroadcastDataFrame"},
+ {0x00150080, nullptr, "SetMaxSendDelay"},
+ {0x00170182, nullptr, "SendTo"},
+ {0x001A0000, nullptr, "GetChannel"},
{0x001B0302, Initialize, "Initialize"},
{0x001D0044, nullptr, "BeginHostingNetwork"},
{0x001E0084, nullptr, "ConnectToNetwork"},
{0x001F0006, nullptr, "DecryptBeaconData"},
+ {0x00200040, nullptr, "Flush"},
+ {0x00210080, nullptr, "SetProbeResponseParam"},
+ {0x00220402, nullptr, "ScanOnConnection"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp
index 7420a62f4..05d01bc48 100644
--- a/src/core/hle/service/pm_app.cpp
+++ b/src/core/hle/service/pm_app.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/pm_app.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -19,6 +18,10 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00070042, nullptr, "GetFIRMLaunchParams"},
{0x00080100, nullptr, "GetTitleExheaderFlags"},
{0x00090042, nullptr, "SetFIRMLaunchParams"},
+ {0x000A0140, nullptr, "SetResourceLimit"},
+ {0x000B0140, nullptr, "GetResourceLimitMax"},
+ {0x000C0080, nullptr, "UnregisterProcess"},
+ {0x000D0240, nullptr, "LaunchTitleUpdate"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
index 22c1093ff..6bdee4d9e 100644
--- a/src/core/hle/service/ptm/ptm.cpp
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -110,8 +110,8 @@ void Init() {
FileSys::Path gamecoin_path("gamecoin.dat");
FileSys::Mode open_mode = {};
- open_mode.write_flag = 1;
- open_mode.create_flag = 1;
+ open_mode.write_flag.Assign(1);
+ open_mode.create_flag.Assign(1);
// Open the file and write the default gamecoin information
auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode);
if (gamecoin_result.Succeeded()) {
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
index f2e76441f..4cf7383d1 100644
--- a/src/core/hle/service/ptm/ptm.h
+++ b/src/core/hle/service/ptm/ptm.h
@@ -4,11 +4,12 @@
#pragma once
-#include <array>
-#include "core/hle/service/service.h"
-#include "core/hle/result.h"
+#include "common/common_types.h"
namespace Service {
+
+class Interface;
+
namespace PTM {
/// Charge levels used by PTM functions
diff --git a/src/core/hle/service/ptm/ptm_play.cpp b/src/core/hle/service/ptm/ptm_play.cpp
index 7bb990193..ca5dd0403 100644
--- a/src/core/hle/service/ptm/ptm_play.cpp
+++ b/src/core/hle/service/ptm/ptm_play.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/ptm/ptm_play.h"
namespace Service {
diff --git a/src/core/hle/service/ptm/ptm_sysm.cpp b/src/core/hle/service/ptm/ptm_sysm.cpp
index 655658f3b..fe76dd108 100644
--- a/src/core/hle/service/ptm/ptm_sysm.cpp
+++ b/src/core/hle/service/ptm/ptm_sysm.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "core/hle/hle.h"
#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/ptm/ptm_sysm.h"
@@ -39,7 +38,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x08110000, nullptr, "GetShellStatus"},
{0x08120000, nullptr, "IsShutdownByBatteryEmpty"},
{0x08130000, nullptr, "FormatSavedata"},
- {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"}
+ {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"},
+ {0x08180040, nullptr, "ConfigureNew3DSCPU"},
};
PTM_Sysm_Interface::PTM_Sysm_Interface() {
diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp
index 09dc38c3e..17e764866 100644
--- a/src/core/hle/service/ptm/ptm_u.cpp
+++ b/src/core/hle/service/ptm/ptm_u.cpp
@@ -2,9 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/logging/log.h"
-
-#include "core/hle/hle.h"
#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/ptm/ptm_u.h"
diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 633b66fe2..b52e52d4a 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -178,17 +178,17 @@ struct CTRPollFD {
static Events TranslateTo3DS(u32 input_event) {
Events ev = {};
if (input_event & POLLIN)
- ev.pollin = 1;
+ ev.pollin.Assign(1);
if (input_event & POLLPRI)
- ev.pollpri = 1;
+ ev.pollpri.Assign(1);
if (input_event & POLLHUP)
- ev.pollhup = 1;
+ ev.pollhup.Assign(1);
if (input_event & POLLERR)
- ev.pollerr = 1;
+ ev.pollerr.Assign(1);
if (input_event & POLLOUT)
- ev.pollout = 1;
+ ev.pollout.Assign(1);
if (input_event & POLLNVAL)
- ev.pollnval = 1;
+ ev.pollnval.Assign(1);
return ev;
}
@@ -552,13 +552,23 @@ static void RecvFrom(Service::Interface* self) {
u32 flags = cmd_buffer[3];
socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]);
- u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
+ struct
+ {
+ u32 output_buffer_descriptor;
+ u32 output_buffer_addr;
+ u32 address_buffer_descriptor;
+ u32 output_src_address_buffer;
+ } buffer_parameters;
+
+ std::memcpy(&buffer_parameters, &cmd_buffer[64], sizeof(buffer_parameters));
+
+ u8* output_buff = Memory::GetPointer(buffer_parameters.output_buffer_addr);
sockaddr src_addr;
socklen_t src_addr_len = sizeof(src_addr);
int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
- if (cmd_buffer[0x1A0 >> 2] != 0) {
- CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2]));
+ if (buffer_parameters.output_src_address_buffer != 0) {
+ CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer));
*ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
}
@@ -732,7 +742,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000C0082, Shutdown, "Shutdown"},
{0x000D0082, nullptr, "GetHostByName"},
{0x000E00C2, nullptr, "GetHostByAddr"},
- {0x000F0106, nullptr, "unknown_resolve_ip"},
+ {0x000F0106, nullptr, "GetAddrInfo"},
+ {0x00100102, nullptr, "GetNameInfo"},
{0x00110102, nullptr, "GetSockOpt"},
{0x00120104, nullptr, "SetSockOpt"},
{0x001300C2, Fcntl, "Fcntl"},
@@ -749,6 +760,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x001E0040, nullptr, "ICMPClose"},
{0x001F0040, nullptr, "GetResolverInfo"},
{0x00210002, nullptr, "CloseSockets"},
+ {0x00230040, nullptr, "AddGlobalSocket"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp
index 3b8c7c0e4..41fc3437b 100644
--- a/src/core/hle/service/srv.cpp
+++ b/src/core/hle/service/srv.cpp
@@ -2,9 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
#include "core/hle/service/srv.h"
#include "core/hle/kernel/event.h"
diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp
index cabd18c80..14a4e98ec 100644
--- a/src/core/hle/service/ssl_c.cpp
+++ b/src/core/hle/service/ssl_c.cpp
@@ -4,7 +4,7 @@
#include <random>
-#include "core/hle/hle.h"
+#include "common/common_types.h"
#include "core/hle/service/ssl_c.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -62,10 +62,24 @@ static void GenerateRandomData(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010002, Initialize, "Initialize"},
{0x000200C2, nullptr, "CreateContext"},
+ {0x00030000, nullptr, "CreateRootCertChain"},
+ {0x00040040, nullptr, "DestroyRootCertChain"},
{0x00050082, nullptr, "AddTrustedRootCA"},
+ {0x00060080, nullptr, "RootCertChainAddDefaultCert"},
+ {0x00070080, nullptr, "RootCertChainRemoveCert"},
+ {0x000E0040, nullptr, "OpenDefaultClientCertContext"},
+ {0x000F0040, nullptr, "CloseClientCertContext"},
{0x00110042, GenerateRandomData, "GenerateRandomData"},
+ {0x00120042, nullptr, "InitializeConnectionSession"},
+ {0x00130040, nullptr, "StartConnection"},
+ {0x00140040, nullptr, "StartConnectionGetOut"},
{0x00150082, nullptr, "Read"},
{0x00170082, nullptr, "Write"},
+ {0x00180080, nullptr, "ContextSetRootCertChain"},
+ {0x00190080, nullptr, "ContextSetClientCert"},
+ {0x001B0080, nullptr, "ContextClearOpt"},
+ {0x001E0040, nullptr, "DestroyContext"},
+ {0x001F0082, nullptr, "ContextInitSharedmem"}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp
index 6b1b71fe4..a495441a4 100644
--- a/src/core/hle/service/y2r_u.cpp
+++ b/src/core/hle/service/y2r_u.cpp
@@ -4,15 +4,15 @@
#include <cstring>
+#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/hle/hle.h"
#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/service/y2r_u.h"
#include "core/hw/y2r.h"
#include "video_core/renderer_base.h"
-#include "video_core/utils.h"
#include "video_core/video_core.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -267,7 +267,7 @@ static void StartConversion(Service::Interface* self) {
// dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
u32 total_output_size = conversion.input_lines *
(conversion.dst.transfer_unit + conversion.dst.gap);
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(
+ VideoCore::g_renderer->Rasterizer()->InvalidateRegion(
Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size);
LOG_DEBUG(Service_Y2R, "called");
@@ -375,21 +375,41 @@ static void DriverFinalize(Service::Interface* self) {
const Interface::FunctionInfo FunctionTable[] = {
{0x00010040, SetInputFormat, "SetInputFormat"},
+ {0x00020000, nullptr, "GetInputFormat"},
{0x00030040, SetOutputFormat, "SetOutputFormat"},
+ {0x00040000, nullptr, "GetOutputFormat"},
{0x00050040, SetRotation, "SetRotation"},
+ {0x00060000, nullptr, "GetRotation"},
{0x00070040, SetBlockAlignment, "SetBlockAlignment"},
+ {0x00080000, nullptr, "GetBlockAlignment"},
+ {0x00090040, nullptr, "SetSpacialDithering"},
+ {0x000A0000, nullptr, "GetSpacialDithering"},
+ {0x000B0040, nullptr, "SetTemporalDithering"},
+ {0x000C0000, nullptr, "GetTemporalDithering"},
{0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"},
{0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"},
{0x00100102, SetSendingY, "SetSendingY"},
{0x00110102, SetSendingU, "SetSendingU"},
{0x00120102, SetSendingV, "SetSendingV"},
{0x00130102, SetSendingYUYV, "SetSendingYUYV"},
+ {0x00140000, nullptr, "IsFinishedSendingYuv"},
+ {0x00150000, nullptr, "IsFinishedSendingY"},
+ {0x00160000, nullptr, "IsFinishedSendingU"},
+ {0x00170000, nullptr, "IsFinishedSendingV"},
{0x00180102, SetReceiving, "SetReceiving"},
+ {0x00190000, nullptr, "IsFinishedReceiving"},
{0x001A0040, SetInputLineWidth, "SetInputLineWidth"},
+ {0x001B0000, nullptr, "GetInputLineWidth"},
{0x001C0040, SetInputLines, "SetInputLines"},
+ {0x001D0000, nullptr, "GetInputLines"},
{0x001E0100, SetCoefficient, "SetCoefficient"},
+ {0x001F0000, nullptr, "GetCoefficient"},
{0x00200040, SetStandardCoefficient, "SetStandardCoefficient"},
+ {0x00210040, nullptr, "GetStandardCoefficientParams"},
{0x00220040, SetAlpha, "SetAlpha"},
+ {0x00230000, nullptr, "GetAlpha"},
+ {0x00240200, nullptr, "SetDitheringWeightParams"},
+ {0x00250000, nullptr, "GetDitheringWeightParams"},
{0x00260000, StartConversion, "StartConversion"},
{0x00270000, StopConversion, "StopConversion"},
{0x00280000, IsBusyConversion, "IsBusyConversion"},
@@ -397,6 +417,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x002A0000, PingProcess, "PingProcess"},
{0x002B0000, DriverInitialize, "DriverInitialize"},
{0x002C0000, DriverFinalize, "DriverFinalize"},
+ {0x002D0000, nullptr, "GetPackageParameter"},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 7f63ff505..7a39b101d 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -161,6 +161,8 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d",
handle, addr, permissions, other_permissions);
+ // TODO(Subv): The same process that created a SharedMemory object can not map it in its own address space
+
SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
if (shared_memory == nullptr)
return ERR_INVALID_HANDLE;
@@ -175,13 +177,27 @@ static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 o
case MemoryPermission::WriteExecute:
case MemoryPermission::ReadWriteExecute:
case MemoryPermission::DontCare:
- shared_memory->Map(addr, permissions_type,
+ return shared_memory->Map(addr, permissions_type,
static_cast<MemoryPermission>(other_permissions));
- break;
default:
LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions);
}
- return RESULT_SUCCESS;
+
+ return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+}
+
+static ResultCode UnmapMemoryBlock(Handle handle, u32 addr) {
+ using Kernel::SharedMemory;
+
+ LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X", handle, addr);
+
+ // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap
+
+ SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle);
+ if (shared_memory == nullptr)
+ return ERR_INVALID_HANDLE;
+
+ return shared_memory->Unmap(addr);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -470,6 +486,7 @@ static ResultCode CreateThread(Handle* out_handle, s32 priority, u32 entry_point
}
switch (processor_id) {
+ case THREADPROCESSORID_ALL:
case THREADPROCESSORID_DEFAULT:
case THREADPROCESSORID_0:
case THREADPROCESSORID_1:
@@ -765,7 +782,13 @@ static s64 GetSystemTick() {
static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission,
u32 other_permission) {
using Kernel::SharedMemory;
- // TODO(Subv): Implement this function
+
+ if (size % Memory::PAGE_SIZE != 0)
+ return ResultCode(ErrorDescription::MisalignedSize, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+
+ // TODO(Subv): Return E0A01BF5 if the address is not in the application's heap
+
+ // TODO(Subv): Implement this function properly
using Kernel::MemoryPermission;
SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(size,
@@ -781,7 +804,7 @@ static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32
static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {
using Kernel::MemoryRegion;
- LOG_TRACE(Kernel_SVC, "called process=0x%08X type=%u param=%d", process_handle, type, param);
+ LOG_TRACE(Kernel_SVC, "called type=%u param=%d", type, param);
switch ((SystemInfoType)type) {
case SystemInfoType::REGION_MEMORY_USAGE:
@@ -807,7 +830,7 @@ static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) {
}
break;
case SystemInfoType::KERNEL_ALLOCATED_PAGES:
- LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=2 param=%d", type, param);
+ LOG_ERROR(Kernel_SVC, "unimplemented GetSystemInfo type=2 param=%d", param);
*out = 0;
break;
case SystemInfoType::KERNEL_SPAWNED_PIDS:
@@ -912,7 +935,7 @@ static const FunctionDef SVC_Table[] = {
{0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"},
{0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"},
{0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
- {0x20, nullptr, "UnmapMemoryBlock"},
+ {0x20, HLE::Wrap<UnmapMemoryBlock>, "UnmapMemoryBlock"},
{0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"},
{0x22, HLE::Wrap<ArbitrateAddress>, "ArbitrateAddress"},
{0x23, HLE::Wrap<CloseHandle>, "CloseHandle"},
diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index bc7bde903..7e2f9cdfa 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -17,7 +17,6 @@
#include "core/core_timing.h"
#include "core/hle/service/gsp_gpu.h"
-#include "core/hle/service/dsp_dsp.h"
#include "core/hle/service/hid/hid.h"
#include "core/hw/hw.h"
@@ -26,7 +25,7 @@
#include "core/tracer/recorder.h"
#include "video_core/command_processor.h"
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
@@ -141,13 +140,13 @@ inline void Write(u32 addr, const T data) {
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1);
}
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress());
+ VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress());
}
// Reset "trigger" flag and set the "finish" flag
// NOTE: This was confirmed to happen on hardware even if "address_start" is zero.
- config.trigger = 0;
- config.finished = 1;
+ config.trigger.Assign(0);
+ config.finished.Assign(1);
}
break;
}
@@ -172,7 +171,7 @@ inline void Write(u32 addr, const T data) {
u32 output_gap = config.texture_copy.output_gap * 16;
size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap);
- VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), contiguous_input_size);
+ VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size);
u32 remaining_size = config.texture_copy.size;
u32 remaining_input = input_width;
@@ -205,7 +204,7 @@ inline void Write(u32 addr, const T data) {
config.flags);
size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap);
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), contiguous_output_size);
+ VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size);
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
break;
@@ -232,7 +231,7 @@ inline void Write(u32 addr, const T data) {
u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format);
u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format);
- VideoCore::g_renderer->hw_rasterizer->NotifyPreRead(config.GetPhysicalInputAddress(), input_size);
+ VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), input_size);
for (u32 y = 0; y < output_height; ++y) {
for (u32 x = 0; x < output_width; ++x) {
@@ -339,7 +338,7 @@ inline void Write(u32 addr, const T data) {
g_regs.display_transfer_config.trigger = 0;
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF);
- VideoCore::g_renderer->hw_rasterizer->NotifyFlush(config.GetPhysicalOutputAddress(), output_size);
+ VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), output_size);
}
break;
}
@@ -414,11 +413,6 @@ static void VBlankCallback(u64 userdata, int cycles_late) {
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();
-
// Check for user input updates
Service::HID::Update();
@@ -444,16 +438,16 @@ void Init() {
framebuffer_sub.address_left1 = 0x1848F000;
framebuffer_sub.address_left2 = 0x184C7800;
- framebuffer_top.width = 240;
- framebuffer_top.height = 400;
+ framebuffer_top.width.Assign(240);
+ framebuffer_top.height.Assign(400);
framebuffer_top.stride = 3 * 240;
- framebuffer_top.color_format = Regs::PixelFormat::RGB8;
+ framebuffer_top.color_format.Assign(Regs::PixelFormat::RGB8);
framebuffer_top.active_fb = 0;
- framebuffer_sub.width = 240;
- framebuffer_sub.height = 320;
+ framebuffer_sub.width.Assign(240);
+ framebuffer_sub.height.Assign(320);
framebuffer_sub.stride = 3 * 240;
- framebuffer_sub.color_format = Regs::PixelFormat::RGB8;
+ framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8);
framebuffer_sub.active_fb = 0;
last_skip_frame = false;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 5d7264f12..69df94324 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -250,7 +250,7 @@ const char *ElfReader::GetSectionName(int section) const {
return nullptr;
int name_offset = sections[section].sh_name;
- const char* ptr = (char*)GetSectionDataPtr(header->e_shstrndx);
+ const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx));
if (ptr)
return ptr + name_offset;
@@ -347,10 +347,10 @@ bool ElfReader::LoadSymbols() {
SectionID sec = GetSectionByName(".symtab");
if (sec != -1) {
int stringSection = sections[sec].sh_link;
- const char *stringBase = (const char *)GetSectionDataPtr(stringSection);
+ const char *stringBase = reinterpret_cast<const char*>(GetSectionDataPtr(stringSection));
//We have a symbol table!
- Elf32_Sym* symtab = (Elf32_Sym *)(GetSectionDataPtr(sec));
+ const Elf32_Sym* symtab = reinterpret_cast<const Elf32_Sym*>(GetSectionDataPtr(sec));
unsigned int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym);
for (unsigned sym = 0; sym < numSymbols; sym++) {
int size = symtab[sym].st_size;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 6b88169e1..b1907cd55 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -137,11 +137,11 @@ ResultStatus LoadFile(const std::string& filename) {
AppLoader_NCCH app_loader(std::move(file), filename);
// Load application and RomFS
- if (ResultStatus::Success == app_loader.Load()) {
+ ResultStatus result = app_loader.Load();
+ if (ResultStatus::Success == result) {
Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
- return ResultStatus::Success;
}
- break;
+ return result;
}
// CIA file format...
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 68b3f546e..93f21bec2 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -255,7 +255,8 @@ ResultStatus AppLoader_NCCH::Load() {
priority = exheader_header.arm11_system_local_caps.priority;
resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category;
- LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name);
+ LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name);
+ LOG_INFO(Loader, "Program ID: %016X" , ncch_header.program_id);
LOG_DEBUG(Loader, "Code compressed: %s" , is_compressed ? "yes" : "no");
LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
LOG_DEBUG(Loader, "Code size: 0x%08X", code_size);
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index fc79c3ee9..7de5bd15d 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -13,6 +13,7 @@
#include "core/hle/kernel/process.h"
#include "core/memory.h"
#include "core/memory_setup.h"
+#include "core/mmio.h"
namespace Memory {
@@ -25,6 +26,12 @@ enum class PageType {
Special,
};
+struct SpecialRegion {
+ VAddr base;
+ u32 size;
+ MMIORegionPointer handler;
+};
+
/**
* A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely
* mimics the way a real CPU page table works, but instead is optimized for minimal decoding and
@@ -41,8 +48,13 @@ struct PageTable {
std::array<u8*, NUM_ENTRIES> pointers;
/**
+ * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of type `Special`.
+ */
+ std::vector<SpecialRegion> special_regions;
+
+ /**
* Array of fine grained page attributes. If it is set to any value other than `Memory`, then
- * the corresponding entry in `pointer` MUST be set to null.
+ * the corresponding entry in `pointers` MUST be set to null.
*/
std::array<PageType, NUM_ENTRIES> attributes;
};
@@ -80,10 +92,12 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target) {
MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory);
}
-void MapIoRegion(VAddr base, u32 size) {
+void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) {
ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size);
ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base);
MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special);
+
+ current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
}
void UnmapRegion(VAddr base, u32 size) {
@@ -92,6 +106,22 @@ void UnmapRegion(VAddr base, u32 size) {
MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped);
}
+/**
+ * This function should only be called for virtual addreses with attribute `PageType::Special`.
+ */
+static MMIORegionPointer GetMMIOHandler(VAddr vaddr) {
+ for (const auto& region : current_page_table->special_regions) {
+ if (vaddr >= region.base && vaddr < (region.base + region.size)) {
+ return region.handler;
+ }
+ }
+ ASSERT_MSG(false, "Mapped IO page without a handler @ %08X", vaddr);
+ return nullptr; // Should never happen
+}
+
+template<typename T>
+T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
+
template <typename T>
T Read(const VAddr vaddr) {
const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
@@ -108,14 +138,17 @@ T Read(const VAddr vaddr) {
return 0;
case PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
+ break;
case PageType::Special:
- LOG_ERROR(HW_Memory, "I/O reads aren't implemented yet @ %08X", vaddr);
- return 0;
+ return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr);
default:
UNREACHABLE();
}
}
+template<typename T>
+void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data);
+
template <typename T>
void Write(const VAddr vaddr, const T data) {
u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
@@ -131,9 +164,10 @@ void Write(const VAddr vaddr, const T data) {
return;
case PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr);
+ break;
case PageType::Special:
- LOG_ERROR(HW_Memory, "I/O writes aren't implemented yet @ %08X", vaddr);
- return;
+ WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data);
+ break;
default:
UNREACHABLE();
}
@@ -191,6 +225,46 @@ void WriteBlock(const VAddr addr, const u8* data, const size_t size) {
}
}
+template<>
+u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) {
+ return mmio_handler->Read8(addr);
+}
+
+template<>
+u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) {
+ return mmio_handler->Read16(addr);
+}
+
+template<>
+u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) {
+ return mmio_handler->Read32(addr);
+}
+
+template<>
+u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) {
+ return mmio_handler->Read64(addr);
+}
+
+template<>
+void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) {
+ mmio_handler->Write8(addr, data);
+}
+
+template<>
+void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) {
+ mmio_handler->Write16(addr, data);
+}
+
+template<>
+void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) {
+ mmio_handler->Write32(addr, data);
+}
+
+template<>
+void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) {
+ mmio_handler->Write64(addr, data);
+}
+
PAddr VirtualToPhysicalAddress(const VAddr addr) {
if (addr == 0) {
return 0;
@@ -217,7 +291,7 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) {
} else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) {
return addr - VRAM_PADDR + VRAM_VADDR;
} else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) {
- return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapBase();
+ return addr - FCRAM_PADDR + Kernel::g_current_process->GetLinearHeapAreaAddress();
} else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) {
return addr - DSP_RAM_PADDR + DSP_RAM_VADDR;
} else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) {
diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h
index 84ff30120..05f70a1fe 100644
--- a/src/core/memory_setup.h
+++ b/src/core/memory_setup.h
@@ -23,10 +23,11 @@ void MapMemoryRegion(VAddr base, u32 size, u8* target);
/**
* Maps a region of the emulated process address space as a IO region.
- * @note Currently this can only be used to mark a region as being IO, since actual memory-mapped
- * IO isn't yet supported.
+ * @param base The address to start mapping at. Must be page-aligned.
+ * @param size The amount of bytes to map. Must be page-aligned.
+ * @param mmio_handler The handler that backs the mapping.
*/
-void MapIoRegion(VAddr base, u32 size);
+void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler);
void UnmapRegion(VAddr base, u32 size);
diff --git a/src/core/mmio.h b/src/core/mmio.h
new file mode 100644
index 000000000..06b555e98
--- /dev/null
+++ b/src/core/mmio.h
@@ -0,0 +1,34 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+
+namespace Memory {
+
+/**
+ * Represents a device with memory mapped IO.
+ * A device may be mapped to multiple regions of memory.
+ */
+class MMIORegion {
+public:
+ virtual ~MMIORegion() = default;
+
+ virtual u8 Read8(VAddr addr) = 0;
+ virtual u16 Read16(VAddr addr) = 0;
+ virtual u32 Read32(VAddr addr) = 0;
+ virtual u64 Read64(VAddr addr) = 0;
+
+ virtual void Write8(VAddr addr, u8 data) = 0;
+ virtual void Write16(VAddr addr, u16 data) = 0;
+ virtual void Write32(VAddr addr, u32 data) = 0;
+ virtual void Write64(VAddr addr, u64 data) = 0;
+};
+
+using MMIORegionPointer = std::shared_ptr<MMIORegion>;
+
+};
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 7e9c56538..4a4757af3 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -2,9 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "audio_core/audio_core.h"
+
#include "core/core.h"
#include "core/core_timing.h"
#include "core/system.h"
+#include "core/gdbstub/gdbstub.h"
#include "core/hw/hw.h"
#include "core/hle/hle.h"
#include "core/hle/kernel/kernel.h"
@@ -12,23 +15,27 @@
#include "video_core/video_core.h"
-#include "core/gdbstub/gdbstub.h"
-
namespace System {
-void Init(EmuWindow* emu_window) {
+Result Init(EmuWindow* emu_window) {
Core::Init();
CoreTiming::Init();
Memory::Init();
HW::Init();
Kernel::Init();
HLE::Init();
- VideoCore::Init(emu_window);
+ if (!VideoCore::Init(emu_window)) {
+ return Result::ErrorInitVideoCore;
+ }
+ AudioCore::Init();
GDBStub::Init();
+
+ return Result::Success;
}
void Shutdown() {
GDBStub::Shutdown();
+ AudioCore::Shutdown();
VideoCore::Shutdown();
HLE::Shutdown();
Kernel::Shutdown();
diff --git a/src/core/system.h b/src/core/system.h
index 59a75ca12..a4a627ea9 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -8,7 +8,14 @@ class EmuWindow;
namespace System {
-void Init(EmuWindow* emu_window);
+enum class Result {
+ Success, ///< Everything is fine
+ Error, ///< Something went wrong (no module specified)
+ ErrorInitCore, ///< Something went wrong during core init
+ ErrorInitVideoCore, ///< Something went wrong during video core init
+};
+
+Result Init(EmuWindow* emu_window);
void Shutdown();
}
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 2a924f4ad..76cfd4f7d 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -11,8 +11,10 @@ set(SRCS
pica.cpp
primitive_assembly.cpp
rasterizer.cpp
+ renderer_base.cpp
shader/shader.cpp
shader/shader_interpreter.cpp
+ swrasterizer.cpp
utils.cpp
video_core.cpp
)
@@ -30,13 +32,16 @@ set(HEADERS
clipper.h
command_processor.h
gpu_debugger.h
- hwrasterizer_base.h
pica.h
+ pica_state.h
+ pica_types.h
primitive_assembly.h
rasterizer.h
+ rasterizer_interface.h
renderer_base.h
shader/shader.h
shader/shader_interpreter.h
+ swrasterizer.h
utils.h
video_core.h
)
diff --git a/src/video_core/clipper.cpp b/src/video_core/clipper.cpp
index ed99c4f13..3d503486e 100644
--- a/src/video_core/clipper.cpp
+++ b/src/video_core/clipper.cpp
@@ -6,6 +6,7 @@
#include "video_core/clipper.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/rasterizer.h"
#include "video_core/shader/shader_interpreter.h"
@@ -59,15 +60,17 @@ static void InitScreenCoordinates(OutputVertex& vtx)
} viewport;
const auto& regs = g_state.regs;
- viewport.halfsize_x = float24::FromRawFloat24(regs.viewport_size_x);
- viewport.halfsize_y = float24::FromRawFloat24(regs.viewport_size_y);
+ viewport.halfsize_x = float24::FromRaw(regs.viewport_size_x);
+ viewport.halfsize_y = float24::FromRaw(regs.viewport_size_y);
viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.viewport_corner.x));
viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.viewport_corner.y));
- viewport.zscale = float24::FromRawFloat24(regs.viewport_depth_range);
- viewport.offset_z = float24::FromRawFloat24(regs.viewport_depth_far_plane);
+ viewport.zscale = float24::FromRaw(regs.viewport_depth_range);
+ viewport.offset_z = float24::FromRaw(regs.viewport_depth_far_plane);
float24 inv_w = float24::FromFloat32(1.f) / vtx.pos.w;
vtx.color *= inv_w;
+ vtx.view *= inv_w;
+ vtx.quat *= inv_w;
vtx.tc0 *= inv_w;
vtx.tc1 *= inv_w;
vtx.tc2 *= inv_w;
@@ -78,7 +81,7 @@ static void InitScreenCoordinates(OutputVertex& vtx)
vtx.screenpos[2] = viewport.offset_z + vtx.pos.z * inv_w * viewport.zscale;
}
-void ProcessTriangle(OutputVertex &v0, OutputVertex &v1, OutputVertex &v2) {
+void ProcessTriangle(const OutputVertex &v0, const OutputVertex &v1, const OutputVertex &v2) {
using boost::container::static_vector;
// Clipping a planar n-gon against a plane will remove at least 1 vertex and introduces 2 at
diff --git a/src/video_core/clipper.h b/src/video_core/clipper.h
index 6ed01e877..f85d8d4c9 100644
--- a/src/video_core/clipper.h
+++ b/src/video_core/clipper.h
@@ -14,7 +14,7 @@ namespace Clipper {
using Shader::OutputVertex;
-void ProcessTriangle(OutputVertex& v0, OutputVertex& v1, OutputVertex& v2);
+void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2);
} // namespace
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index bd1b09a4b..2274dfa66 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -15,6 +15,7 @@
#include "video_core/clipper.h"
#include "video_core/command_processor.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/primitive_assembly.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -73,6 +74,14 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D);
break;
+ case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
+ if (regs.vs_default_attributes_setup.index == 15) {
+ // Reset immediate primitive state
+ g_state.immediate.primitive_assembler.Reconfigure(regs.triangle_topology);
+ g_state.immediate.attribute_id = 0;
+ }
+ break;
+
// Load default vertex input attributes
case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233):
case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234):
@@ -98,21 +107,58 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
Math::Vec4<float24>& attribute = g_state.vs.default_attributes[setup.index];
// NOTE: The destination component order indeed is "backwards"
- attribute.w = float24::FromRawFloat24(default_attr_write_buffer[0] >> 8);
- attribute.z = float24::FromRawFloat24(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF));
- attribute.y = float24::FromRawFloat24(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF));
- attribute.x = float24::FromRawFloat24(default_attr_write_buffer[2] & 0xFFFFFF);
+ attribute.w = float24::FromRaw(default_attr_write_buffer[0] >> 8);
+ attribute.z = float24::FromRaw(((default_attr_write_buffer[0] & 0xFF) << 16) | ((default_attr_write_buffer[1] >> 16) & 0xFFFF));
+ attribute.y = float24::FromRaw(((default_attr_write_buffer[1] & 0xFFFF) << 8) | ((default_attr_write_buffer[2] >> 24) & 0xFF));
+ attribute.x = float24::FromRaw(default_attr_write_buffer[2] & 0xFFFFFF);
LOG_TRACE(HW_GPU, "Set default VS attribute %x to (%f %f %f %f)", (int)setup.index,
attribute.x.ToFloat32(), attribute.y.ToFloat32(), attribute.z.ToFloat32(),
attribute.w.ToFloat32());
// TODO: Verify that this actually modifies the register!
- setup.index = setup.index + 1;
+ if (setup.index < 15) {
+ setup.index++;
+ } else {
+ // Put each attribute into an immediate input buffer.
+ // When all specified immediate attributes are present, the Vertex Shader is invoked and everything is
+ // sent to the primitive assembler.
+
+ auto& immediate_input = g_state.immediate.input;
+ auto& immediate_attribute_id = g_state.immediate.attribute_id;
+ const auto& attribute_config = regs.vertex_attributes;
+
+ immediate_input.attr[immediate_attribute_id++] = attribute;
+
+ if (immediate_attribute_id >= attribute_config.GetNumTotalAttributes()) {
+ immediate_attribute_id = 0;
+
+ Shader::UnitState<false> shader_unit;
+ Shader::Setup(shader_unit);
+
+ // Send to vertex shader
+ Shader::OutputVertex output = Shader::Run(shader_unit, immediate_input, attribute_config.GetNumTotalAttributes());
+
+ // Send to renderer
+ using Pica::Shader::OutputVertex;
+ auto AddTriangle = [](const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2) {
+ VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
+ };
+
+ g_state.immediate.primitive_assembler.SubmitVertex(output, AddTriangle);
+ }
+ }
}
break;
}
+ case PICA_REG_INDEX(gpu_mode):
+ if (regs.gpu_mode == Regs::GPUMode::Configuring && regs.vs_default_attributes_setup.index == 15) {
+ // Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
+ VideoCore::g_renderer->Rasterizer()->DrawTriangles();
+ }
+ break;
+
case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c):
case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d):
{
@@ -157,15 +203,25 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// TODO: What happens if a loader overwrites a previous one's data?
for (unsigned component = 0; component < loader_config.component_count; ++component) {
- if (component >= 12)
+ if (component >= 12) {
LOG_ERROR(HW_GPU, "Overflow in the vertex attribute loader %u trying to load component %u", loader, component);
+ continue;
+ }
+
u32 attribute_index = loader_config.GetComponent(component);
- vertex_attribute_sources[attribute_index] = load_address;
- vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
- vertex_attribute_formats[attribute_index] = attribute_config.GetFormat(attribute_index);
- vertex_attribute_elements[attribute_index] = attribute_config.GetNumElements(attribute_index);
- vertex_attribute_element_size[attribute_index] = attribute_config.GetElementSizeInBytes(attribute_index);
- load_address += attribute_config.GetStride(attribute_index);
+ if (attribute_index < 12) {
+ vertex_attribute_sources[attribute_index] = load_address;
+ vertex_attribute_strides[attribute_index] = static_cast<u32>(loader_config.byte_count);
+ vertex_attribute_formats[attribute_index] = attribute_config.GetFormat(attribute_index);
+ vertex_attribute_elements[attribute_index] = attribute_config.GetNumElements(attribute_index);
+ vertex_attribute_element_size[attribute_index] = attribute_config.GetElementSizeInBytes(attribute_index);
+ load_address += attribute_config.GetStride(attribute_index);
+ } else if (attribute_index < 16) {
+ // Attribute ids 12, 13, 14 and 15 signify 4, 8, 12 and 16-byte paddings, respectively
+ load_address += (attribute_index - 11) * 4;
+ } else {
+ UNREACHABLE(); // This is truly unreachable due to the number of bits for each component
+ }
}
}
@@ -336,19 +392,14 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
}
}
- if (Settings::values.use_hw_renderer) {
- // Send to hardware renderer
- static auto AddHWTriangle = [](const Pica::Shader::OutputVertex& v0,
- const Pica::Shader::OutputVertex& v1,
- const Pica::Shader::OutputVertex& v2) {
- VideoCore::g_renderer->hw_rasterizer->AddTriangle(v0, v1, v2);
- };
+ // Send to renderer
+ using Pica::Shader::OutputVertex;
+ auto AddTriangle = [](
+ const OutputVertex& v0, const OutputVertex& v1, const OutputVertex& v2) {
+ VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
+ };
- primitive_assembler.SubmitVertex(output, AddHWTriangle);
- } else {
- // Send to triangle clipper
- primitive_assembler.SubmitVertex(output, Clipper::ProcessTriangle);
- }
+ primitive_assembler.SubmitVertex(output, AddTriangle);
}
for (auto& range : memory_accesses.ranges) {
@@ -356,9 +407,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
range.second, range.first);
}
- if (Settings::values.use_hw_renderer) {
- VideoCore::g_renderer->hw_rasterizer->DrawTriangles();
- }
+ VideoCore::g_renderer->Rasterizer()->DrawTriangles();
#if PICA_DUMP_GEOMETRY
geometry_dumper.Dump();
@@ -425,10 +474,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
uniform[3 - i] = float24::FromFloat32(*(float*)(&uniform_write_buffer[i]));
} else {
// TODO: Untested
- uniform.w = float24::FromRawFloat24(uniform_write_buffer[0] >> 8);
- uniform.z = float24::FromRawFloat24(((uniform_write_buffer[0] & 0xFF)<<16) | ((uniform_write_buffer[1] >> 16) & 0xFFFF));
- uniform.y = float24::FromRawFloat24(((uniform_write_buffer[1] & 0xFFFF)<<8) | ((uniform_write_buffer[2] >> 24) & 0xFF));
- uniform.x = float24::FromRawFloat24(uniform_write_buffer[2] & 0xFFFFFF);
+ uniform.w = float24::FromRaw(uniform_write_buffer[0] >> 8);
+ uniform.z = float24::FromRaw(((uniform_write_buffer[0] & 0xFF) << 16) | ((uniform_write_buffer[1] >> 16) & 0xFFFF));
+ uniform.y = float24::FromRaw(((uniform_write_buffer[1] & 0xFFFF) << 8) | ((uniform_write_buffer[2] >> 24) & 0xFF));
+ uniform.x = float24::FromRaw(uniform_write_buffer[2] & 0xFFFFFF);
}
LOG_TRACE(HW_GPU, "Set uniform %x to (%f %f %f %f)", (int)uniform_setup.index,
@@ -436,7 +485,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
uniform.w.ToFloat32());
// TODO: Verify that this actually modifies the register!
- uniform_setup.index = uniform_setup.index + 1;
+ uniform_setup.index.Assign(uniform_setup.index + 1);
}
break;
}
@@ -471,11 +520,29 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
break;
}
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[0], 0x1c8):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[1], 0x1c9):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[2], 0x1ca):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[3], 0x1cb):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[4], 0x1cc):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[5], 0x1cd):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf):
+ {
+ auto& lut_config = regs.lighting.lut_config;
+
+ ASSERT_MSG(lut_config.index < 256, "lut_config.index exceeded maximum value of 255!");
+
+ g_state.lighting.luts[lut_config.type][lut_config.index].raw = value;
+ lut_config.index.Assign(lut_config.index + 1);
+ break;
+ }
+
default:
break;
}
- VideoCore::g_renderer->hw_rasterizer->NotifyPicaRegisterChanged(id);
+ VideoCore::g_renderer->Rasterizer()->NotifyPicaRegisterChanged(id);
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::PicaCommandProcessed, reinterpret_cast<void*>(&id));
@@ -493,9 +560,8 @@ void ProcessCommandList(const u32* list, u32 size) {
u32 value = *g_state.cmd_list.current_ptr++;
const CommandHeader header = { *g_state.cmd_list.current_ptr++ };
- u32 cmd = header.cmd_id;
- WritePicaReg(cmd, value, header.parameter_mask);
+ WritePicaReg(header.cmd_id, value, header.parameter_mask);
for (unsigned i = 0; i < header.extra_data_length; ++i) {
u32 cmd = header.cmd_id + (header.group_commands ? i + 1 : 0);
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index f1cfa9361..271e81ca1 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -28,6 +28,7 @@
#include "core/settings.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/renderer_base.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
@@ -46,10 +47,8 @@ void DebugContext::OnEvent(Event event, void* data) {
{
std::unique_lock<std::mutex> lock(breakpoint_mutex);
- if (Settings::values.use_hw_renderer) {
- // Commit the hardware renderer's framebuffer so it will show on debug widgets
- VideoCore::g_renderer->hw_rasterizer->CommitFramebuffer();
- }
+ // Commit the hardware renderer's framebuffer so it will show on debug widgets
+ VideoCore::g_renderer->Rasterizer()->FlushFramebuffer();
// TODO: Should stop the CPU thread here once we multithread emulation.
@@ -115,7 +114,7 @@ void GeometryDumper::Dump() {
}
-void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const State::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes)
+void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes)
{
struct StuffToWrite {
u8* pointer;
@@ -203,11 +202,11 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, c
if (it == output_info_table.end()) {
output_info_table.emplace_back();
- output_info_table.back().type = type;
- output_info_table.back().component_mask = component_mask;
- output_info_table.back().id = i;
+ output_info_table.back().type.Assign(type);
+ output_info_table.back().component_mask.Assign(component_mask);
+ output_info_table.back().id.Assign(i);
} else {
- it->component_mask = it->component_mask | component_mask;
+ it->component_mask.Assign(it->component_mask | component_mask);
}
} catch (const std::out_of_range& ) {
DEBUG_ASSERT_MSG(false, "Unknown output attribute mapping");
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index 85762f5b4..795160a32 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -17,6 +17,7 @@
#include "core/tracer/recorder.h"
#include "video_core/pica.h"
+#include "video_core/shader/shader.h"
namespace Pica {
@@ -182,7 +183,7 @@ private:
};
void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
- const State::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes);
+ const Shader::ShaderSetup& setup, const Regs::VSOutputAttributes* output_attributes);
// Utility class to log Pica commands.
diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp
index 8c528989e..32ad72674 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -6,72 +6,481 @@
#include <unordered_map>
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/shader/shader.h"
namespace Pica {
State g_state;
+static const std::pair<u16, const char*> register_names[] = {
+ { 0x010, "GPUREG_FINALIZE" },
+
+ { 0x040, "GPUREG_FACECULLING_CONFIG" },
+ { 0x041, "GPUREG_VIEWPORT_WIDTH" },
+ { 0x042, "GPUREG_VIEWPORT_INVW" },
+ { 0x043, "GPUREG_VIEWPORT_HEIGHT" },
+ { 0x044, "GPUREG_VIEWPORT_INVH" },
+
+ { 0x047, "GPUREG_FRAGOP_CLIP" },
+ { 0x048, "GPUREG_FRAGOP_CLIP_DATA0" },
+ { 0x049, "GPUREG_FRAGOP_CLIP_DATA1" },
+ { 0x04A, "GPUREG_FRAGOP_CLIP_DATA2" },
+ { 0x04B, "GPUREG_FRAGOP_CLIP_DATA3" },
+
+ { 0x04D, "GPUREG_DEPTHMAP_SCALE" },
+ { 0x04E, "GPUREG_DEPTHMAP_OFFSET" },
+ { 0x04F, "GPUREG_SH_OUTMAP_TOTAL" },
+ { 0x050, "GPUREG_SH_OUTMAP_O0" },
+ { 0x051, "GPUREG_SH_OUTMAP_O1" },
+ { 0x052, "GPUREG_SH_OUTMAP_O2" },
+ { 0x053, "GPUREG_SH_OUTMAP_O3" },
+ { 0x054, "GPUREG_SH_OUTMAP_O4" },
+ { 0x055, "GPUREG_SH_OUTMAP_O5" },
+ { 0x056, "GPUREG_SH_OUTMAP_O6" },
+
+ { 0x061, "GPUREG_EARLYDEPTH_FUNC" },
+ { 0x062, "GPUREG_EARLYDEPTH_TEST1" },
+ { 0x063, "GPUREG_EARLYDEPTH_CLEAR" },
+ { 0x064, "GPUREG_SH_OUTATTR_MODE" },
+ { 0x065, "GPUREG_SCISSORTEST_MODE" },
+ { 0x066, "GPUREG_SCISSORTEST_POS" },
+ { 0x067, "GPUREG_SCISSORTEST_DIM" },
+ { 0x068, "GPUREG_VIEWPORT_XY" },
+
+ { 0x06A, "GPUREG_EARLYDEPTH_DATA" },
+
+ { 0x06D, "GPUREG_DEPTHMAP_ENABLE" },
+ { 0x06E, "GPUREG_RENDERBUF_DIM" },
+ { 0x06F, "GPUREG_SH_OUTATTR_CLOCK" },
+
+ { 0x080, "GPUREG_TEXUNIT_CONFIG" },
+ { 0x081, "GPUREG_TEXUNIT0_BORDER_COLOR" },
+ { 0x082, "GPUREG_TEXUNIT0_DIM" },
+ { 0x083, "GPUREG_TEXUNIT0_PARAM" },
+ { 0x084, "GPUREG_TEXUNIT0_LOD" },
+ { 0x085, "GPUREG_TEXUNIT0_ADDR1" },
+ { 0x086, "GPUREG_TEXUNIT0_ADDR2" },
+ { 0x087, "GPUREG_TEXUNIT0_ADDR3" },
+ { 0x088, "GPUREG_TEXUNIT0_ADDR4" },
+ { 0x089, "GPUREG_TEXUNIT0_ADDR5" },
+ { 0x08A, "GPUREG_TEXUNIT0_ADDR6" },
+ { 0x08B, "GPUREG_TEXUNIT0_SHADOW" },
+
+ { 0x08E, "GPUREG_TEXUNIT0_TYPE" },
+ { 0x08F, "GPUREG_LIGHTING_ENABLE0" },
+
+ { 0x091, "GPUREG_TEXUNIT1_BORDER_COLOR" },
+ { 0x092, "GPUREG_TEXUNIT1_DIM" },
+ { 0x093, "GPUREG_TEXUNIT1_PARAM" },
+ { 0x094, "GPUREG_TEXUNIT1_LOD" },
+ { 0x095, "GPUREG_TEXUNIT1_ADDR" },
+ { 0x096, "GPUREG_TEXUNIT1_TYPE" },
+
+ { 0x099, "GPUREG_TEXUNIT2_BORDER_COLOR" },
+ { 0x09A, "GPUREG_TEXUNIT2_DIM" },
+ { 0x09B, "GPUREG_TEXUNIT2_PARAM" },
+ { 0x09C, "GPUREG_TEXUNIT2_LOD" },
+ { 0x09D, "GPUREG_TEXUNIT2_ADDR" },
+ { 0x09E, "GPUREG_TEXUNIT2_TYPE" },
+
+ { 0x0A8, "GPUREG_TEXUNIT3_PROCTEX0" },
+ { 0x0A9, "GPUREG_TEXUNIT3_PROCTEX1" },
+ { 0x0AA, "GPUREG_TEXUNIT3_PROCTEX2" },
+ { 0x0AB, "GPUREG_TEXUNIT3_PROCTEX3" },
+ { 0x0AC, "GPUREG_TEXUNIT3_PROCTEX4" },
+ { 0x0AD, "GPUREG_TEXUNIT3_PROCTEX5" },
+
+ { 0x0AF, "GPUREG_PROCTEX_LUT" },
+ { 0x0B0, "GPUREG_PROCTEX_LUT_DATA0" },
+ { 0x0B1, "GPUREG_PROCTEX_LUT_DATA1" },
+ { 0x0B2, "GPUREG_PROCTEX_LUT_DATA2" },
+ { 0x0B3, "GPUREG_PROCTEX_LUT_DATA3" },
+ { 0x0B4, "GPUREG_PROCTEX_LUT_DATA4" },
+ { 0x0B5, "GPUREG_PROCTEX_LUT_DATA5" },
+ { 0x0B6, "GPUREG_PROCTEX_LUT_DATA6" },
+ { 0x0B7, "GPUREG_PROCTEX_LUT_DATA7" },
+
+ { 0x0C0, "GPUREG_TEXENV0_SOURCE" },
+ { 0x0C1, "GPUREG_TEXENV0_OPERAND" },
+ { 0x0C2, "GPUREG_TEXENV0_COMBINER" },
+ { 0x0C3, "GPUREG_TEXENV0_COLOR" },
+ { 0x0C4, "GPUREG_TEXENV0_SCALE" },
+
+ { 0x0C8, "GPUREG_TEXENV1_SOURCE" },
+ { 0x0C9, "GPUREG_TEXENV1_OPERAND" },
+ { 0x0CA, "GPUREG_TEXENV1_COMBINER" },
+ { 0x0CB, "GPUREG_TEXENV1_COLOR" },
+ { 0x0CC, "GPUREG_TEXENV1_SCALE" },
+
+ { 0x0D0, "GPUREG_TEXENV2_SOURCE" },
+ { 0x0D1, "GPUREG_TEXENV2_OPERAND" },
+ { 0x0D2, "GPUREG_TEXENV2_COMBINER" },
+ { 0x0D3, "GPUREG_TEXENV2_COLOR" },
+ { 0x0D4, "GPUREG_TEXENV2_SCALE" },
+
+ { 0x0D8, "GPUREG_TEXENV3_SOURCE" },
+ { 0x0D9, "GPUREG_TEXENV3_OPERAND" },
+ { 0x0DA, "GPUREG_TEXENV3_COMBINER" },
+ { 0x0DB, "GPUREG_TEXENV3_COLOR" },
+ { 0x0DC, "GPUREG_TEXENV3_SCALE" },
+
+ { 0x0E0, "GPUREG_TEXENV_UPDATE_BUFFER" },
+ { 0x0E1, "GPUREG_FOG_COLOR" },
+
+ { 0x0E4, "GPUREG_GAS_ATTENUATION" },
+ { 0x0E5, "GPUREG_GAS_ACCMAX" },
+ { 0x0E6, "GPUREG_FOG_LUT_INDEX" },
+
+ { 0x0E8, "GPUREG_FOG_LUT_DATA0" },
+ { 0x0E9, "GPUREG_FOG_LUT_DATA1" },
+ { 0x0EA, "GPUREG_FOG_LUT_DATA2" },
+ { 0x0EB, "GPUREG_FOG_LUT_DATA3" },
+ { 0x0EC, "GPUREG_FOG_LUT_DATA4" },
+ { 0x0ED, "GPUREG_FOG_LUT_DATA5" },
+ { 0x0EE, "GPUREG_FOG_LUT_DATA6" },
+ { 0x0EF, "GPUREG_FOG_LUT_DATA7" },
+ { 0x0F0, "GPUREG_TEXENV4_SOURCE" },
+ { 0x0F1, "GPUREG_TEXENV4_OPERAND" },
+ { 0x0F2, "GPUREG_TEXENV4_COMBINER" },
+ { 0x0F3, "GPUREG_TEXENV4_COLOR" },
+ { 0x0F4, "GPUREG_TEXENV4_SCALE" },
+
+ { 0x0F8, "GPUREG_TEXENV5_SOURCE" },
+ { 0x0F9, "GPUREG_TEXENV5_OPERAND" },
+ { 0x0FA, "GPUREG_TEXENV5_COMBINER" },
+ { 0x0FB, "GPUREG_TEXENV5_COLOR" },
+ { 0x0FC, "GPUREG_TEXENV5_SCALE" },
+ { 0x0FD, "GPUREG_TEXENV_BUFFER_COLOR" },
+
+ { 0x100, "GPUREG_COLOR_OPERATION" },
+ { 0x101, "GPUREG_BLEND_FUNC" },
+ { 0x102, "GPUREG_LOGIC_OP" },
+ { 0x103, "GPUREG_BLEND_COLOR" },
+ { 0x104, "GPUREG_FRAGOP_ALPHA_TEST" },
+ { 0x105, "GPUREG_STENCIL_TEST" },
+ { 0x106, "GPUREG_STENCIL_OP" },
+ { 0x107, "GPUREG_DEPTH_COLOR_MASK" },
+
+ { 0x110, "GPUREG_FRAMEBUFFER_INVALIDATE" },
+ { 0x111, "GPUREG_FRAMEBUFFER_FLUSH" },
+ { 0x112, "GPUREG_COLORBUFFER_READ" },
+ { 0x113, "GPUREG_COLORBUFFER_WRITE" },
+ { 0x114, "GPUREG_DEPTHBUFFER_READ" },
+ { 0x115, "GPUREG_DEPTHBUFFER_WRITE" },
+ { 0x116, "GPUREG_DEPTHBUFFER_FORMAT" },
+ { 0x117, "GPUREG_COLORBUFFER_FORMAT" },
+ { 0x118, "GPUREG_EARLYDEPTH_TEST2" },
+
+ { 0x11B, "GPUREG_FRAMEBUFFER_BLOCK32" },
+ { 0x11C, "GPUREG_DEPTHBUFFER_LOC" },
+ { 0x11D, "GPUREG_COLORBUFFER_LOC" },
+ { 0x11E, "GPUREG_FRAMEBUFFER_DIM" },
+
+ { 0x120, "GPUREG_GAS_LIGHT_XY" },
+ { 0x121, "GPUREG_GAS_LIGHT_Z" },
+ { 0x122, "GPUREG_GAS_LIGHT_Z_COLOR" },
+ { 0x123, "GPUREG_GAS_LUT_INDEX" },
+ { 0x124, "GPUREG_GAS_LUT_DATA" },
+
+ { 0x126, "GPUREG_GAS_DELTAZ_DEPTH" },
+
+ { 0x130, "GPUREG_FRAGOP_SHADOW" },
+
+ { 0x140, "GPUREG_LIGHT0_SPECULAR0" },
+ { 0x141, "GPUREG_LIGHT0_SPECULAR1" },
+ { 0x142, "GPUREG_LIGHT0_DIFFUSE" },
+ { 0x143, "GPUREG_LIGHT0_AMBIENT" },
+ { 0x144, "GPUREG_LIGHT0_XY" },
+ { 0x145, "GPUREG_LIGHT0_Z" },
+ { 0x146, "GPUREG_LIGHT0_SPOTDIR_XY" },
+ { 0x147, "GPUREG_LIGHT0_SPOTDIR_Z" },
+
+ { 0x149, "GPUREG_LIGHT0_CONFIG" },
+ { 0x14A, "GPUREG_LIGHT0_ATTENUATION_BIAS" },
+ { 0x14B, "GPUREG_LIGHT0_ATTENUATION_SCALE" },
+
+ { 0x150, "GPUREG_LIGHT1_SPECULAR0" },
+ { 0x151, "GPUREG_LIGHT1_SPECULAR1" },
+ { 0x152, "GPUREG_LIGHT1_DIFFUSE" },
+ { 0x153, "GPUREG_LIGHT1_AMBIENT" },
+ { 0x154, "GPUREG_LIGHT1_XY" },
+ { 0x155, "GPUREG_LIGHT1_Z" },
+ { 0x156, "GPUREG_LIGHT1_SPOTDIR_XY" },
+ { 0x157, "GPUREG_LIGHT1_SPOTDIR_Z" },
+
+ { 0x159, "GPUREG_LIGHT1_CONFIG" },
+ { 0x15A, "GPUREG_LIGHT1_ATTENUATION_BIAS" },
+ { 0x15B, "GPUREG_LIGHT1_ATTENUATION_SCALE" },
+
+ { 0x160, "GPUREG_LIGHT2_SPECULAR0" },
+ { 0x161, "GPUREG_LIGHT2_SPECULAR1" },
+ { 0x162, "GPUREG_LIGHT2_DIFFUSE" },
+ { 0x163, "GPUREG_LIGHT2_AMBIENT" },
+ { 0x164, "GPUREG_LIGHT2_XY" },
+ { 0x165, "GPUREG_LIGHT2_Z" },
+ { 0x166, "GPUREG_LIGHT2_SPOTDIR_XY" },
+ { 0x167, "GPUREG_LIGHT2_SPOTDIR_Z" },
+
+ { 0x169, "GPUREG_LIGHT2_CONFIG" },
+ { 0x16A, "GPUREG_LIGHT2_ATTENUATION_BIAS" },
+ { 0x16B, "GPUREG_LIGHT2_ATTENUATION_SCALE" },
+
+ { 0x170, "GPUREG_LIGHT3_SPECULAR0" },
+ { 0x171, "GPUREG_LIGHT3_SPECULAR1" },
+ { 0x172, "GPUREG_LIGHT3_DIFFUSE" },
+ { 0x173, "GPUREG_LIGHT3_AMBIENT" },
+ { 0x174, "GPUREG_LIGHT3_XY" },
+ { 0x175, "GPUREG_LIGHT3_Z" },
+ { 0x176, "GPUREG_LIGHT3_SPOTDIR_XY" },
+ { 0x177, "GPUREG_LIGHT3_SPOTDIR_Z" },
+
+ { 0x179, "GPUREG_LIGHT3_CONFIG" },
+ { 0x17A, "GPUREG_LIGHT3_ATTENUATION_BIAS" },
+ { 0x17B, "GPUREG_LIGHT3_ATTENUATION_SCALE" },
+
+ { 0x180, "GPUREG_LIGHT4_SPECULAR0" },
+ { 0x181, "GPUREG_LIGHT4_SPECULAR1" },
+ { 0x182, "GPUREG_LIGHT4_DIFFUSE" },
+ { 0x183, "GPUREG_LIGHT4_AMBIENT" },
+ { 0x184, "GPUREG_LIGHT4_XY" },
+ { 0x185, "GPUREG_LIGHT4_Z" },
+ { 0x186, "GPUREG_LIGHT4_SPOTDIR_XY" },
+ { 0x187, "GPUREG_LIGHT4_SPOTDIR_Z" },
+
+ { 0x189, "GPUREG_LIGHT4_CONFIG" },
+ { 0x18A, "GPUREG_LIGHT4_ATTENUATION_BIAS" },
+ { 0x18B, "GPUREG_LIGHT4_ATTENUATION_SCALE" },
+
+ { 0x190, "GPUREG_LIGHT5_SPECULAR0" },
+ { 0x191, "GPUREG_LIGHT5_SPECULAR1" },
+ { 0x192, "GPUREG_LIGHT5_DIFFUSE" },
+ { 0x193, "GPUREG_LIGHT5_AMBIENT" },
+ { 0x194, "GPUREG_LIGHT5_XY" },
+ { 0x195, "GPUREG_LIGHT5_Z" },
+ { 0x196, "GPUREG_LIGHT5_SPOTDIR_XY" },
+ { 0x197, "GPUREG_LIGHT5_SPOTDIR_Z" },
+
+ { 0x199, "GPUREG_LIGHT5_CONFIG" },
+ { 0x19A, "GPUREG_LIGHT5_ATTENUATION_BIAS" },
+ { 0x19B, "GPUREG_LIGHT5_ATTENUATION_SCALE" },
+
+ { 0x1A0, "GPUREG_LIGHT6_SPECULAR0" },
+ { 0x1A1, "GPUREG_LIGHT6_SPECULAR1" },
+ { 0x1A2, "GPUREG_LIGHT6_DIFFUSE" },
+ { 0x1A3, "GPUREG_LIGHT6_AMBIENT" },
+ { 0x1A4, "GPUREG_LIGHT6_XY" },
+ { 0x1A5, "GPUREG_LIGHT6_Z" },
+ { 0x1A6, "GPUREG_LIGHT6_SPOTDIR_XY" },
+ { 0x1A7, "GPUREG_LIGHT6_SPOTDIR_Z" },
+
+ { 0x1A9, "GPUREG_LIGHT6_CONFIG" },
+ { 0x1AA, "GPUREG_LIGHT6_ATTENUATION_BIAS" },
+ { 0x1AB, "GPUREG_LIGHT6_ATTENUATION_SCALE" },
+
+ { 0x1B0, "GPUREG_LIGHT7_SPECULAR0" },
+ { 0x1B1, "GPUREG_LIGHT7_SPECULAR1" },
+ { 0x1B2, "GPUREG_LIGHT7_DIFFUSE" },
+ { 0x1B3, "GPUREG_LIGHT7_AMBIENT" },
+ { 0x1B4, "GPUREG_LIGHT7_XY" },
+ { 0x1B5, "GPUREG_LIGHT7_Z" },
+ { 0x1B6, "GPUREG_LIGHT7_SPOTDIR_XY" },
+ { 0x1B7, "GPUREG_LIGHT7_SPOTDIR_Z" },
+
+ { 0x1B9, "GPUREG_LIGHT7_CONFIG" },
+ { 0x1BA, "GPUREG_LIGHT7_ATTENUATION_BIAS" },
+ { 0x1BB, "GPUREG_LIGHT7_ATTENUATION_SCALE" },
+
+ { 0x1C0, "GPUREG_LIGHTING_AMBIENT" },
+
+ { 0x1C2, "GPUREG_LIGHTING_NUM_LIGHTS" },
+ { 0x1C3, "GPUREG_LIGHTING_CONFIG0" },
+ { 0x1C4, "GPUREG_LIGHTING_CONFIG1" },
+ { 0x1C5, "GPUREG_LIGHTING_LUT_INDEX" },
+ { 0x1C6, "GPUREG_LIGHTING_ENABLE1" },
+
+ { 0x1C8, "GPUREG_LIGHTING_LUT_DATA0" },
+ { 0x1C9, "GPUREG_LIGHTING_LUT_DATA1" },
+ { 0x1CA, "GPUREG_LIGHTING_LUT_DATA2" },
+ { 0x1CB, "GPUREG_LIGHTING_LUT_DATA3" },
+ { 0x1CC, "GPUREG_LIGHTING_LUT_DATA4" },
+ { 0x1CD, "GPUREG_LIGHTING_LUT_DATA5" },
+ { 0x1CE, "GPUREG_LIGHTING_LUT_DATA6" },
+ { 0x1CF, "GPUREG_LIGHTING_LUT_DATA7" },
+ { 0x1D0, "GPUREG_LIGHTING_LUTINPUT_ABS" },
+ { 0x1D1, "GPUREG_LIGHTING_LUTINPUT_SELECT" },
+ { 0x1D2, "GPUREG_LIGHTING_LUTINPUT_SCALE" },
+
+ { 0x1D9, "GPUREG_LIGHTING_LIGHT_PERMUTATION" },
+
+ { 0x200, "GPUREG_ATTRIBBUFFERS_LOC" },
+ { 0x201, "GPUREG_ATTRIBBUFFERS_FORMAT_LOW" },
+ { 0x202, "GPUREG_ATTRIBBUFFERS_FORMAT_HIGH" },
+ { 0x203, "GPUREG_ATTRIBBUFFER0_OFFSET" },
+ { 0x204, "GPUREG_ATTRIBBUFFER0_CONFIG1" },
+ { 0x205, "GPUREG_ATTRIBBUFFER0_CONFIG2" },
+ { 0x206, "GPUREG_ATTRIBBUFFER1_OFFSET" },
+ { 0x207, "GPUREG_ATTRIBBUFFER1_CONFIG1" },
+ { 0x208, "GPUREG_ATTRIBBUFFER1_CONFIG2" },
+ { 0x209, "GPUREG_ATTRIBBUFFER2_OFFSET" },
+ { 0x20A, "GPUREG_ATTRIBBUFFER2_CONFIG1" },
+ { 0x20B, "GPUREG_ATTRIBBUFFER2_CONFIG2" },
+ { 0x20C, "GPUREG_ATTRIBBUFFER3_OFFSET" },
+ { 0x20D, "GPUREG_ATTRIBBUFFER3_CONFIG1" },
+ { 0x20E, "GPUREG_ATTRIBBUFFER3_CONFIG2" },
+ { 0x20F, "GPUREG_ATTRIBBUFFER4_OFFSET" },
+ { 0x210, "GPUREG_ATTRIBBUFFER4_CONFIG1" },
+ { 0x211, "GPUREG_ATTRIBBUFFER4_CONFIG2" },
+ { 0x212, "GPUREG_ATTRIBBUFFER5_OFFSET" },
+ { 0x213, "GPUREG_ATTRIBBUFFER5_CONFIG1" },
+ { 0x214, "GPUREG_ATTRIBBUFFER5_CONFIG2" },
+ { 0x215, "GPUREG_ATTRIBBUFFER6_OFFSET" },
+ { 0x216, "GPUREG_ATTRIBBUFFER6_CONFIG1" },
+ { 0x217, "GPUREG_ATTRIBBUFFER6_CONFIG2" },
+ { 0x218, "GPUREG_ATTRIBBUFFER7_OFFSET" },
+ { 0x219, "GPUREG_ATTRIBBUFFER7_CONFIG1" },
+ { 0x21A, "GPUREG_ATTRIBBUFFER7_CONFIG2" },
+ { 0x21B, "GPUREG_ATTRIBBUFFER8_OFFSET" },
+ { 0x21C, "GPUREG_ATTRIBBUFFER8_CONFIG1" },
+ { 0x21D, "GPUREG_ATTRIBBUFFER8_CONFIG2" },
+ { 0x21E, "GPUREG_ATTRIBBUFFER9_OFFSET" },
+ { 0x21F, "GPUREG_ATTRIBBUFFER9_CONFIG1" },
+ { 0x220, "GPUREG_ATTRIBBUFFER9_CONFIG2" },
+ { 0x221, "GPUREG_ATTRIBBUFFER10_OFFSET" },
+ { 0x222, "GPUREG_ATTRIBBUFFER10_CONFIG1" },
+ { 0x223, "GPUREG_ATTRIBBUFFER10_CONFIG2" },
+ { 0x224, "GPUREG_ATTRIBBUFFER11_OFFSET" },
+ { 0x225, "GPUREG_ATTRIBBUFFER11_CONFIG1" },
+ { 0x226, "GPUREG_ATTRIBBUFFER11_CONFIG2" },
+ { 0x227, "GPUREG_INDEXBUFFER_CONFIG" },
+ { 0x228, "GPUREG_NUMVERTICES" },
+ { 0x229, "GPUREG_GEOSTAGE_CONFIG" },
+ { 0x22A, "GPUREG_VERTEX_OFFSET" },
+
+ { 0x22D, "GPUREG_POST_VERTEX_CACHE_NUM" },
+ { 0x22E, "GPUREG_DRAWARRAYS" },
+ { 0x22F, "GPUREG_DRAWELEMENTS" },
+
+ { 0x231, "GPUREG_VTX_FUNC" },
+ { 0x232, "GPUREG_FIXEDATTRIB_INDEX" },
+ { 0x233, "GPUREG_FIXEDATTRIB_DATA0" },
+ { 0x234, "GPUREG_FIXEDATTRIB_DATA1" },
+ { 0x235, "GPUREG_FIXEDATTRIB_DATA2" },
+
+ { 0x238, "GPUREG_CMDBUF_SIZE0" },
+ { 0x239, "GPUREG_CMDBUF_SIZE1" },
+ { 0x23A, "GPUREG_CMDBUF_ADDR0" },
+ { 0x23B, "GPUREG_CMDBUF_ADDR1" },
+ { 0x23C, "GPUREG_CMDBUF_JUMP0" },
+ { 0x23D, "GPUREG_CMDBUF_JUMP1" },
+
+ { 0x242, "GPUREG_VSH_NUM_ATTR" },
+
+ { 0x244, "GPUREG_VSH_COM_MODE" },
+ { 0x245, "GPUREG_START_DRAW_FUNC0" },
+
+ { 0x24A, "GPUREG_VSH_OUTMAP_TOTAL1" },
+
+ { 0x251, "GPUREG_VSH_OUTMAP_TOTAL2" },
+ { 0x252, "GPUREG_GSH_MISC0" },
+ { 0x253, "GPUREG_GEOSTAGE_CONFIG2" },
+ { 0x254, "GPUREG_GSH_MISC1" },
+
+ { 0x25E, "GPUREG_PRIMITIVE_CONFIG" },
+ { 0x25F, "GPUREG_RESTART_PRIMITIVE" },
+
+ { 0x280, "GPUREG_GSH_BOOLUNIFORM" },
+ { 0x281, "GPUREG_GSH_INTUNIFORM_I0" },
+ { 0x282, "GPUREG_GSH_INTUNIFORM_I1" },
+ { 0x283, "GPUREG_GSH_INTUNIFORM_I2" },
+ { 0x284, "GPUREG_GSH_INTUNIFORM_I3" },
+
+ { 0x289, "GPUREG_GSH_INPUTBUFFER_CONFIG" },
+ { 0x28A, "GPUREG_GSH_ENTRYPOINT" },
+ { 0x28B, "GPUREG_GSH_ATTRIBUTES_PERMUTATION_LOW" },
+ { 0x28C, "GPUREG_GSH_ATTRIBUTES_PERMUTATION_HIGH" },
+ { 0x28D, "GPUREG_GSH_OUTMAP_MASK" },
+
+ { 0x28F, "GPUREG_GSH_CODETRANSFER_END" },
+ { 0x290, "GPUREG_GSH_FLOATUNIFORM_INDEX" },
+ { 0x291, "GPUREG_GSH_FLOATUNIFORM_DATA0" },
+ { 0x292, "GPUREG_GSH_FLOATUNIFORM_DATA1" },
+ { 0x293, "GPUREG_GSH_FLOATUNIFORM_DATA2" },
+ { 0x294, "GPUREG_GSH_FLOATUNIFORM_DATA3" },
+ { 0x295, "GPUREG_GSH_FLOATUNIFORM_DATA4" },
+ { 0x296, "GPUREG_GSH_FLOATUNIFORM_DATA5" },
+ { 0x297, "GPUREG_GSH_FLOATUNIFORM_DATA6" },
+ { 0x298, "GPUREG_GSH_FLOATUNIFORM_DATA7" },
+
+ { 0x29B, "GPUREG_GSH_CODETRANSFER_INDEX" },
+ { 0x29C, "GPUREG_GSH_CODETRANSFER_DATA0" },
+ { 0x29D, "GPUREG_GSH_CODETRANSFER_DATA1" },
+ { 0x29E, "GPUREG_GSH_CODETRANSFER_DATA2" },
+ { 0x29F, "GPUREG_GSH_CODETRANSFER_DATA3" },
+ { 0x2A0, "GPUREG_GSH_CODETRANSFER_DATA4" },
+ { 0x2A1, "GPUREG_GSH_CODETRANSFER_DATA5" },
+ { 0x2A2, "GPUREG_GSH_CODETRANSFER_DATA6" },
+ { 0x2A3, "GPUREG_GSH_CODETRANSFER_DATA7" },
+
+ { 0x2A5, "GPUREG_GSH_OPDESCS_INDEX" },
+ { 0x2A6, "GPUREG_GSH_OPDESCS_DATA0" },
+ { 0x2A7, "GPUREG_GSH_OPDESCS_DATA1" },
+ { 0x2A8, "GPUREG_GSH_OPDESCS_DATA2" },
+ { 0x2A9, "GPUREG_GSH_OPDESCS_DATA3" },
+ { 0x2AA, "GPUREG_GSH_OPDESCS_DATA4" },
+ { 0x2AB, "GPUREG_GSH_OPDESCS_DATA5" },
+ { 0x2AC, "GPUREG_GSH_OPDESCS_DATA6" },
+ { 0x2AD, "GPUREG_GSH_OPDESCS_DATA7" },
+
+ { 0x2B0, "GPUREG_VSH_BOOLUNIFORM" },
+ { 0x2B1, "GPUREG_VSH_INTUNIFORM_I0" },
+ { 0x2B2, "GPUREG_VSH_INTUNIFORM_I1" },
+ { 0x2B3, "GPUREG_VSH_INTUNIFORM_I2" },
+ { 0x2B4, "GPUREG_VSH_INTUNIFORM_I3" },
+
+ { 0x2B9, "GPUREG_VSH_INPUTBUFFER_CONFIG" },
+ { 0x2BA, "GPUREG_VSH_ENTRYPOINT" },
+ { 0x2BB, "GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW" },
+ { 0x2BC, "GPUREG_VSH_ATTRIBUTES_PERMUTATION_HIGH" },
+ { 0x2BD, "GPUREG_VSH_OUTMAP_MASK" },
+
+ { 0x2BF, "GPUREG_VSH_CODETRANSFER_END" },
+ { 0x2C0, "GPUREG_VSH_FLOATUNIFORM_INDEX" },
+ { 0x2C1, "GPUREG_VSH_FLOATUNIFORM_DATA0" },
+ { 0x2C2, "GPUREG_VSH_FLOATUNIFORM_DATA1" },
+ { 0x2C3, "GPUREG_VSH_FLOATUNIFORM_DATA2" },
+ { 0x2C4, "GPUREG_VSH_FLOATUNIFORM_DATA3" },
+ { 0x2C5, "GPUREG_VSH_FLOATUNIFORM_DATA4" },
+ { 0x2C6, "GPUREG_VSH_FLOATUNIFORM_DATA5" },
+ { 0x2C7, "GPUREG_VSH_FLOATUNIFORM_DATA6" },
+ { 0x2C8, "GPUREG_VSH_FLOATUNIFORM_DATA7" },
+
+ { 0x2CB, "GPUREG_VSH_CODETRANSFER_INDEX" },
+ { 0x2CC, "GPUREG_VSH_CODETRANSFER_DATA0" },
+ { 0x2CD, "GPUREG_VSH_CODETRANSFER_DATA1" },
+ { 0x2CE, "GPUREG_VSH_CODETRANSFER_DATA2" },
+ { 0x2CF, "GPUREG_VSH_CODETRANSFER_DATA3" },
+ { 0x2D0, "GPUREG_VSH_CODETRANSFER_DATA4" },
+ { 0x2D1, "GPUREG_VSH_CODETRANSFER_DATA5" },
+ { 0x2D2, "GPUREG_VSH_CODETRANSFER_DATA6" },
+ { 0x2D3, "GPUREG_VSH_CODETRANSFER_DATA7" },
+
+ { 0x2D5, "GPUREG_VSH_OPDESCS_INDEX" },
+ { 0x2D6, "GPUREG_VSH_OPDESCS_DATA0" },
+ { 0x2D7, "GPUREG_VSH_OPDESCS_DATA1" },
+ { 0x2D8, "GPUREG_VSH_OPDESCS_DATA2" },
+ { 0x2D9, "GPUREG_VSH_OPDESCS_DATA3" },
+ { 0x2DA, "GPUREG_VSH_OPDESCS_DATA4" },
+ { 0x2DB, "GPUREG_VSH_OPDESCS_DATA5" },
+ { 0x2DC, "GPUREG_VSH_OPDESCS_DATA6" },
+ { 0x2DD, "GPUREG_VSH_OPDESCS_DATA7" },
+};
+
std::string Regs::GetCommandName(int index) {
- static std::unordered_map<u32, std::string> map;
+ static std::unordered_map<u32, const char*> map;
if (map.empty()) {
- #define ADD_FIELD(name) \
- map.insert({static_cast<u32>(PICA_REG_INDEX(name)), #name}); \
- /* TODO: change to Regs::name when VS2015 and other compilers support it */ \
- for (u32 i = PICA_REG_INDEX(name) + 1; i < PICA_REG_INDEX(name) + sizeof(Regs().name) / 4; ++i) \
- map.insert({i, #name + std::string("+") + std::to_string(i-PICA_REG_INDEX(name))}); \
-
- ADD_FIELD(trigger_irq);
- ADD_FIELD(cull_mode);
- ADD_FIELD(viewport_size_x);
- ADD_FIELD(viewport_size_y);
- ADD_FIELD(viewport_depth_range);
- ADD_FIELD(viewport_depth_far_plane);
- ADD_FIELD(viewport_corner);
- ADD_FIELD(texture0_enable);
- ADD_FIELD(texture0);
- ADD_FIELD(texture0_format);
- ADD_FIELD(texture1);
- ADD_FIELD(texture1_format);
- ADD_FIELD(texture2);
- ADD_FIELD(texture2_format);
- ADD_FIELD(tev_stage0);
- ADD_FIELD(tev_stage1);
- ADD_FIELD(tev_stage2);
- ADD_FIELD(tev_stage3);
- ADD_FIELD(tev_combiner_buffer_input);
- ADD_FIELD(tev_stage4);
- ADD_FIELD(tev_stage5);
- ADD_FIELD(tev_combiner_buffer_color);
- ADD_FIELD(output_merger);
- ADD_FIELD(framebuffer);
- ADD_FIELD(vertex_attributes);
- ADD_FIELD(index_array);
- ADD_FIELD(num_vertices);
- ADD_FIELD(vertex_offset);
- ADD_FIELD(trigger_draw);
- ADD_FIELD(trigger_draw_indexed);
- ADD_FIELD(vs_default_attributes_setup);
- ADD_FIELD(command_buffer);
- ADD_FIELD(triangle_topology);
- ADD_FIELD(restart_primitive);
- ADD_FIELD(gs.bool_uniforms);
- ADD_FIELD(gs.int_uniforms);
- ADD_FIELD(gs.main_offset);
- ADD_FIELD(gs.input_register_map);
- ADD_FIELD(gs.uniform_setup);
- ADD_FIELD(gs.program);
- ADD_FIELD(gs.swizzle_patterns);
- ADD_FIELD(vs.bool_uniforms);
- ADD_FIELD(vs.int_uniforms);
- ADD_FIELD(vs.main_offset);
- ADD_FIELD(vs.input_register_map);
- ADD_FIELD(vs.uniform_setup);
- ADD_FIELD(vs.program);
- ADD_FIELD(vs.swizzle_patterns);
-
-#undef ADD_FIELD
+ map.insert(begin(register_names), end(register_names));
}
// Return empty string if no match is found
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index 2f1b2dec4..2e0c33201 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -16,6 +16,8 @@
#include "common/vector_math.h"
#include "common/logging/log.h"
+#include "pica_types.h"
+
namespace Pica {
// Returns index corresponding to the Regs member labeled by field_name
@@ -239,7 +241,8 @@ struct Regs {
TextureConfig texture0;
INSERT_PADDING_WORDS(0x8);
BitField<0, 4, TextureFormat> texture0_format;
- INSERT_PADDING_WORDS(0x2);
+ BitField<0, 1, u32> fragment_lighting_enable;
+ INSERT_PADDING_WORDS(0x1);
TextureConfig texture1;
BitField<0, 4, TextureFormat> texture1_format;
INSERT_PADDING_WORDS(0x2);
@@ -641,7 +644,268 @@ struct Regs {
}
}
- INSERT_PADDING_WORDS(0xe0);
+ INSERT_PADDING_WORDS(0x20);
+
+ enum class LightingSampler {
+ Distribution0 = 0,
+ Distribution1 = 1,
+ Fresnel = 3,
+ ReflectBlue = 4,
+ ReflectGreen = 5,
+ ReflectRed = 6,
+ SpotlightAttenuation = 8,
+ DistanceAttenuation = 16,
+ };
+
+ /**
+ * Pica fragment lighting supports using different LUTs for each lighting component:
+ * Reflectance R, G, and B channels, distribution function for specular components 0 and 1,
+ * fresnel factor, and spotlight attenuation. Furthermore, which LUTs are used for each channel
+ * (or whether a channel is enabled at all) is specified by various pre-defined lighting
+ * configurations. With configurations that require more LUTs, more cycles are required on HW to
+ * perform lighting computations.
+ */
+ enum class LightingConfig {
+ Config0 = 0, ///< Reflect Red, Distribution 0, Spotlight
+ Config1 = 1, ///< Reflect Red, Fresnel, Spotlight
+ Config2 = 2, ///< Reflect Red, Distribution 0/1
+ Config3 = 3, ///< Distribution 0/1, Fresnel
+ Config4 = 4, ///< Reflect Red/Green/Blue, Distribution 0/1, Spotlight
+ Config5 = 5, ///< Reflect Red/Green/Blue, Distribution 0, Fresnel, Spotlight
+ Config6 = 6, ///< Reflect Red, Distribution 0/1, Fresnel, Spotlight
+ Config7 = 8, ///< Reflect Red/Green/Blue, Distribution 0/1, Fresnel, Spotlight
+ ///< NOTE: '8' is intentional, '7' does not appear to be a valid configuration
+ };
+
+ /// Selects which lighting components are affected by fresnel
+ enum class LightingFresnelSelector {
+ None = 0, ///< Fresnel is disabled
+ PrimaryAlpha = 1, ///< Primary (diffuse) lighting alpha is affected by fresnel
+ SecondaryAlpha = 2, ///< Secondary (specular) lighting alpha is affected by fresnel
+ Both = PrimaryAlpha | SecondaryAlpha, ///< Both primary and secondary lighting alphas are affected by fresnel
+ };
+
+ /// Factor used to scale the output of a lighting LUT
+ enum class LightingScale {
+ Scale1 = 0, ///< Scale is 1x
+ Scale2 = 1, ///< Scale is 2x
+ Scale4 = 2, ///< Scale is 4x
+ Scale8 = 3, ///< Scale is 8x
+ Scale1_4 = 6, ///< Scale is 0.25x
+ Scale1_2 = 7, ///< Scale is 0.5x
+ };
+
+ enum class LightingLutInput {
+ NH = 0, // Cosine of the angle between the normal and half-angle vectors
+ VH = 1, // Cosine of the angle between the view and half-angle vectors
+ NV = 2, // Cosine of the angle between the normal and the view vector
+ LN = 3, // Cosine of the angle between the light and the normal vectors
+ };
+
+ enum class LightingBumpMode : u32 {
+ None = 0,
+ NormalMap = 1,
+ TangentMap = 2,
+ };
+
+ union LightColor {
+ BitField< 0, 10, u32> b;
+ BitField<10, 10, u32> g;
+ BitField<20, 10, u32> r;
+
+ Math::Vec3f ToVec3f() const {
+ // These fields are 10 bits wide, however 255 corresponds to 1.0f for each color component
+ return Math::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f);
+ }
+ };
+
+ /// Returns true if the specified lighting sampler is supported by the current Pica lighting configuration
+ static bool IsLightingSamplerSupported(LightingConfig config, LightingSampler sampler) {
+ switch (sampler) {
+ case LightingSampler::Distribution0:
+ return (config != LightingConfig::Config1);
+
+ case LightingSampler::Distribution1:
+ return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) && (config != LightingConfig::Config5);
+
+ case LightingSampler::Fresnel:
+ return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) && (config != LightingConfig::Config4);
+
+ case LightingSampler::ReflectRed:
+ return (config != LightingConfig::Config3);
+
+ case LightingSampler::ReflectGreen:
+ case LightingSampler::ReflectBlue:
+ return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) || (config == LightingConfig::Config7);
+ }
+ return false;
+ }
+
+ struct {
+ struct LightSrc {
+ LightColor specular_0; // material.specular_0 * light.specular_0
+ LightColor specular_1; // material.specular_1 * light.specular_1
+ LightColor diffuse; // material.diffuse * light.diffuse
+ LightColor ambient; // material.ambient * light.ambient
+
+ struct {
+ // Encoded as 16-bit floating point
+ union {
+ BitField< 0, 16, u32> x;
+ BitField<16, 16, u32> y;
+ };
+ union {
+ BitField< 0, 16, u32> z;
+ };
+
+ INSERT_PADDING_WORDS(0x3);
+
+ union {
+ BitField<0, 1, u32> directional;
+ BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
+ };
+ };
+
+ BitField<0, 20, u32> dist_atten_bias;
+ BitField<0, 20, u32> dist_atten_scale;
+
+ INSERT_PADDING_WORDS(0x4);
+ };
+ static_assert(sizeof(LightSrc) == 0x10 * sizeof(u32), "LightSrc structure must be 0x10 words");
+
+ LightSrc light[8];
+ LightColor global_ambient; // Emission + (material.ambient * lighting.ambient)
+ INSERT_PADDING_WORDS(0x1);
+ BitField<0, 3, u32> num_lights; // Number of enabled lights - 1
+
+ union {
+ BitField< 2, 2, LightingFresnelSelector> fresnel_selector;
+ BitField< 4, 4, LightingConfig> config;
+ BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
+ BitField<27, 1, u32> clamp_highlights;
+ BitField<28, 2, LightingBumpMode> bump_mode;
+ BitField<30, 1, u32> disable_bump_renorm;
+ };
+
+ union {
+ BitField<16, 1, u32> disable_lut_d0;
+ BitField<17, 1, u32> disable_lut_d1;
+ BitField<19, 1, u32> disable_lut_fr;
+ BitField<20, 1, u32> disable_lut_rr;
+ BitField<21, 1, u32> disable_lut_rg;
+ BitField<22, 1, u32> disable_lut_rb;
+
+ // Each bit specifies whether distance attenuation should be applied for the
+ // corresponding light
+
+ BitField<24, 1, u32> disable_dist_atten_light_0;
+ BitField<25, 1, u32> disable_dist_atten_light_1;
+ BitField<26, 1, u32> disable_dist_atten_light_2;
+ BitField<27, 1, u32> disable_dist_atten_light_3;
+ BitField<28, 1, u32> disable_dist_atten_light_4;
+ BitField<29, 1, u32> disable_dist_atten_light_5;
+ BitField<30, 1, u32> disable_dist_atten_light_6;
+ BitField<31, 1, u32> disable_dist_atten_light_7;
+ };
+
+ bool IsDistAttenDisabled(unsigned index) const {
+ const unsigned disable[] = { disable_dist_atten_light_0, disable_dist_atten_light_1,
+ disable_dist_atten_light_2, disable_dist_atten_light_3,
+ disable_dist_atten_light_4, disable_dist_atten_light_5,
+ disable_dist_atten_light_6, disable_dist_atten_light_7 };
+ return disable[index] != 0;
+ }
+
+ union {
+ BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
+ BitField<8, 5, u32> type; ///< Type of LUT for which to set data
+ } lut_config;
+
+ BitField<0, 1, u32> disable;
+ INSERT_PADDING_WORDS(0x1);
+
+ // When data is written to any of these registers, it gets written to the lookup table of
+ // the selected type at the selected index, specified above in the `lut_config` register.
+ // With each write, `lut_config.index` is incremented. It does not matter which of these
+ // registers is written to, the behavior will be the same.
+ u32 lut_data[8];
+
+ // These are used to specify if absolute (abs) value should be used for each LUT index. When
+ // abs mode is disabled, LUT indexes are in the range of (-1.0, 1.0). Otherwise, they are in
+ // the range of (0.0, 1.0).
+ union {
+ BitField< 1, 1, u32> disable_d0;
+ BitField< 5, 1, u32> disable_d1;
+ BitField< 9, 1, u32> disable_sp;
+ BitField<13, 1, u32> disable_fr;
+ BitField<17, 1, u32> disable_rb;
+ BitField<21, 1, u32> disable_rg;
+ BitField<25, 1, u32> disable_rr;
+ } abs_lut_input;
+
+ union {
+ BitField< 0, 3, LightingLutInput> d0;
+ BitField< 4, 3, LightingLutInput> d1;
+ BitField< 8, 3, LightingLutInput> sp;
+ BitField<12, 3, LightingLutInput> fr;
+ BitField<16, 3, LightingLutInput> rb;
+ BitField<20, 3, LightingLutInput> rg;
+ BitField<24, 3, LightingLutInput> rr;
+ } lut_input;
+
+ union {
+ BitField< 0, 3, LightingScale> d0;
+ BitField< 4, 3, LightingScale> d1;
+ BitField< 8, 3, LightingScale> sp;
+ BitField<12, 3, LightingScale> fr;
+ BitField<16, 3, LightingScale> rb;
+ BitField<20, 3, LightingScale> rg;
+ BitField<24, 3, LightingScale> rr;
+
+ static float GetScale(LightingScale scale) {
+ switch (scale) {
+ case LightingScale::Scale1:
+ return 1.0f;
+ case LightingScale::Scale2:
+ return 2.0f;
+ case LightingScale::Scale4:
+ return 4.0f;
+ case LightingScale::Scale8:
+ return 8.0f;
+ case LightingScale::Scale1_4:
+ return 0.25f;
+ case LightingScale::Scale1_2:
+ return 0.5f;
+ }
+ return 0.0f;
+ }
+ } lut_scale;
+
+ INSERT_PADDING_WORDS(0x6);
+
+ union {
+ // There are 8 light enable "slots", corresponding to the total number of lights
+ // supported by Pica. For N enabled lights (specified by register 0x1c2, or 'src_num'
+ // above), the first N slots below will be set to integers within the range of 0-7,
+ // corresponding to the actual light that is enabled for each slot.
+
+ BitField< 0, 3, u32> slot_0;
+ BitField< 4, 3, u32> slot_1;
+ BitField< 8, 3, u32> slot_2;
+ BitField<12, 3, u32> slot_3;
+ BitField<16, 3, u32> slot_4;
+ BitField<20, 3, u32> slot_5;
+ BitField<24, 3, u32> slot_6;
+ BitField<28, 3, u32> slot_7;
+
+ unsigned GetNum(unsigned index) const {
+ const unsigned enable_slots[] = { slot_0, slot_1, slot_2, slot_3, slot_4, slot_5, slot_6, slot_7 };
+ return enable_slots[index];
+ }
+ } light_enable;
+ } lighting;
+
+ INSERT_PADDING_WORDS(0x26);
enum class VertexAttributeFormat : u64 {
BYTE = 0,
@@ -825,7 +1089,16 @@ struct Regs {
}
} command_buffer;
- INSERT_PADDING_WORDS(0x20);
+ INSERT_PADDING_WORDS(0x07);
+
+ enum class GPUMode : u32 {
+ Drawing = 0,
+ Configuring = 1
+ };
+
+ GPUMode gpu_mode;
+
+ INSERT_PADDING_WORDS(0x18);
enum class TriangleTopology : u32 {
List = 0,
@@ -990,6 +1263,7 @@ ASSERT_REG_POSITION(viewport_corner, 0x68);
ASSERT_REG_POSITION(texture0_enable, 0x80);
ASSERT_REG_POSITION(texture0, 0x81);
ASSERT_REG_POSITION(texture0_format, 0x8e);
+ASSERT_REG_POSITION(fragment_lighting_enable, 0x8f);
ASSERT_REG_POSITION(texture1, 0x91);
ASSERT_REG_POSITION(texture1_format, 0x96);
ASSERT_REG_POSITION(texture2, 0x99);
@@ -1004,6 +1278,7 @@ ASSERT_REG_POSITION(tev_stage5, 0xf8);
ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd);
ASSERT_REG_POSITION(output_merger, 0x100);
ASSERT_REG_POSITION(framebuffer, 0x110);
+ASSERT_REG_POSITION(lighting, 0x140);
ASSERT_REG_POSITION(vertex_attributes, 0x200);
ASSERT_REG_POSITION(index_array, 0x227);
ASSERT_REG_POSITION(num_vertices, 0x228);
@@ -1012,6 +1287,7 @@ ASSERT_REG_POSITION(trigger_draw, 0x22e);
ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f);
ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232);
ASSERT_REG_POSITION(command_buffer, 0x238);
+ASSERT_REG_POSITION(gpu_mode, 0x245);
ASSERT_REG_POSITION(triangle_topology, 0x25e);
ASSERT_REG_POSITION(restart_primitive, 0x25f);
ASSERT_REG_POSITION(gs, 0x280);
@@ -1026,157 +1302,10 @@ static_assert(sizeof(Regs::ShaderConfig) == 0x30 * sizeof(u32), "ShaderConfig st
static_assert(sizeof(Regs) <= 0x300 * sizeof(u32), "Register set structure larger than it should be");
static_assert(sizeof(Regs) >= 0x300 * sizeof(u32), "Register set structure smaller than it should be");
-struct float24 {
- static float24 FromFloat32(float val) {
- float24 ret;
- ret.value = val;
- return ret;
- }
-
- // 16 bit mantissa, 7 bit exponent, 1 bit sign
- // TODO: No idea if this works as intended
- static float24 FromRawFloat24(u32 hex) {
- float24 ret;
- if ((hex & 0xFFFFFF) == 0) {
- ret.value = 0;
- } else {
- u32 mantissa = hex & 0xFFFF;
- u32 exponent = (hex >> 16) & 0x7F;
- u32 sign = hex >> 23;
- ret.value = std::pow(2.0f, (float)exponent-63.0f) * (1.0f + mantissa * std::pow(2.0f, -16.f));
- if (sign)
- ret.value = -ret.value;
- }
- return ret;
- }
-
- static float24 Zero() {
- return FromFloat32(0.f);
- }
-
- // Not recommended for anything but logging
- float ToFloat32() const {
- return value;
- }
-
- float24 operator * (const float24& flt) const {
- if ((this->value == 0.f && !std::isnan(flt.value)) ||
- (flt.value == 0.f && !std::isnan(this->value)))
- // PICA gives 0 instead of NaN when multiplying by inf
- return Zero();
- return float24::FromFloat32(ToFloat32() * flt.ToFloat32());
- }
-
- float24 operator / (const float24& flt) const {
- return float24::FromFloat32(ToFloat32() / flt.ToFloat32());
- }
-
- float24 operator + (const float24& flt) const {
- return float24::FromFloat32(ToFloat32() + flt.ToFloat32());
- }
-
- float24 operator - (const float24& flt) const {
- return float24::FromFloat32(ToFloat32() - flt.ToFloat32());
- }
-
- float24& operator *= (const float24& flt) {
- if ((this->value == 0.f && !std::isnan(flt.value)) ||
- (flt.value == 0.f && !std::isnan(this->value)))
- // PICA gives 0 instead of NaN when multiplying by inf
- *this = Zero();
- else value *= flt.ToFloat32();
- return *this;
- }
-
- float24& operator /= (const float24& flt) {
- value /= flt.ToFloat32();
- return *this;
- }
-
- float24& operator += (const float24& flt) {
- value += flt.ToFloat32();
- return *this;
- }
-
- float24& operator -= (const float24& flt) {
- value -= flt.ToFloat32();
- return *this;
- }
-
- float24 operator - () const {
- return float24::FromFloat32(-ToFloat32());
- }
-
- bool operator < (const float24& flt) const {
- return ToFloat32() < flt.ToFloat32();
- }
-
- bool operator > (const float24& flt) const {
- return ToFloat32() > flt.ToFloat32();
- }
-
- bool operator >= (const float24& flt) const {
- return ToFloat32() >= flt.ToFloat32();
- }
-
- bool operator <= (const float24& flt) const {
- return ToFloat32() <= flt.ToFloat32();
- }
-
- bool operator == (const float24& flt) const {
- return ToFloat32() == flt.ToFloat32();
- }
-
- bool operator != (const float24& flt) const {
- return ToFloat32() != flt.ToFloat32();
- }
-
-private:
- // Stored as a regular float, merely for convenience
- // TODO: Perform proper arithmetic on this!
- float value;
-};
-static_assert(sizeof(float24) == sizeof(float), "Shader JIT assumes float24 is implemented as a 32-bit float");
-
-/// Struct used to describe current Pica state
-struct State {
- /// Pica registers
- Regs regs;
-
- /// Vertex shader memory
- struct ShaderSetup {
- struct {
- // The float uniforms are accessed by the shader JIT using SSE instructions, and are
- // therefore required to be 16-byte aligned.
- Math::Vec4<float24> MEMORY_ALIGNED16(f[96]);
-
- std::array<bool, 16> b;
- std::array<Math::Vec4<u8>, 4> i;
- } uniforms;
-
- Math::Vec4<float24> default_attributes[16];
-
- std::array<u32, 1024> program_code;
- std::array<u32, 1024> swizzle_data;
- };
-
- ShaderSetup vs;
- ShaderSetup gs;
-
- /// Current Pica command list
- struct {
- const u32* head_ptr;
- const u32* current_ptr;
- u32 length;
- } cmd_list;
-};
-
/// Initialize Pica state
void Init();
/// Shutdown Pica state
void Shutdown();
-extern State g_state; ///< Current Pica state
-
} // namespace
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
new file mode 100644
index 000000000..c7616bc55
--- /dev/null
+++ b/src/video_core/pica_state.h
@@ -0,0 +1,60 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "video_core/pica.h"
+#include "video_core/primitive_assembly.h"
+#include "video_core/shader/shader.h"
+
+namespace Pica {
+
+/// Struct used to describe current Pica state
+struct State {
+ /// Pica registers
+ Regs regs;
+
+ Shader::ShaderSetup vs;
+ Shader::ShaderSetup gs;
+
+ struct {
+ union LutEntry {
+ // Used for raw access
+ u32 raw;
+
+ // LUT value, encoded as 12-bit fixed point, with 12 fraction bits
+ BitField< 0, 12, u32> value;
+
+ // Used by HW for efficient interpolation, Citra does not use these
+ BitField<12, 12, u32> difference;
+
+ float ToFloat() {
+ return static_cast<float>(value) / 4095.f;
+ }
+ };
+
+ std::array<std::array<LutEntry, 256>, 24> luts;
+ } lighting;
+
+ /// Current Pica command list
+ struct {
+ const u32* head_ptr;
+ const u32* current_ptr;
+ u32 length;
+ } cmd_list;
+
+ /// Struct used to describe immediate mode rendering state
+ struct ImmediateModeState {
+ Shader::InputVertex input;
+ // This is constructed with a dummy triangle topology
+ PrimitiveAssembler<Shader::OutputVertex> primitive_assembler;
+ int attribute_id = 0;
+
+ ImmediateModeState() : primitive_assembler(Regs::TriangleTopology::List) {}
+ } immediate;
+};
+
+extern State g_state; ///< Current Pica state
+
+} // namespace
diff --git a/src/video_core/pica_types.h b/src/video_core/pica_types.h
new file mode 100644
index 000000000..ecf45654b
--- /dev/null
+++ b/src/video_core/pica_types.h
@@ -0,0 +1,146 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstring>
+
+#include "common/common_types.h"
+
+namespace Pica {
+
+/**
+ * Template class for converting arbitrary Pica float types to IEEE 754 32-bit single-precision
+ * floating point.
+ *
+ * When decoding, format is as follows:
+ * - The first `M` bits are the mantissa
+ * - The next `E` bits are the exponent
+ * - The last bit is the sign bit
+ *
+ * @todo Verify on HW if this conversion is sufficiently accurate.
+ */
+template<unsigned M, unsigned E>
+struct Float {
+public:
+ static Float<M, E> FromFloat32(float val) {
+ Float<M, E> ret;
+ ret.value = val;
+ return ret;
+ }
+
+ static Float<M, E> FromRaw(u32 hex) {
+ Float<M, E> res;
+
+ const int width = M + E + 1;
+ const int bias = 128 - (1 << (E - 1));
+ const int exponent = (hex >> M) & ((1 << E) - 1);
+ const unsigned mantissa = hex & ((1 << M) - 1);
+
+ if (hex & ((1 << (width - 1)) - 1))
+ hex = ((hex >> (E + M)) << 31) | (mantissa << (23 - M)) | ((exponent + bias) << 23);
+ else
+ hex = ((hex >> (E + M)) << 31);
+
+ std::memcpy(&res.value, &hex, sizeof(float));
+
+ return res;
+ }
+
+ static Float<M, E> Zero() {
+ return FromFloat32(0.f);
+ }
+
+ // Not recommended for anything but logging
+ float ToFloat32() const {
+ return value;
+ }
+
+ Float<M, E> operator * (const Float<M, E>& flt) const {
+ if ((this->value == 0.f && !std::isnan(flt.value)) ||
+ (flt.value == 0.f && !std::isnan(this->value)))
+ // PICA gives 0 instead of NaN when multiplying by inf
+ return Zero();
+ return Float<M, E>::FromFloat32(ToFloat32() * flt.ToFloat32());
+ }
+
+ Float<M, E> operator / (const Float<M, E>& flt) const {
+ return Float<M, E>::FromFloat32(ToFloat32() / flt.ToFloat32());
+ }
+
+ Float<M, E> operator + (const Float<M, E>& flt) const {
+ return Float<M, E>::FromFloat32(ToFloat32() + flt.ToFloat32());
+ }
+
+ Float<M, E> operator - (const Float<M, E>& flt) const {
+ return Float<M, E>::FromFloat32(ToFloat32() - flt.ToFloat32());
+ }
+
+ Float<M, E>& operator *= (const Float<M, E>& flt) {
+ if ((this->value == 0.f && !std::isnan(flt.value)) ||
+ (flt.value == 0.f && !std::isnan(this->value)))
+ // PICA gives 0 instead of NaN when multiplying by inf
+ *this = Zero();
+ else value *= flt.ToFloat32();
+ return *this;
+ }
+
+ Float<M, E>& operator /= (const Float<M, E>& flt) {
+ value /= flt.ToFloat32();
+ return *this;
+ }
+
+ Float<M, E>& operator += (const Float<M, E>& flt) {
+ value += flt.ToFloat32();
+ return *this;
+ }
+
+ Float<M, E>& operator -= (const Float<M, E>& flt) {
+ value -= flt.ToFloat32();
+ return *this;
+ }
+
+ Float<M, E> operator - () const {
+ return Float<M, E>::FromFloat32(-ToFloat32());
+ }
+
+ bool operator < (const Float<M, E>& flt) const {
+ return ToFloat32() < flt.ToFloat32();
+ }
+
+ bool operator > (const Float<M, E>& flt) const {
+ return ToFloat32() > flt.ToFloat32();
+ }
+
+ bool operator >= (const Float<M, E>& flt) const {
+ return ToFloat32() >= flt.ToFloat32();
+ }
+
+ bool operator <= (const Float<M, E>& flt) const {
+ return ToFloat32() <= flt.ToFloat32();
+ }
+
+ bool operator == (const Float<M, E>& flt) const {
+ return ToFloat32() == flt.ToFloat32();
+ }
+
+ bool operator != (const Float<M, E>& flt) const {
+ return ToFloat32() != flt.ToFloat32();
+ }
+
+private:
+ static const unsigned MASK = (1 << (M + E + 1)) - 1;
+ static const unsigned MANTISSA_MASK = (1 << M) - 1;
+ static const unsigned EXPONENT_MASK = (1 << E) - 1;
+
+ // Stored as a regular float, merely for convenience
+ // TODO: Perform proper arithmetic on this!
+ float value;
+};
+
+using float24 = Float<16, 7>;
+using float20 = Float<12, 7>;
+using float16 = Float<10, 5>;
+
+} // namespace Pica
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp
index 44a8dbfe9..0061690f1 100644
--- a/src/video_core/primitive_assembly.cpp
+++ b/src/video_core/primitive_assembly.cpp
@@ -39,13 +39,12 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl
buffer[buffer_index] = vtx;
- if (topology == Regs::TriangleTopology::Strip) {
- strip_ready |= (buffer_index == 1);
+ strip_ready |= (buffer_index == 1);
+
+ if (topology == Regs::TriangleTopology::Strip)
buffer_index = !buffer_index;
- } else if (topology == Regs::TriangleTopology::Fan) {
+ else if (topology == Regs::TriangleTopology::Fan)
buffer_index = 1;
- strip_ready = true;
- }
break;
default:
@@ -54,6 +53,18 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx, TriangleHandl
}
}
+template<typename VertexType>
+void PrimitiveAssembler<VertexType>::Reset() {
+ buffer_index = 0;
+ strip_ready = false;
+}
+
+template<typename VertexType>
+void PrimitiveAssembler<VertexType>::Reconfigure(Regs::TriangleTopology topology) {
+ Reset();
+ this->topology = topology;
+}
+
// explicitly instantiate use cases
template
struct PrimitiveAssembler<Shader::OutputVertex>;
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h
index 52d0ec8ff..cc6e5fde5 100644
--- a/src/video_core/primitive_assembly.h
+++ b/src/video_core/primitive_assembly.h
@@ -30,6 +30,16 @@ struct PrimitiveAssembler {
*/
void SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler);
+ /**
+ * Resets the internal state of the PrimitiveAssembler.
+ */
+ void Reset();
+
+ /**
+ * Reconfigures the PrimitiveAssembler to use a different triangle topology.
+ */
+ void Reconfigure(Regs::TriangleTopology topology);
+
private:
Regs::TriangleTopology topology;
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index ecfdbc9e8..fd02aa652 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -15,6 +15,7 @@
#include "core/hw/gpu.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/rasterizer.h"
#include "video_core/utils.h"
#include "video_core/debug_utils/debug_utils.h"
@@ -857,12 +858,12 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
}
}
- // TODO: Does depth indeed only get written even if depth testing is enabled?
+ unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format);
+ u32 z = (u32)((v0.screenpos[2].ToFloat32() * w0 +
+ v1.screenpos[2].ToFloat32() * w1 +
+ v2.screenpos[2].ToFloat32() * w2) * ((1 << num_bits) - 1) / wsum);
+
if (output_merger.depth_test_enable) {
- unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format);
- u32 z = (u32)((v0.screenpos[2].ToFloat32() * w0 +
- v1.screenpos[2].ToFloat32() * w1 +
- v2.screenpos[2].ToFloat32() * w2) * ((1 << num_bits) - 1) / wsum);
u32 ref_z = GetDepth(x >> 4, y >> 4);
bool pass = false;
@@ -906,11 +907,11 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0,
UpdateStencil(stencil_test.action_depth_fail);
continue;
}
-
- if (output_merger.depth_write_enable)
- SetDepth(x >> 4, y >> 4, z);
}
+ if (output_merger.depth_write_enable)
+ SetDepth(x >> 4, y >> 4, z);
+
// The stencil depth_pass action is executed even if depth testing is disabled
if (stencil_action_enable)
UpdateStencil(stencil_test.action_depth_pass);
diff --git a/src/video_core/hwrasterizer_base.h b/src/video_core/rasterizer_interface.h
index 54b8892fb..008c5827b 100644
--- a/src/video_core/hwrasterizer_base.h
+++ b/src/video_core/rasterizer_interface.h
@@ -12,10 +12,11 @@ struct OutputVertex;
}
}
-class HWRasterizer {
+namespace VideoCore {
+
+class RasterizerInterface {
public:
- virtual ~HWRasterizer() {
- }
+ virtual ~RasterizerInterface() {}
/// Initialize API-specific GPU objects
virtual void InitObjects() = 0;
@@ -32,14 +33,16 @@ public:
virtual void DrawTriangles() = 0;
/// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
- virtual void CommitFramebuffer() = 0;
+ virtual void FlushFramebuffer() = 0;
/// Notify rasterizer that the specified PICA register has been changed
virtual void NotifyPicaRegisterChanged(u32 id) = 0;
- /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
- virtual void NotifyPreRead(PAddr addr, u32 size) = 0;
+ /// Notify rasterizer that any caches of the specified region should be flushed to 3DS memory.
+ virtual void FlushRegion(PAddr addr, u32 size) = 0;
- /// Notify rasterizer that a 3DS memory region has been changed
- virtual void NotifyFlush(PAddr addr, u32 size) = 0;
+ /// Notify rasterizer that any caches of the specified region should be discraded and reloaded from 3DS memory.
+ virtual void InvalidateRegion(PAddr addr, u32 size) = 0;
};
+
+}
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
new file mode 100644
index 000000000..6467ff723
--- /dev/null
+++ b/src/video_core/renderer_base.cpp
@@ -0,0 +1,29 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <memory>
+
+#include "common/make_unique.h"
+
+#include "core/settings.h"
+
+#include "video_core/renderer_base.h"
+#include "video_core/video_core.h"
+#include "video_core/swrasterizer.h"
+#include "video_core/renderer_opengl/gl_rasterizer.h"
+
+void RendererBase::RefreshRasterizerSetting() {
+ bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled;
+ if (rasterizer == nullptr || opengl_rasterizer_active != hw_renderer_enabled) {
+ opengl_rasterizer_active = hw_renderer_enabled;
+
+ if (hw_renderer_enabled) {
+ rasterizer = Common::make_unique<RasterizerOpenGL>();
+ } else {
+ rasterizer = Common::make_unique<VideoCore::SWRasterizer>();
+ }
+ rasterizer->InitObjects();
+ rasterizer->Reset();
+ }
+}
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 6587bcf27..f68091cc8 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -8,7 +8,7 @@
#include "common/common_types.h"
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/rasterizer_interface.h"
class EmuWindow;
@@ -22,9 +22,6 @@ public:
kFramebuffer_Texture
};
- RendererBase() : m_current_fps(0), m_current_frame(0) {
- }
-
virtual ~RendererBase() {
}
@@ -38,7 +35,7 @@ public:
virtual void SetWindow(EmuWindow* window) = 0;
/// Initialize the renderer
- virtual void Init() = 0;
+ virtual bool Init() = 0;
/// Shutdown the renderer
virtual void ShutDown() = 0;
@@ -46,18 +43,25 @@ public:
// Getter/setter functions:
// ------------------------
- f32 GetCurrentframe() const {
+ f32 GetCurrentFPS() const {
return m_current_fps;
}
- int current_frame() const {
+ int GetCurrentFrame() const {
return m_current_frame;
}
- std::unique_ptr<HWRasterizer> hw_rasterizer;
+ VideoCore::RasterizerInterface* Rasterizer() const {
+ return rasterizer.get();
+ }
+
+ void RefreshRasterizerSetting();
protected:
- f32 m_current_fps; ///< Current framerate, should be set by the renderer
- int m_current_frame; ///< Current frame, should be set by the renderer
+ std::unique_ptr<VideoCore::RasterizerInterface> rasterizer;
+ f32 m_current_fps = 0.0f; ///< Current framerate, should be set by the renderer
+ int m_current_frame = 0; ///< Current frame, should be set by the renderer
+private:
+ bool opengl_rasterizer_active = false;
};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 822739088..b3dc6aa19 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -19,6 +19,7 @@
#include "core/hw/gpu.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/utils.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
@@ -36,7 +37,7 @@ static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) {
stage.GetAlphaMultiplier() == 1);
}
-RasterizerOpenGL::RasterizerOpenGL() : last_fb_color_addr(0), last_fb_depth_addr(0) { }
+RasterizerOpenGL::RasterizerOpenGL() : cached_fb_color_addr(0), cached_fb_depth_addr(0) { }
RasterizerOpenGL::~RasterizerOpenGL() { }
void RasterizerOpenGL::InitObjects() {
@@ -75,6 +76,12 @@ void RasterizerOpenGL::InitObjects() {
glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1);
glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2);
+ glVertexAttribPointer(GLShader::ATTRIBUTE_NORMQUAT, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, normquat));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_NORMQUAT);
+
+ glVertexAttribPointer(GLShader::ATTRIBUTE_VIEW, 3, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, view));
+ glEnableVertexAttribArray(GLShader::ATTRIBUTE_VIEW);
+
SetShader();
// Create textures for OGL framebuffer that will be rendered to, initially 1x1 to succeed in framebuffer creation
@@ -120,12 +127,26 @@ void RasterizerOpenGL::InitObjects() {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_color_texture.texture.handle, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, fb_depth_texture.texture.handle, 0);
+ for (size_t i = 0; i < lighting_lut.size(); ++i) {
+ lighting_lut[i].Create();
+ state.lighting_lut[i].texture_1d = lighting_lut[i].handle;
+
+ glActiveTexture(GL_TEXTURE3 + i);
+ glBindTexture(GL_TEXTURE_1D, state.lighting_lut[i].texture_1d);
+
+ glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA32F, 256, 0, GL_RGBA, GL_FLOAT, nullptr);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+ state.Apply();
+
ASSERT_MSG(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE,
"OpenGL rasterizer framebuffer setup failed, status %X", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
void RasterizerOpenGL::Reset() {
SyncCullMode();
+ SyncDepthModifiers();
SyncBlendEnabled();
SyncBlendFuncs();
SyncBlendColor();
@@ -135,15 +156,37 @@ void RasterizerOpenGL::Reset() {
SetShader();
- res_cache.FullFlush();
+ res_cache.InvalidateAll();
+}
+
+/**
+ * This is a helper function to resolve an issue with opposite quaternions being interpolated by
+ * OpenGL. See below for a detailed description of this issue (yuriks):
+ *
+ * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you
+ * interpolate two quaternions that are opposite, instead of going from one rotation to another
+ * using the shortest path, you'll go around the longest path. You can test if two quaternions are
+ * opposite by checking if Dot(Q1, W2) < 0. In that case, you can flip either of them, therefore
+ * making Dot(-Q1, W2) positive.
+ *
+ * NOTE: This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This
+ * should be correct for nearly all cases, however a more correct implementation (but less trivial
+ * and perhaps unnecessary) would be to handle this per-fragment, by interpolating the quaternions
+ * manually using two Lerps, and doing this correction before each Lerp.
+ */
+static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) {
+ Math::Vec4f a{ qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32() };
+ Math::Vec4f b{ qb.x.ToFloat32(), qb.y.ToFloat32(), qb.z.ToFloat32(), qb.w.ToFloat32() };
+
+ return (Math::Dot(a, b) < 0.f);
}
void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0,
const Pica::Shader::OutputVertex& v1,
const Pica::Shader::OutputVertex& v2) {
- vertex_batch.emplace_back(v0);
- vertex_batch.emplace_back(v1);
- vertex_batch.emplace_back(v2);
+ vertex_batch.emplace_back(v0, false);
+ vertex_batch.emplace_back(v1, AreQuaternionsOpposite(v0.quat, v1.quat));
+ vertex_batch.emplace_back(v2, AreQuaternionsOpposite(v0.quat, v2.quat));
}
void RasterizerOpenGL::DrawTriangles() {
@@ -155,6 +198,13 @@ void RasterizerOpenGL::DrawTriangles() {
state.draw.shader_dirty = false;
}
+ for (unsigned index = 0; index < lighting_lut.size(); index++) {
+ if (uniform_block_data.lut_dirty[index]) {
+ SyncLightingLUT(index);
+ uniform_block_data.lut_dirty[index] = false;
+ }
+ }
+
if (uniform_block_data.dirty) {
glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, GL_STATIC_DRAW);
uniform_block_data.dirty = false;
@@ -168,19 +218,17 @@ void RasterizerOpenGL::DrawTriangles() {
// Flush the resource cache at the current depth and color framebuffer addresses for render-to-texture
const auto& regs = Pica::g_state.regs;
- PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
- u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
- * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+ u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format)
+ * fb_color_texture.width * fb_color_texture.height;
- PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
- u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format)
- * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+ u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format)
+ * fb_depth_texture.width * fb_depth_texture.height;
- res_cache.NotifyFlush(cur_fb_color_addr, cur_fb_color_size, true);
- res_cache.NotifyFlush(cur_fb_depth_addr, cur_fb_depth_size, true);
+ res_cache.InvalidateInRange(cached_fb_color_addr, cached_fb_color_size, true);
+ res_cache.InvalidateInRange(cached_fb_depth_addr, cached_fb_depth_size, true);
}
-void RasterizerOpenGL::CommitFramebuffer() {
+void RasterizerOpenGL::FlushFramebuffer() {
CommitColorBuffer();
CommitDepthBuffer();
}
@@ -188,15 +236,18 @@ void RasterizerOpenGL::CommitFramebuffer() {
void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
const auto& regs = Pica::g_state.regs;
- if (!Settings::values.use_hw_renderer)
- return;
-
switch(id) {
// Culling
case PICA_REG_INDEX(cull_mode):
SyncCullMode();
break;
+ // Depth modifiers
+ case PICA_REG_INDEX(viewport_depth_range):
+ case PICA_REG_INDEX(viewport_depth_far_plane):
+ SyncDepthModifiers();
+ break;
+
// Blending
case PICA_REG_INDEX(output_merger.alphablend_enable):
SyncBlendEnabled();
@@ -281,54 +332,203 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
case PICA_REG_INDEX(tev_combiner_buffer_color):
SyncCombinerColor();
break;
+
+ // Fragment lighting specular 0 color
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_0, 0x140 + 0 * 0x10):
+ SyncLightSpecular0(0);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_0, 0x140 + 1 * 0x10):
+ SyncLightSpecular0(1);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_0, 0x140 + 2 * 0x10):
+ SyncLightSpecular0(2);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_0, 0x140 + 3 * 0x10):
+ SyncLightSpecular0(3);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_0, 0x140 + 4 * 0x10):
+ SyncLightSpecular0(4);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_0, 0x140 + 5 * 0x10):
+ SyncLightSpecular0(5);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_0, 0x140 + 6 * 0x10):
+ SyncLightSpecular0(6);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_0, 0x140 + 7 * 0x10):
+ SyncLightSpecular0(7);
+ break;
+
+ // Fragment lighting specular 1 color
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_1, 0x141 + 0 * 0x10):
+ SyncLightSpecular1(0);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_1, 0x141 + 1 * 0x10):
+ SyncLightSpecular1(1);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_1, 0x141 + 2 * 0x10):
+ SyncLightSpecular1(2);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_1, 0x141 + 3 * 0x10):
+ SyncLightSpecular1(3);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_1, 0x141 + 4 * 0x10):
+ SyncLightSpecular1(4);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_1, 0x141 + 5 * 0x10):
+ SyncLightSpecular1(5);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_1, 0x141 + 6 * 0x10):
+ SyncLightSpecular1(6);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_1, 0x141 + 7 * 0x10):
+ SyncLightSpecular1(7);
+ break;
+
+ // Fragment lighting diffuse color
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].diffuse, 0x142 + 0 * 0x10):
+ SyncLightDiffuse(0);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].diffuse, 0x142 + 1 * 0x10):
+ SyncLightDiffuse(1);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].diffuse, 0x142 + 2 * 0x10):
+ SyncLightDiffuse(2);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].diffuse, 0x142 + 3 * 0x10):
+ SyncLightDiffuse(3);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].diffuse, 0x142 + 4 * 0x10):
+ SyncLightDiffuse(4);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].diffuse, 0x142 + 5 * 0x10):
+ SyncLightDiffuse(5);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].diffuse, 0x142 + 6 * 0x10):
+ SyncLightDiffuse(6);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].diffuse, 0x142 + 7 * 0x10):
+ SyncLightDiffuse(7);
+ break;
+
+ // Fragment lighting ambient color
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].ambient, 0x143 + 0 * 0x10):
+ SyncLightAmbient(0);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].ambient, 0x143 + 1 * 0x10):
+ SyncLightAmbient(1);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].ambient, 0x143 + 2 * 0x10):
+ SyncLightAmbient(2);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].ambient, 0x143 + 3 * 0x10):
+ SyncLightAmbient(3);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].ambient, 0x143 + 4 * 0x10):
+ SyncLightAmbient(4);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].ambient, 0x143 + 5 * 0x10):
+ SyncLightAmbient(5);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].ambient, 0x143 + 6 * 0x10):
+ SyncLightAmbient(6);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].ambient, 0x143 + 7 * 0x10):
+ SyncLightAmbient(7);
+ break;
+
+ // Fragment lighting position
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].x, 0x144 + 0 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[0].z, 0x145 + 0 * 0x10):
+ SyncLightPosition(0);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].x, 0x144 + 1 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[1].z, 0x145 + 1 * 0x10):
+ SyncLightPosition(1);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].x, 0x144 + 2 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[2].z, 0x145 + 2 * 0x10):
+ SyncLightPosition(2);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].x, 0x144 + 3 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[3].z, 0x145 + 3 * 0x10):
+ SyncLightPosition(3);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].x, 0x144 + 4 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[4].z, 0x145 + 4 * 0x10):
+ SyncLightPosition(4);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].x, 0x144 + 5 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[5].z, 0x145 + 5 * 0x10):
+ SyncLightPosition(5);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].x, 0x144 + 6 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[6].z, 0x145 + 6 * 0x10):
+ SyncLightPosition(6);
+ break;
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].x, 0x144 + 7 * 0x10):
+ case PICA_REG_INDEX_WORKAROUND(lighting.light[7].z, 0x145 + 7 * 0x10):
+ SyncLightPosition(7);
+ break;
+
+ // Fragment lighting global ambient color (emission + ambient * ambient)
+ case PICA_REG_INDEX_WORKAROUND(lighting.global_ambient, 0x1c0):
+ SyncGlobalAmbient();
+ break;
+
+ // Fragment lighting lookup tables
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[0], 0x1c8):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[1], 0x1c9):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[2], 0x1ca):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[3], 0x1cb):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[4], 0x1cc):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[5], 0x1cd):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce):
+ case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf):
+ {
+ auto& lut_config = regs.lighting.lut_config;
+ uniform_block_data.lut_dirty[lut_config.type / 4] = true;
+ break;
+ }
+
}
}
-void RasterizerOpenGL::NotifyPreRead(PAddr addr, u32 size) {
+void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) {
const auto& regs = Pica::g_state.regs;
- if (!Settings::values.use_hw_renderer)
- return;
+ u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format)
+ * fb_color_texture.width * fb_color_texture.height;
- PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
- u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
- * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
-
- PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
- u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format)
- * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+ u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format)
+ * fb_depth_texture.width * fb_depth_texture.height;
// If source memory region overlaps 3DS framebuffers, commit them before the copy happens
- if (MathUtil::IntervalsIntersect(addr, size, cur_fb_color_addr, cur_fb_color_size))
+ if (MathUtil::IntervalsIntersect(addr, size, cached_fb_color_addr, cached_fb_color_size))
CommitColorBuffer();
- if (MathUtil::IntervalsIntersect(addr, size, cur_fb_depth_addr, cur_fb_depth_size))
+ if (MathUtil::IntervalsIntersect(addr, size, cached_fb_depth_addr, cached_fb_depth_size))
CommitDepthBuffer();
}
-void RasterizerOpenGL::NotifyFlush(PAddr addr, u32 size) {
+void RasterizerOpenGL::InvalidateRegion(PAddr addr, u32 size) {
const auto& regs = Pica::g_state.regs;
- if (!Settings::values.use_hw_renderer)
- return;
-
- PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
- u32 cur_fb_color_size = Pica::Regs::BytesPerColorPixel(regs.framebuffer.color_format)
- * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+ u32 cached_fb_color_size = Pica::Regs::BytesPerColorPixel(fb_color_texture.format)
+ * fb_color_texture.width * fb_color_texture.height;
- PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
- u32 cur_fb_depth_size = Pica::Regs::BytesPerDepthPixel(regs.framebuffer.depth_format)
- * regs.framebuffer.GetWidth() * regs.framebuffer.GetHeight();
+ u32 cached_fb_depth_size = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format)
+ * fb_depth_texture.width * fb_depth_texture.height;
// If modified memory region overlaps 3DS framebuffers, reload their contents into OpenGL
- if (MathUtil::IntervalsIntersect(addr, size, cur_fb_color_addr, cur_fb_color_size))
+ if (MathUtil::IntervalsIntersect(addr, size, cached_fb_color_addr, cached_fb_color_size))
ReloadColorBuffer();
- if (MathUtil::IntervalsIntersect(addr, size, cur_fb_depth_addr, cur_fb_depth_size))
+ if (MathUtil::IntervalsIntersect(addr, size, cached_fb_depth_addr, cached_fb_depth_size))
ReloadDepthBuffer();
// Notify cache of flush in case the region touches a cached resource
- res_cache.NotifyFlush(addr, size);
+ res_cache.InvalidateInRange(addr, size);
}
void RasterizerOpenGL::SamplerInfo::Create() {
@@ -499,27 +699,48 @@ void RasterizerOpenGL::SetShader() {
uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]");
if (uniform_tex != -1) { glUniform1i(uniform_tex, 2); }
+ // Set the texture samplers to correspond to different lookup table texture units
+ GLuint uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[0]");
+ if (uniform_lut != -1) { glUniform1i(uniform_lut, 3); }
+ uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[1]");
+ if (uniform_lut != -1) { glUniform1i(uniform_lut, 4); }
+ uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[2]");
+ if (uniform_lut != -1) { glUniform1i(uniform_lut, 5); }
+ uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[3]");
+ if (uniform_lut != -1) { glUniform1i(uniform_lut, 6); }
+ uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[4]");
+ if (uniform_lut != -1) { glUniform1i(uniform_lut, 7); }
+ uniform_lut = glGetUniformLocation(shader->shader.handle, "lut[5]");
+ if (uniform_lut != -1) { glUniform1i(uniform_lut, 8); }
+
current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
unsigned int block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
- }
- // Update uniforms
- SyncAlphaTest();
- SyncCombinerColor();
- auto& tev_stages = Pica::g_state.regs.GetTevStages();
- for (int index = 0; index < tev_stages.size(); ++index)
- SyncTevConstColor(index, tev_stages[index]);
+ // Update uniforms
+ SyncAlphaTest();
+ SyncCombinerColor();
+ auto& tev_stages = Pica::g_state.regs.GetTevStages();
+ for (int index = 0; index < tev_stages.size(); ++index)
+ SyncTevConstColor(index, tev_stages[index]);
+
+ SyncGlobalAmbient();
+ for (int light_index = 0; light_index < 8; light_index++) {
+ SyncLightDiffuse(light_index);
+ SyncLightAmbient(light_index);
+ SyncLightPosition(light_index);
+ }
+ }
}
void RasterizerOpenGL::SyncFramebuffer() {
const auto& regs = Pica::g_state.regs;
- PAddr cur_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
+ PAddr new_fb_color_addr = regs.framebuffer.GetColorBufferPhysicalAddress();
Pica::Regs::ColorFormat new_fb_color_format = regs.framebuffer.color_format;
- PAddr cur_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
+ PAddr new_fb_depth_addr = regs.framebuffer.GetDepthBufferPhysicalAddress();
Pica::Regs::DepthFormat new_fb_depth_format = regs.framebuffer.depth_format;
bool fb_size_changed = fb_color_texture.width != static_cast<GLsizei>(regs.framebuffer.GetWidth()) ||
@@ -531,10 +752,10 @@ void RasterizerOpenGL::SyncFramebuffer() {
bool depth_fb_prop_changed = fb_depth_texture.format != new_fb_depth_format ||
fb_size_changed;
- bool color_fb_modified = last_fb_color_addr != cur_fb_color_addr ||
+ bool color_fb_modified = cached_fb_color_addr != new_fb_color_addr ||
color_fb_prop_changed;
- bool depth_fb_modified = last_fb_depth_addr != cur_fb_depth_addr ||
+ bool depth_fb_modified = cached_fb_depth_addr != new_fb_depth_addr ||
depth_fb_prop_changed;
// Commit if framebuffer modified in any way
@@ -574,13 +795,13 @@ void RasterizerOpenGL::SyncFramebuffer() {
// Load buffer data again if fb modified in any way
if (color_fb_modified) {
- last_fb_color_addr = cur_fb_color_addr;
+ cached_fb_color_addr = new_fb_color_addr;
ReloadColorBuffer();
}
if (depth_fb_modified) {
- last_fb_depth_addr = cur_fb_depth_addr;
+ cached_fb_depth_addr = new_fb_depth_addr;
ReloadDepthBuffer();
}
@@ -596,12 +817,12 @@ void RasterizerOpenGL::SyncCullMode() {
case Pica::Regs::CullMode::KeepClockWise:
state.cull.enabled = true;
- state.cull.mode = GL_BACK;
+ state.cull.front_face = GL_CW;
break;
case Pica::Regs::CullMode::KeepCounterClockWise:
state.cull.enabled = true;
- state.cull.mode = GL_FRONT;
+ state.cull.front_face = GL_CCW;
break;
default:
@@ -611,6 +832,15 @@ void RasterizerOpenGL::SyncCullMode() {
}
}
+void RasterizerOpenGL::SyncDepthModifiers() {
+ float depth_scale = -Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32();
+ float depth_offset = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_far_plane).ToFloat32() / 2.0f;
+
+ // TODO: Implement scale modifier
+ uniform_block_data.data.depth_offset = depth_offset;
+ uniform_block_data.dirty = true;
+}
+
void RasterizerOpenGL::SyncBlendEnabled() {
state.blend.enabled = (Pica::g_state.regs.output_merger.alphablend_enable == 1);
}
@@ -657,8 +887,10 @@ void RasterizerOpenGL::SyncStencilTest() {
void RasterizerOpenGL::SyncDepthTest() {
const auto& regs = Pica::g_state.regs;
- state.depth.test_enabled = (regs.output_merger.depth_test_enable == 1);
- state.depth.test_func = PicaToGL::CompareFunc(regs.output_merger.depth_test_func);
+ state.depth.test_enabled = regs.output_merger.depth_test_enable == 1 ||
+ regs.output_merger.depth_write_enable == 1;
+ state.depth.test_func = regs.output_merger.depth_test_enable == 1 ?
+ PicaToGL::CompareFunc(regs.output_merger.depth_test_func) : GL_ALWAYS;
state.color_mask.red_enabled = regs.output_merger.red_enable;
state.color_mask.green_enabled = regs.output_merger.green_enable;
state.color_mask.blue_enabled = regs.output_merger.blue_enable;
@@ -682,19 +914,87 @@ void RasterizerOpenGL::SyncTevConstColor(int stage_index, const Pica::Regs::TevS
}
}
+void RasterizerOpenGL::SyncGlobalAmbient() {
+ auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.global_ambient);
+ if (color != uniform_block_data.data.lighting_global_ambient) {
+ uniform_block_data.data.lighting_global_ambient = color;
+ uniform_block_data.dirty = true;
+ }
+}
+
+void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) {
+ std::array<GLvec4, 256> new_data;
+
+ for (unsigned offset = 0; offset < new_data.size(); ++offset) {
+ new_data[offset][0] = Pica::g_state.lighting.luts[(lut_index * 4) + 0][offset].ToFloat();
+ new_data[offset][1] = Pica::g_state.lighting.luts[(lut_index * 4) + 1][offset].ToFloat();
+ new_data[offset][2] = Pica::g_state.lighting.luts[(lut_index * 4) + 2][offset].ToFloat();
+ new_data[offset][3] = Pica::g_state.lighting.luts[(lut_index * 4) + 3][offset].ToFloat();
+ }
+
+ if (new_data != lighting_lut_data[lut_index]) {
+ lighting_lut_data[lut_index] = new_data;
+ glActiveTexture(GL_TEXTURE3 + lut_index);
+ glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RGBA, GL_FLOAT, lighting_lut_data[lut_index].data());
+ }
+}
+
+void RasterizerOpenGL::SyncLightSpecular0(int light_index) {
+ auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_0);
+ if (color != uniform_block_data.data.light_src[light_index].specular_0) {
+ uniform_block_data.data.light_src[light_index].specular_0 = color;
+ uniform_block_data.dirty = true;
+ }
+}
+
+void RasterizerOpenGL::SyncLightSpecular1(int light_index) {
+ auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_1);
+ if (color != uniform_block_data.data.light_src[light_index].specular_1) {
+ uniform_block_data.data.light_src[light_index].specular_1 = color;
+ uniform_block_data.dirty = true;
+ }
+}
+
+void RasterizerOpenGL::SyncLightDiffuse(int light_index) {
+ auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].diffuse);
+ if (color != uniform_block_data.data.light_src[light_index].diffuse) {
+ uniform_block_data.data.light_src[light_index].diffuse = color;
+ uniform_block_data.dirty = true;
+ }
+}
+
+void RasterizerOpenGL::SyncLightAmbient(int light_index) {
+ auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].ambient);
+ if (color != uniform_block_data.data.light_src[light_index].ambient) {
+ uniform_block_data.data.light_src[light_index].ambient = color;
+ uniform_block_data.dirty = true;
+ }
+}
+
+void RasterizerOpenGL::SyncLightPosition(int light_index) {
+ GLvec3 position = {
+ Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].x).ToFloat32(),
+ Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].y).ToFloat32(),
+ Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32() };
+
+ if (position != uniform_block_data.data.light_src[light_index].position) {
+ uniform_block_data.data.light_src[light_index].position = position;
+ uniform_block_data.dirty = true;
+ }
+}
+
void RasterizerOpenGL::SyncDrawState() {
const auto& regs = Pica::g_state.regs;
// Sync the viewport
- GLsizei viewport_width = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_x).ToFloat32() * 2;
- GLsizei viewport_height = (GLsizei)Pica::float24::FromRawFloat24(regs.viewport_size_y).ToFloat32() * 2;
+ GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2;
+ GLsizei viewport_height = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_y).ToFloat32() * 2;
// OpenGL uses different y coordinates, so negate corner offset and flip origin
// TODO: Ensure viewport_corner.x should not be negated or origin flipped
// TODO: Use floating-point viewports for accuracy if supported
- glViewport((GLsizei)static_cast<float>(regs.viewport_corner.x),
- -(GLsizei)static_cast<float>(regs.viewport_corner.y)
- + regs.framebuffer.GetHeight() - viewport_height,
+ glViewport((GLsizei)regs.viewport_corner.x,
+ (GLsizei)regs.viewport_corner.y,
viewport_width, viewport_height);
// Sync bound texture(s), upload if not cached
@@ -717,7 +1017,7 @@ void RasterizerOpenGL::SyncDrawState() {
MICROPROFILE_DEFINE(OpenGL_FramebufferReload, "OpenGL", "FB Reload", MP_RGB(70, 70, 200));
void RasterizerOpenGL::ReloadColorBuffer() {
- u8* color_buffer = Memory::GetPhysicalPointer(Pica::g_state.regs.framebuffer.GetColorBufferPhysicalAddress());
+ u8* color_buffer = Memory::GetPhysicalPointer(cached_fb_color_addr);
if (color_buffer == nullptr)
return;
@@ -733,7 +1033,7 @@ void RasterizerOpenGL::ReloadColorBuffer() {
for (int x = 0; x < fb_color_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_color_texture.width) * bytes_per_pixel;
+ u32 gl_pixel_index = (x + (fb_color_texture.height - 1 - y) * fb_color_texture.width) * bytes_per_pixel;
u8* pixel = color_buffer + dst_offset;
memcpy(&temp_fb_color_buffer[gl_pixel_index], pixel, bytes_per_pixel);
@@ -752,13 +1052,11 @@ void RasterizerOpenGL::ReloadColorBuffer() {
}
void RasterizerOpenGL::ReloadDepthBuffer() {
- PAddr depth_buffer_addr = Pica::g_state.regs.framebuffer.GetDepthBufferPhysicalAddress();
-
- if (depth_buffer_addr == 0)
+ if (cached_fb_depth_addr == 0)
return;
// TODO: Appears to work, but double-check endianness of depth values and order of depth-stencil
- u8* depth_buffer = Memory::GetPhysicalPointer(depth_buffer_addr);
+ u8* depth_buffer = Memory::GetPhysicalPointer(cached_fb_depth_addr);
if (depth_buffer == nullptr)
return;
@@ -779,7 +1077,7 @@ void RasterizerOpenGL::ReloadDepthBuffer() {
for (int x = 0; x < fb_depth_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_depth_texture.width);
+ u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width);
u8* pixel = depth_buffer + dst_offset;
u32 depth_stencil = *(u32*)pixel;
@@ -791,7 +1089,7 @@ void RasterizerOpenGL::ReloadDepthBuffer() {
for (int x = 0; x < fb_depth_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp;
+ u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width) * gl_bpp;
u8* pixel = depth_buffer + dst_offset;
memcpy(&temp_fb_depth_data[gl_pixel_index], pixel, bytes_per_pixel);
@@ -821,8 +1119,8 @@ Common::Profiling::TimingCategory buffer_commit_category("Framebuffer Commit");
MICROPROFILE_DEFINE(OpenGL_FramebufferCommit, "OpenGL", "FB Commit", MP_RGB(70, 70, 200));
void RasterizerOpenGL::CommitColorBuffer() {
- if (last_fb_color_addr != 0) {
- u8* color_buffer = Memory::GetPhysicalPointer(last_fb_color_addr);
+ if (cached_fb_color_addr != 0) {
+ u8* color_buffer = Memory::GetPhysicalPointer(cached_fb_color_addr);
if (color_buffer != nullptr) {
Common::Profiling::ScopeTimer timer(buffer_commit_category);
@@ -846,7 +1144,7 @@ void RasterizerOpenGL::CommitColorBuffer() {
for (int x = 0; x < fb_color_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_color_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = x * bytes_per_pixel + y * fb_color_texture.width * bytes_per_pixel;
+ u32 gl_pixel_index = x * bytes_per_pixel + (fb_color_texture.height - 1 - y) * fb_color_texture.width * bytes_per_pixel;
u8* pixel = color_buffer + dst_offset;
memcpy(pixel, &temp_gl_color_buffer[gl_pixel_index], bytes_per_pixel);
@@ -857,9 +1155,9 @@ void RasterizerOpenGL::CommitColorBuffer() {
}
void RasterizerOpenGL::CommitDepthBuffer() {
- if (last_fb_depth_addr != 0) {
+ if (cached_fb_depth_addr != 0) {
// TODO: Output seems correct visually, but doesn't quite match sw renderer output. One of them is wrong.
- u8* depth_buffer = Memory::GetPhysicalPointer(last_fb_depth_addr);
+ u8* depth_buffer = Memory::GetPhysicalPointer(cached_fb_depth_addr);
if (depth_buffer != nullptr) {
Common::Profiling::ScopeTimer timer(buffer_commit_category);
@@ -888,7 +1186,7 @@ void RasterizerOpenGL::CommitDepthBuffer() {
for (int x = 0; x < fb_depth_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_depth_texture.width);
+ u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width);
u8* pixel = depth_buffer + dst_offset;
u32 depth_stencil = ((u32*)temp_gl_depth_data)[gl_pixel_index];
@@ -900,7 +1198,7 @@ void RasterizerOpenGL::CommitDepthBuffer() {
for (int x = 0; x < fb_depth_texture.width; ++x) {
const u32 coarse_y = y & ~7;
u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * fb_depth_texture.width * bytes_per_pixel;
- u32 gl_pixel_index = (x + y * fb_depth_texture.width) * gl_bpp;
+ u32 gl_pixel_index = (x + (fb_depth_texture.height - 1 - y) * fb_depth_texture.width) * gl_bpp;
u8* pixel = depth_buffer + dst_offset;
memcpy(pixel, &temp_gl_depth_data[gl_pixel_index], bytes_per_pixel);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 5ba898189..fc85aa3ff 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -14,9 +14,11 @@
#include "common/hash.h"
#include "video_core/pica.h"
-#include "video_core/hwrasterizer_base.h"
+#include "video_core/pica_state.h"
+#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
+#include "video_core/renderer_opengl/pica_to_gl.h"
#include "video_core/shader/shader_interpreter.h"
/**
@@ -71,6 +73,59 @@ struct PicaShaderConfig {
regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
regs.tev_combiner_buffer_input.update_mask_a.Value() << 4;
+ // Fragment lighting
+
+ res.lighting.enable = !regs.lighting.disable;
+ res.lighting.src_num = regs.lighting.num_lights + 1;
+
+ for (unsigned light_index = 0; light_index < res.lighting.src_num; ++light_index) {
+ unsigned num = regs.lighting.light_enable.GetNum(light_index);
+ const auto& light = regs.lighting.light[num];
+ res.lighting.light[light_index].num = num;
+ res.lighting.light[light_index].directional = light.directional != 0;
+ res.lighting.light[light_index].two_sided_diffuse = light.two_sided_diffuse != 0;
+ res.lighting.light[light_index].dist_atten_enable = !regs.lighting.IsDistAttenDisabled(num);
+ res.lighting.light[light_index].dist_atten_bias = Pica::float20::FromRaw(light.dist_atten_bias).ToFloat32();
+ res.lighting.light[light_index].dist_atten_scale = Pica::float20::FromRaw(light.dist_atten_scale).ToFloat32();
+ }
+
+ res.lighting.lut_d0.enable = regs.lighting.disable_lut_d0 == 0;
+ res.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0;
+ res.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value();
+ res.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0);
+
+ res.lighting.lut_d1.enable = regs.lighting.disable_lut_d1 == 0;
+ res.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0;
+ res.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value();
+ res.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1);
+
+ res.lighting.lut_fr.enable = regs.lighting.disable_lut_fr == 0;
+ res.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0;
+ res.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value();
+ res.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr);
+
+ res.lighting.lut_rr.enable = regs.lighting.disable_lut_rr == 0;
+ res.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0;
+ res.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value();
+ res.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr);
+
+ res.lighting.lut_rg.enable = regs.lighting.disable_lut_rg == 0;
+ res.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0;
+ res.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value();
+ res.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg);
+
+ res.lighting.lut_rb.enable = regs.lighting.disable_lut_rb == 0;
+ res.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0;
+ res.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value();
+ res.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb);
+
+ res.lighting.config = regs.lighting.config;
+ res.lighting.fresnel_selector = regs.lighting.fresnel_selector;
+ res.lighting.bump_mode = regs.lighting.bump_mode;
+ res.lighting.bump_selector = regs.lighting.bump_selector;
+ res.lighting.bump_renorm = regs.lighting.disable_bump_renorm == 0;
+ res.lighting.clamp_highlights = regs.lighting.clamp_highlights != 0;
+
return res;
}
@@ -86,9 +141,37 @@ struct PicaShaderConfig {
return std::memcmp(this, &o, sizeof(PicaShaderConfig)) == 0;
};
- Pica::Regs::CompareFunc alpha_test_func;
+ Pica::Regs::CompareFunc alpha_test_func = Pica::Regs::CompareFunc::Never;
std::array<Pica::Regs::TevStageConfig, 6> tev_stages = {};
- u8 combiner_buffer_input;
+ u8 combiner_buffer_input = 0;
+
+ struct {
+ struct {
+ unsigned num = 0;
+ bool directional = false;
+ bool two_sided_diffuse = false;
+ bool dist_atten_enable = false;
+ GLfloat dist_atten_scale = 0.0f;
+ GLfloat dist_atten_bias = 0.0f;
+ } light[8];
+
+ bool enable = false;
+ unsigned src_num = 0;
+ Pica::Regs::LightingBumpMode bump_mode = Pica::Regs::LightingBumpMode::None;
+ unsigned bump_selector = 0;
+ bool bump_renorm = false;
+ bool clamp_highlights = false;
+
+ Pica::Regs::LightingConfig config = Pica::Regs::LightingConfig::Config0;
+ Pica::Regs::LightingFresnelSelector fresnel_selector = Pica::Regs::LightingFresnelSelector::None;
+
+ struct {
+ bool enable = false;
+ bool abs_input = false;
+ Pica::Regs::LightingLutInput type = Pica::Regs::LightingLutInput::NH;
+ float scale = 1.0f;
+ } lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb;
+ } lighting;
};
namespace std {
@@ -102,37 +185,22 @@ struct hash<PicaShaderConfig> {
} // namespace std
-class RasterizerOpenGL : public HWRasterizer {
+class RasterizerOpenGL : public VideoCore::RasterizerInterface {
public:
RasterizerOpenGL();
~RasterizerOpenGL() override;
- /// Initialize API-specific GPU objects
void InitObjects() override;
-
- /// Reset the rasterizer, such as flushing all caches and updating all state
void Reset() override;
-
- /// Queues the primitive formed by the given vertices for rendering
void AddTriangle(const Pica::Shader::OutputVertex& v0,
const Pica::Shader::OutputVertex& v1,
const Pica::Shader::OutputVertex& v2) override;
-
- /// Draw the current batch of triangles
void DrawTriangles() override;
-
- /// Commit the rasterizer's framebuffer contents immediately to the current 3DS memory framebuffer
- void CommitFramebuffer() override;
-
- /// Notify rasterizer that the specified PICA register has been changed
+ void FlushFramebuffer() override;
void NotifyPicaRegisterChanged(u32 id) override;
-
- /// Notify rasterizer that the specified 3DS memory region will be read from after this notification
- void NotifyPreRead(PAddr addr, u32 size) override;
-
- /// Notify rasterizer that a 3DS memory region has been changed
- void NotifyFlush(PAddr addr, u32 size) override;
+ void FlushRegion(PAddr addr, u32 size) override;
+ void InvalidateRegion(PAddr addr, u32 size) override;
/// OpenGL shader generated for a given Pica register state
struct PicaShader {
@@ -182,7 +250,7 @@ private:
/// Structure that the hardware rendered vertices are composed of
struct HardwareVertex {
- HardwareVertex(const Pica::Shader::OutputVertex& v) {
+ HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) {
position[0] = v.pos.x.ToFloat32();
position[1] = v.pos.y.ToFloat32();
position[2] = v.pos.z.ToFloat32();
@@ -197,6 +265,19 @@ private:
tex_coord1[1] = v.tc1.y.ToFloat32();
tex_coord2[0] = v.tc2.x.ToFloat32();
tex_coord2[1] = v.tc2.y.ToFloat32();
+ normquat[0] = v.quat.x.ToFloat32();
+ normquat[1] = v.quat.y.ToFloat32();
+ normquat[2] = v.quat.z.ToFloat32();
+ normquat[3] = v.quat.w.ToFloat32();
+ view[0] = v.view.x.ToFloat32();
+ view[1] = v.view.y.ToFloat32();
+ view[2] = v.view.z.ToFloat32();
+
+ if (flip_quaternion) {
+ for (float& x : normquat) {
+ x = -x;
+ }
+ }
}
GLfloat position[4];
@@ -204,19 +285,31 @@ private:
GLfloat tex_coord0[2];
GLfloat tex_coord1[2];
GLfloat tex_coord2[2];
+ GLfloat normquat[4];
+ GLfloat view[3];
+ };
+
+ struct LightSrc {
+ alignas(16) GLvec3 specular_0;
+ alignas(16) GLvec3 specular_1;
+ alignas(16) GLvec3 diffuse;
+ alignas(16) GLvec3 ambient;
+ alignas(16) GLvec3 position;
};
/// Uniform structure for the Uniform Buffer Object, all members must be 16-byte aligned
struct UniformData {
// A vec4 color for each of the six tev stages
- std::array<GLfloat, 4> const_color[6];
- std::array<GLfloat, 4> tev_combiner_buffer_color;
+ GLvec4 const_color[6];
+ GLvec4 tev_combiner_buffer_color;
GLint alphatest_ref;
- INSERT_PADDING_BYTES(12);
+ GLfloat depth_offset;
+ alignas(16) GLvec3 lighting_global_ambient;
+ LightSrc light_src[8];
};
- static_assert(sizeof(UniformData) == 0x80, "The size of the UniformData structure has changed, update the structure in the shader");
- static_assert(sizeof(UniformData) < 16000, "UniformData structure must be less than 16kb as per the OpenGL spec");
+ static_assert(sizeof(UniformData) == 0x310, "The size of the UniformData structure has changed, update the structure in the shader");
+ static_assert(sizeof(UniformData) < 16384, "UniformData structure must be less than 16kb as per the OpenGL spec");
/// Reconfigure the OpenGL color texture to use the given format and dimensions
void ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height);
@@ -233,6 +326,9 @@ private:
/// Syncs the cull mode to match the PICA register
void SyncCullMode();
+ /// Syncs the depth scale and offset to match the PICA registers
+ void SyncDepthModifiers();
+
/// Syncs the blend enabled status to match the PICA register
void SyncBlendEnabled();
@@ -260,6 +356,27 @@ private:
/// Syncs the TEV combiner color buffer to match the PICA register
void SyncCombinerColor();
+ /// Syncs the lighting global ambient color to match the PICA register
+ void SyncGlobalAmbient();
+
+ /// Syncs the lighting lookup tables
+ void SyncLightingLUT(unsigned index);
+
+ /// Syncs the specified light's diffuse color to match the PICA register
+ void SyncLightDiffuse(int light_index);
+
+ /// Syncs the specified light's ambient color to match the PICA register
+ void SyncLightAmbient(int light_index);
+
+ /// Syncs the specified light's position to match the PICA register
+ void SyncLightPosition(int light_index);
+
+ /// Syncs the specified light's specular 0 color to match the PICA register
+ void SyncLightSpecular0(int light_index);
+
+ /// Syncs the specified light's specular 1 color to match the PICA register
+ void SyncLightSpecular1(int light_index);
+
/// Syncs the remaining OpenGL drawing state to match the current PICA state
void SyncDrawState();
@@ -289,8 +406,8 @@ private:
OpenGLState state;
- PAddr last_fb_color_addr;
- PAddr last_fb_depth_addr;
+ PAddr cached_fb_color_addr;
+ PAddr cached_fb_depth_addr;
// Hardware rasterizer
std::array<SamplerInfo, 3> texture_samplers;
@@ -302,6 +419,7 @@ private:
struct {
UniformData data;
+ bool lut_dirty[6];
bool dirty;
} uniform_block_data;
@@ -309,4 +427,7 @@ private:
OGLBuffer vertex_buffer;
OGLBuffer uniform_buffer;
OGLFramebuffer framebuffer;
+
+ std::array<OGLTexture, 6> lighting_lut;
+ std::array<std::array<GLvec4, 256>, 6> lighting_lut_data;
};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 10d4ab0b6..a9ad46fe0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -15,7 +15,7 @@
#include "video_core/renderer_opengl/pica_to_gl.h"
RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
- FullFlush();
+ InvalidateAll();
}
MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -58,8 +58,7 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
}
}
-void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash) {
- // Flush any texture that falls in the flushed region
+void RasterizerCacheOpenGL::InvalidateInRange(PAddr addr, u32 size, bool ignore_hash) {
// TODO: Optimize by also inserting upper bound (addr + size) of each texture into the same map and also narrow using lower_bound
auto cache_upper_bound = texture_cache.upper_bound(addr + size);
@@ -77,6 +76,6 @@ void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash)
}
}
-void RasterizerCacheOpenGL::FullFlush() {
+void RasterizerCacheOpenGL::InvalidateAll() {
texture_cache.clear();
}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 98a48ffbe..b69651427 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -23,11 +23,11 @@ public:
LoadAndBindTexture(state, texture_unit, Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format));
}
- /// Flush any cached resource that touches the flushed region
- void NotifyFlush(PAddr addr, u32 size, bool ignore_hash = false);
+ /// Invalidate any cached resource intersecting the specified region.
+ void InvalidateInRange(PAddr addr, u32 size, bool ignore_hash = false);
- /// Flush all cached OpenGL resources tracked by this cache manager
- void FullFlush();
+ /// Invalidate all cached OpenGL resources tracked by this cache manager
+ void InvalidateAll();
private:
struct CachedTexture {
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 498c506e7..ee4b54ab9 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -32,12 +32,10 @@ static void AppendSource(std::string& out, TevStageConfig::Source source,
out += "primary_color";
break;
case Source::PrimaryFragmentColor:
- // HACK: Until we implement fragment lighting, use primary_color
- out += "primary_color";
+ out += "primary_fragment_color";
break;
case Source::SecondaryFragmentColor:
- // HACK: Until we implement fragment lighting, use zero
- out += "vec4(0.0)";
+ out += "secondary_fragment_color";
break;
case Source::Texture0:
out += "texture(tex[0], texcoord[0])";
@@ -320,25 +318,229 @@ static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsi
out += "next_combiner_buffer.a = last_tex_env_out.a;\n";
}
+/// Writes the code to emulate fragment lighting
+static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
+ // Define lighting globals
+ out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
+ "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n"
+ "vec3 light_vector = vec3(0.0);\n"
+ "vec3 refl_value = vec3(0.0);\n";
+
+ // Compute fragment normals
+ if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) {
+ // Bump mapping is enabled using a normal map, read perturbation vector from the selected texture
+ std::string bump_selector = std::to_string(config.lighting.bump_selector);
+ out += "vec3 surface_normal = 2.0 * texture(tex[" + bump_selector + "], texcoord[" + bump_selector + "]).rgb - 1.0;\n";
+
+ // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher precision result
+ if (config.lighting.bump_renorm) {
+ std::string val = "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))";
+ out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n";
+ }
+ } else if (config.lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) {
+ // Bump mapping is enabled using a tangent map
+ LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)");
+ UNIMPLEMENTED();
+ } else {
+ // No bump mapping - surface local normal is just a unit normal
+ out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n";
+ }
+
+ // Rotate the surface-local normal by the interpolated normal quaternion to convert it to eyespace
+ out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n";
+
+ // Gets the index into the specified lookup table for specular lighting
+ auto GetLutIndex = [config](unsigned light_num, Regs::LightingLutInput input, bool abs) {
+ const std::string half_angle = "normalize(normalize(view) + light_vector)";
+ std::string index;
+ switch (input) {
+ case Regs::LightingLutInput::NH:
+ index = "dot(normal, " + half_angle + ")";
+ break;
+
+ case Regs::LightingLutInput::VH:
+ index = std::string("dot(normalize(view), " + half_angle + ")");
+ break;
+
+ case Regs::LightingLutInput::NV:
+ index = std::string("dot(normal, normalize(view))");
+ break;
+
+ case Regs::LightingLutInput::LN:
+ index = std::string("dot(light_vector, normal)");
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input);
+ UNIMPLEMENTED();
+ break;
+ }
+
+ if (abs) {
+ // LUT index is in the range of (0.0, 1.0)
+ index = config.lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" : "max(" + index + ", 0.f)";
+ return "(FLOAT_255 * clamp(" + index + ", 0.0, 1.0))";
+ } else {
+ // LUT index is in the range of (-1.0, 1.0)
+ index = "clamp(" + index + ", -1.0, 1.0)";
+ return "(FLOAT_255 * ((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0)";
+ }
+
+ return std::string();
+ };
+
+ // Gets the lighting lookup table value given the specified sampler and index
+ auto GetLutValue = [](Regs::LightingSampler sampler, std::string lut_index) {
+ return std::string("texture(lut[" + std::to_string((unsigned)sampler / 4) + "], " +
+ lut_index + ")[" + std::to_string((unsigned)sampler & 3) + "]");
+ };
+
+ // Write the code to emulate each enabled light
+ for (unsigned light_index = 0; light_index < config.lighting.src_num; ++light_index) {
+ const auto& light_config = config.lighting.light[light_index];
+ std::string light_src = "light_src[" + std::to_string(light_config.num) + "]";
+
+ // Compute light vector (directional or positional)
+ if (light_config.directional)
+ out += "light_vector = normalize(" + light_src + ".position);\n";
+ else
+ out += "light_vector = normalize(" + light_src + ".position + view);\n";
+
+ // Compute dot product of light_vector and normal, adjust if lighting is one-sided or two-sided
+ std::string dot_product = light_config.two_sided_diffuse ? "abs(dot(light_vector, normal))" : "max(dot(light_vector, normal), 0.0)";
+
+ // If enabled, compute distance attenuation value
+ std::string dist_atten = "1.0";
+ if (light_config.dist_atten_enable) {
+ std::string scale = std::to_string(light_config.dist_atten_scale);
+ std::string bias = std::to_string(light_config.dist_atten_bias);
+ std::string index = "(" + scale + " * length(-view - " + light_src + ".position) + " + bias + ")";
+ index = "((clamp(" + index + ", 0.0, FLOAT_255)))";
+ const unsigned lut_num = ((unsigned)Regs::LightingSampler::DistanceAttenuation + light_config.num);
+ dist_atten = GetLutValue((Regs::LightingSampler)lut_num, index);
+ }
+
+ // If enabled, clamp specular component if lighting result is negative
+ std::string clamp_highlights = config.lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0";
+
+ // Specular 0 component
+ std::string d0_lut_value = "1.0";
+ if (config.lighting.lut_d0.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution0)) {
+ // Lookup specular "distribution 0" LUT value
+ std::string index = GetLutIndex(light_config.num, config.lighting.lut_d0.type, config.lighting.lut_d0.abs_input);
+ d0_lut_value = "(" + std::to_string(config.lighting.lut_d0.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution0, index) + ")";
+ }
+ std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)";
+
+ // If enabled, lookup ReflectRed value, otherwise, 1.0 is used
+ if (config.lighting.lut_rr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectRed)) {
+ std::string index = GetLutIndex(light_config.num, config.lighting.lut_rr.type, config.lighting.lut_rr.abs_input);
+ std::string value = "(" + std::to_string(config.lighting.lut_rr.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")";
+ out += "refl_value.r = " + value + ";\n";
+ } else {
+ out += "refl_value.r = 1.0;\n";
+ }
+
+ // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used
+ if (config.lighting.lut_rg.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectGreen)) {
+ std::string index = GetLutIndex(light_config.num, config.lighting.lut_rg.type, config.lighting.lut_rg.abs_input);
+ std::string value = "(" + std::to_string(config.lighting.lut_rg.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")";
+ out += "refl_value.g = " + value + ";\n";
+ } else {
+ out += "refl_value.g = refl_value.r;\n";
+ }
+
+ // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used
+ if (config.lighting.lut_rb.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::ReflectBlue)) {
+ std::string index = GetLutIndex(light_config.num, config.lighting.lut_rb.type, config.lighting.lut_rb.abs_input);
+ std::string value = "(" + std::to_string(config.lighting.lut_rb.scale) + " * " + GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")";
+ out += "refl_value.b = " + value + ";\n";
+ } else {
+ out += "refl_value.b = refl_value.r;\n";
+ }
+
+ // Specular 1 component
+ std::string d1_lut_value = "1.0";
+ if (config.lighting.lut_d1.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Distribution1)) {
+ // Lookup specular "distribution 1" LUT value
+ std::string index = GetLutIndex(light_config.num, config.lighting.lut_d1.type, config.lighting.lut_d1.abs_input);
+ d1_lut_value = "(" + std::to_string(config.lighting.lut_d1.scale) + " * " + GetLutValue(Regs::LightingSampler::Distribution1, index) + ")";
+ }
+ std::string specular_1 = "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)";
+
+ // Fresnel
+ if (config.lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(config.lighting.config, Pica::Regs::LightingSampler::Fresnel)) {
+ // Lookup fresnel LUT value
+ std::string index = GetLutIndex(light_config.num, config.lighting.lut_fr.type, config.lighting.lut_fr.abs_input);
+ std::string value = "(" + std::to_string(config.lighting.lut_fr.scale) + " * " + GetLutValue(Regs::LightingSampler::Fresnel, index) + ")";
+
+ // Enabled for difffuse lighting alpha component
+ if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha ||
+ config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
+ out += "diffuse_sum.a *= " + value + ";\n";
+
+ // Enabled for the specular lighting alpha component
+ if (config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha ||
+ config.lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
+ out += "specular_sum.a *= " + value + ";\n";
+ }
+
+ // Compute primary fragment color (diffuse lighting) function
+ out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * " + dot_product + ") + " + light_src + ".ambient) * " + dist_atten + ";\n";
+
+ // Compute secondary fragment color (specular lighting) function
+ out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + ") * " + clamp_highlights + " * " + dist_atten + ";\n";
+ }
+
+ // Sum final lighting result
+ out += "diffuse_sum.rgb += lighting_global_ambient;\n";
+ out += "primary_fragment_color = clamp(diffuse_sum, vec4(0.0), vec4(1.0));\n";
+ out += "secondary_fragment_color = clamp(specular_sum, vec4(0.0), vec4(1.0));\n";
+}
+
std::string GenerateFragmentShader(const PicaShaderConfig& config) {
std::string out = R"(
#version 330 core
#define NUM_TEV_STAGES 6
+#define NUM_LIGHTS 8
+#define LIGHTING_LUT_SIZE 256
+#define FLOAT_255 (255.0 / 256.0)
in vec4 primary_color;
in vec2 texcoord[3];
+in vec4 normquat;
+in vec3 view;
out vec4 color;
+struct LightSrc {
+ vec3 specular_0;
+ vec3 specular_1;
+ vec3 diffuse;
+ vec3 ambient;
+ vec3 position;
+};
+
layout (std140) uniform shader_data {
vec4 const_color[NUM_TEV_STAGES];
vec4 tev_combiner_buffer_color;
int alphatest_ref;
+ float depth_offset;
+ vec3 lighting_global_ambient;
+ LightSrc light_src[NUM_LIGHTS];
};
uniform sampler2D tex[3];
+uniform sampler1D lut[6];
+
+// Rotate the vector v by the quaternion q
+vec3 quaternion_rotate(vec4 q, vec3 v) {
+ return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
+}
void main() {
+vec4 primary_fragment_color = vec4(0.0);
+vec4 secondary_fragment_color = vec4(0.0);
)";
// Do not do any sort of processing if it's obvious we're not going to pass the alpha test
@@ -347,6 +549,9 @@ void main() {
return out;
}
+ if (config.lighting.enable)
+ WriteLighting(out, config);
+
out += "vec4 combiner_buffer = vec4(0.0);\n";
out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n";
out += "vec4 last_tex_env_out = vec4(0.0);\n";
@@ -360,29 +565,37 @@ void main() {
out += ") discard;\n";
}
- out += "color = last_tex_env_out;\n}";
+ out += "color = last_tex_env_out;\n";
+ out += "gl_FragDepth = gl_FragCoord.z + depth_offset;\n}";
return out;
}
std::string GenerateVertexShader() {
std::string out = "#version 330 core\n";
+
out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + ") in vec4 vert_position;\n";
out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n";
out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + ") in vec2 vert_texcoord0;\n";
out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + ") in vec2 vert_texcoord1;\n";
out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + ") in vec2 vert_texcoord2;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_NORMQUAT) + ") in vec4 vert_normquat;\n";
+ out += "layout(location = " + std::to_string((int)ATTRIBUTE_VIEW) + ") in vec3 vert_view;\n";
out += R"(
out vec4 primary_color;
out vec2 texcoord[3];
+out vec4 normquat;
+out vec3 view;
void main() {
primary_color = vert_color;
texcoord[0] = vert_texcoord0;
texcoord[1] = vert_texcoord1;
texcoord[2] = vert_texcoord2;
- gl_Position = vec4(vert_position.x, -vert_position.y, -vert_position.z, vert_position.w);
+ normquat = vert_normquat;
+ view = vert_view;
+ gl_Position = vec4(vert_position.x, vert_position.y, -vert_position.z, vert_position.w);
}
)";
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 046aae14f..097242f6f 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -14,6 +14,8 @@ enum Attributes {
ATTRIBUTE_TEXCOORD0,
ATTRIBUTE_TEXCOORD1,
ATTRIBUTE_TEXCOORD2,
+ ATTRIBUTE_NORMQUAT,
+ ATTRIBUTE_VIEW,
};
/**
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index c44497fc3..08e4d0b54 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -11,6 +11,7 @@ OpenGLState::OpenGLState() {
// These all match default OpenGL values
cull.enabled = false;
cull.mode = GL_BACK;
+ cull.front_face = GL_CCW;
depth.test_enabled = false;
depth.test_func = GL_LESS;
@@ -47,6 +48,10 @@ OpenGLState::OpenGLState() {
texture_unit.sampler = 0;
}
+ for (auto& lut : lighting_lut) {
+ lut.texture_1d = 0;
+ }
+
draw.framebuffer = 0;
draw.vertex_array = 0;
draw.vertex_buffer = 0;
@@ -67,6 +72,10 @@ void OpenGLState::Apply() {
glCullFace(cull.mode);
}
+ if (cull.front_face != cur_state.cull.front_face) {
+ glFrontFace(cull.front_face);
+ }
+
// Depth test
if (depth.test_enabled != cur_state.depth.test_enabled) {
if (depth.test_enabled) {
@@ -165,6 +174,14 @@ void OpenGLState::Apply() {
}
}
+ // Lighting LUTs
+ for (unsigned i = 0; i < ARRAY_SIZE(lighting_lut); ++i) {
+ if (lighting_lut[i].texture_1d != cur_state.lighting_lut[i].texture_1d) {
+ glActiveTexture(GL_TEXTURE3 + i);
+ glBindTexture(GL_TEXTURE_1D, lighting_lut[i].texture_1d);
+ }
+ }
+
// Framebuffer
if (draw.framebuffer != cur_state.draw.framebuffer) {
glBindFramebuffer(GL_FRAMEBUFFER, draw.framebuffer);
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 84b3d49bc..e848058d7 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -11,6 +11,7 @@ public:
struct {
bool enabled; // GL_CULL_FACE
GLenum mode; // GL_CULL_FACE_MODE
+ GLenum front_face; // GL_FRONT_FACE
} cull;
struct {
@@ -61,6 +62,10 @@ public:
} texture_units[3];
struct {
+ GLuint texture_1d; // GL_TEXTURE_BINDING_1D
+ } lighting_lut[6];
+
+ struct {
GLuint framebuffer; // GL_DRAW_FRAMEBUFFER_BINDING
GLuint vertex_array; // GL_VERTEX_ARRAY_BINDING
GLuint vertex_buffer; // GL_ARRAY_BUFFER_BINDING
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index 04c1d1a34..3d6c4e9e5 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -10,6 +10,9 @@
#include "video_core/pica.h"
+using GLvec3 = std::array<GLfloat, 3>;
+using GLvec4 = std::array<GLfloat, 4>;
+
namespace PicaToGL {
inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) {
@@ -175,7 +178,7 @@ inline GLenum StencilOp(Pica::Regs::StencilAction action) {
return stencil_op_table[(unsigned)action];
}
-inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) {
+inline GLvec4 ColorRGBA8(const u32 color) {
return { { (color >> 0 & 0xFF) / 255.0f,
(color >> 8 & 0xFF) / 255.0f,
(color >> 16 & 0xFF) / 255.0f,
@@ -183,4 +186,11 @@ inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) {
} };
}
+inline std::array<GLfloat, 3> LightColor(const Pica::Regs::LightColor& color) {
+ return { { color.r / 255.0f,
+ color.g / 255.0f,
+ color.b / 255.0f
+ } };
+}
+
} // namespace
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 1420229cc..11c4d0daf 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -81,8 +81,8 @@ struct ScreenRectVertex {
* The projection part of the matrix is trivial, hence these operations are represented
* by a 3x2 matrix.
*/
-static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const float height) {
- std::array<GLfloat, 3*2> matrix;
+static std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(const float width, const float height) {
+ std::array<GLfloat, 3 * 2> matrix;
matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
@@ -93,7 +93,6 @@ static std::array<GLfloat, 3*2> MakeOrthographicMatrix(const float width, const
/// RendererOpenGL constructor
RendererOpenGL::RendererOpenGL() {
- hw_rasterizer.reset(new RasterizerOpenGL());
resolution_width = std::max(VideoCore::kScreenTopWidth, VideoCore::kScreenBottomWidth);
resolution_height = VideoCore::kScreenTopHeight + VideoCore::kScreenBottomHeight;
}
@@ -157,15 +156,7 @@ void RendererOpenGL::SwapBuffers() {
profiler.BeginFrame();
- bool hw_renderer_enabled = VideoCore::g_hw_renderer_enabled;
- if (Settings::values.use_hw_renderer != hw_renderer_enabled) {
- // TODO: Save new setting value to config file for next startup
- Settings::values.use_hw_renderer = hw_renderer_enabled;
-
- if (Settings::values.use_hw_renderer) {
- hw_rasterizer->Reset();
- }
- }
+ RefreshRasterizerSetting();
if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
Pica::g_debug_context->recorder->FrameFinished();
@@ -286,8 +277,6 @@ void RendererOpenGL::InitOpenGLObjects() {
state.texture_units[0].texture_2d = 0;
state.Apply();
-
- hw_rasterizer->InitObjects();
}
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@@ -405,8 +394,58 @@ void RendererOpenGL::SetWindow(EmuWindow* window) {
render_window = window;
}
+static const char* GetSource(GLenum source) {
+#define RET(s) case GL_DEBUG_SOURCE_##s: return #s
+ switch (source) {
+ RET(API);
+ RET(WINDOW_SYSTEM);
+ RET(SHADER_COMPILER);
+ RET(THIRD_PARTY);
+ RET(APPLICATION);
+ RET(OTHER);
+ default:
+ UNREACHABLE();
+ }
+#undef RET
+}
+
+static const char* GetType(GLenum type) {
+#define RET(t) case GL_DEBUG_TYPE_##t: return #t
+ switch (type) {
+ RET(ERROR);
+ RET(DEPRECATED_BEHAVIOR);
+ RET(UNDEFINED_BEHAVIOR);
+ RET(PORTABILITY);
+ RET(PERFORMANCE);
+ RET(OTHER);
+ RET(MARKER);
+ default:
+ UNREACHABLE();
+ }
+#undef RET
+}
+
+static void DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
+ const GLchar* message, const void* user_param) {
+ Log::Level level;
+ switch (severity) {
+ case GL_DEBUG_SEVERITY_HIGH:
+ level = Log::Level::Error;
+ break;
+ case GL_DEBUG_SEVERITY_MEDIUM:
+ level = Log::Level::Warning;
+ break;
+ case GL_DEBUG_SEVERITY_NOTIFICATION:
+ case GL_DEBUG_SEVERITY_LOW:
+ level = Log::Level::Debug;
+ break;
+ }
+ LOG_GENERIC(Log::Class::Render_OpenGL, level, "%s %s %d: %s",
+ GetSource(source), GetType(type), id, message);
+}
+
/// Initialize the renderer
-void RendererOpenGL::Init() {
+bool RendererOpenGL::Init() {
render_window->MakeCurrent();
// TODO: Make frontends initialize this, so they can use gladLoadGLLoader with their own loaders
@@ -415,10 +454,23 @@ void RendererOpenGL::Init() {
exit(-1);
}
+ if (GLAD_GL_KHR_debug) {
+ glEnable(GL_DEBUG_OUTPUT);
+ glDebugMessageCallback(DebugHandler, nullptr);
+ }
+
LOG_INFO(Render_OpenGL, "GL_VERSION: %s", glGetString(GL_VERSION));
LOG_INFO(Render_OpenGL, "GL_VENDOR: %s", glGetString(GL_VENDOR));
LOG_INFO(Render_OpenGL, "GL_RENDERER: %s", glGetString(GL_RENDERER));
+ if (!GLAD_GL_VERSION_3_3) {
+ return false;
+ }
+
InitOpenGLObjects();
+
+ RefreshRasterizerSetting();
+
+ return true;
}
/// Shutdown the renderer
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index b42df7654..fe4d142a5 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -31,7 +31,7 @@ public:
void SetWindow(EmuWindow* window) override;
/// Initialize the renderer
- void Init() override;
+ bool Init() override;
/// Shutdown the renderer
void ShutDown() override;
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index 59f54236b..5e8930476 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -14,6 +14,7 @@
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/video_core.h"
#include "shader.h"
@@ -134,16 +135,18 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
}
- LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), quat (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)",
+ LOG_TRACE(Render_Software, "Output vertex: pos(%.2f, %.2f, %.2f, %.2f), quat(%.2f, %.2f, %.2f, %.2f), "
+ "col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f), view(%.2f, %.2f, %.2f)",
ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
ret.quat.x.ToFloat32(), ret.quat.y.ToFloat32(), ret.quat.z.ToFloat32(), ret.quat.w.ToFloat32(),
ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),
- ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32());
+ ret.tc0.u().ToFloat32(), ret.tc0.v().ToFloat32(),
+ ret.view.x.ToFloat32(), ret.view.y.ToFloat32(), ret.view.z.ToFloat32());
return ret;
}
-DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup) {
+DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup) {
UnitState<true> state;
state.program_counter = config.main_offset;
diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h
index 1c6fa592c..7af8f1fa1 100644
--- a/src/video_core/shader/shader.h
+++ b/src/video_core/shader/shader.h
@@ -37,17 +37,19 @@ struct OutputVertex {
Math::Vec4<float24> color;
Math::Vec2<float24> tc0;
Math::Vec2<float24> tc1;
- float24 pad[6];
+ INSERT_PADDING_WORDS(2);
+ Math::Vec3<float24> view;
+ INSERT_PADDING_WORDS(1);
Math::Vec2<float24> tc2;
// Padding for optimal alignment
- float24 pad2[4];
+ INSERT_PADDING_WORDS(4);
// Attributes used to store intermediate results
// position after perspective divide
Math::Vec3<float24> screenpos;
- float24 pad3;
+ INSERT_PADDING_WORDS(1);
// Linear interpolation
// factor: 0=this, 1=vtx
@@ -75,6 +77,22 @@ struct OutputVertex {
static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD");
static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size");
+/// Vertex shader memory
+struct ShaderSetup {
+ struct {
+ // The float uniforms are accessed by the shader JIT using SSE instructions, and are
+ // therefore required to be 16-byte aligned.
+ alignas(16) Math::Vec4<float24> f[96];
+
+ std::array<bool, 16> b;
+ std::array<Math::Vec4<u8>, 4> i;
+ } uniforms;
+
+ Math::Vec4<float24> default_attributes[16];
+
+ std::array<u32, 1024> program_code;
+ std::array<u32, 1024> swizzle_data;
+};
// Helper structure used to keep track of data useful for inspection of shader emulation
template<bool full_debugging>
@@ -258,9 +276,9 @@ struct UnitState {
struct Registers {
// The registers are accessed by the shader JIT using SSE instructions, and are therefore
// required to be 16-byte aligned.
- Math::Vec4<float24> MEMORY_ALIGNED16(input[16]);
- Math::Vec4<float24> MEMORY_ALIGNED16(output[16]);
- Math::Vec4<float24> MEMORY_ALIGNED16(temporary[16]);
+ alignas(16) Math::Vec4<float24> input[16];
+ alignas(16) Math::Vec4<float24> output[16];
+ alignas(16) Math::Vec4<float24> temporary[16];
} registers;
static_assert(std::is_pod<Registers>::value, "Structure is not POD");
@@ -345,7 +363,7 @@ OutputVertex Run(UnitState<false>& state, const InputVertex& input, int num_attr
* @param setup Setup object for the shader pipeline
* @return Debug information for this shader with regards to the given vertex
*/
-DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const State::ShaderSetup& setup);
+DebugData<true> ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup);
} // namespace Shader
diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp
index 7b0c20b74..79fcc56b9 100644
--- a/src/video_core/shader/shader_interpreter.cpp
+++ b/src/video_core/shader/shader_interpreter.cpp
@@ -7,6 +7,7 @@
#include <nihstro/shader_bytecode.h>
#include "video_core/pica.h"
+#include "video_core/pica_state.h"
#include "video_core/shader/shader.h"
#include "video_core/shader/shader_interpreter.h"
@@ -515,7 +516,8 @@ void RunInterpreter(UnitState<Debug>& state) {
case OpCode::Id::JMPU:
Record<DebugDataRecord::COND_BOOL_IN>(state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]);
- if (uniforms.b[instr.flow_control.bool_uniform_id]) {
+
+ if (uniforms.b[instr.flow_control.bool_uniform_id] == !(instr.flow_control.num_instructions & 1)) {
state.program_counter = instr.flow_control.dest_offset - 1;
}
break;
diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp
index 00415e402..5083d7e54 100644
--- a/src/video_core/shader/shader_jit_x64.cpp
+++ b/src/video_core/shader/shader_jit_x64.cpp
@@ -11,6 +11,8 @@
#include "shader.h"
#include "shader_jit_x64.h"
+#include "video_core/pica_state.h"
+
namespace Pica {
namespace Shader {
@@ -653,7 +655,7 @@ void JitCompiler::Compile_IF(Instruction instr) {
FixupBranch b = J_CC(CC_Z, true);
// Compile the code that corresponds to the condition evaluating as true
- Compile_Block(instr.flow_control.dest_offset - 1);
+ Compile_Block(instr.flow_control.dest_offset);
// If there isn't an "ELSE" condition, we are done here
if (instr.flow_control.num_instructions == 0) {
@@ -667,7 +669,7 @@ void JitCompiler::Compile_IF(Instruction instr) {
// This code corresponds to the "ELSE" condition
// Comple the code that corresponds to the condition evaluating as false
- Compile_Block(instr.flow_control.dest_offset + instr.flow_control.num_instructions - 1);
+ Compile_Block(instr.flow_control.dest_offset + instr.flow_control.num_instructions);
SetJumpTarget(b2);
}
@@ -691,7 +693,7 @@ void JitCompiler::Compile_LOOP(Instruction instr) {
auto loop_start = GetCodePtr();
- Compile_Block(instr.flow_control.dest_offset);
+ Compile_Block(instr.flow_control.dest_offset + 1);
ADD(32, R(LOOPCOUNT_REG), R(LOOPINC)); // Increment LOOPCOUNT_REG by Z-component
SUB(32, R(LOOPCOUNT), Imm8(1)); // Increment loop count by 1
@@ -710,19 +712,21 @@ void JitCompiler::Compile_JMP(Instruction instr) {
else
UNREACHABLE();
- FixupBranch b = J_CC(CC_NZ, true);
+ bool inverted_condition = (instr.opcode.Value() == OpCode::Id::JMPU) &&
+ (instr.flow_control.num_instructions & 1);
+ FixupBranch b = J_CC(inverted_condition ? CC_Z : CC_NZ, true);
Compile_Block(instr.flow_control.dest_offset);
SetJumpTarget(b);
}
-void JitCompiler::Compile_Block(unsigned stop) {
+void JitCompiler::Compile_Block(unsigned end) {
// Save current offset pointer
unsigned* prev_offset_ptr = offset_ptr;
unsigned offset = *prev_offset_ptr;
- while (offset <= stop)
+ while (offset < end)
Compile_NextInstr(&offset);
// Restore current offset pointer
diff --git a/src/video_core/shader/shader_jit_x64.h b/src/video_core/shader/shader_jit_x64.h
index 3afbceccf..5ad2d9606 100644
--- a/src/video_core/shader/shader_jit_x64.h
+++ b/src/video_core/shader/shader_jit_x64.h
@@ -61,7 +61,7 @@ public:
void Compile_MAD(Instruction instr);
private:
- void Compile_Block(unsigned stop);
+ void Compile_Block(unsigned end);
void Compile_NextInstr(unsigned* offset);
void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, Gen::X64Reg dest);
diff --git a/src/video_core/swrasterizer.cpp b/src/video_core/swrasterizer.cpp
new file mode 100644
index 000000000..03df15b01
--- /dev/null
+++ b/src/video_core/swrasterizer.cpp
@@ -0,0 +1,16 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/clipper.h"
+#include "video_core/swrasterizer.h"
+
+namespace VideoCore {
+
+void SWRasterizer::AddTriangle(const Pica::Shader::OutputVertex& v0,
+ const Pica::Shader::OutputVertex& v1,
+ const Pica::Shader::OutputVertex& v2) {
+ Pica::Clipper::ProcessTriangle(v0, v1, v2);
+}
+
+}
diff --git a/src/video_core/swrasterizer.h b/src/video_core/swrasterizer.h
new file mode 100644
index 000000000..9a9a76d7a
--- /dev/null
+++ b/src/video_core/swrasterizer.h
@@ -0,0 +1,26 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "video_core/rasterizer_interface.h"
+
+namespace VideoCore {
+
+class SWRasterizer : public RasterizerInterface {
+ void InitObjects() override {}
+ void Reset() override {}
+ void AddTriangle(const Pica::Shader::OutputVertex& v0,
+ const Pica::Shader::OutputVertex& v1,
+ const Pica::Shader::OutputVertex& v2) override;
+ void DrawTriangles() override {}
+ void FlushFramebuffer() override {}
+ void NotifyPicaRegisterChanged(u32 id) override {}
+ void FlushRegion(PAddr addr, u32 size) override {}
+ void InvalidateRegion(PAddr addr, u32 size) override {}
+};
+
+}
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index eaddda668..ee5e50df1 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -2,7 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <memory>
+
#include "common/emu_window.h"
+#include "common/make_unique.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -18,29 +21,33 @@
namespace VideoCore {
-EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
-RendererBase* g_renderer = nullptr; ///< Renderer plugin
+EmuWindow* g_emu_window = nullptr; ///< Frontend emulator window
+std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
std::atomic<bool> g_hw_renderer_enabled;
std::atomic<bool> g_shader_jit_enabled;
/// Initialize the video core
-void Init(EmuWindow* emu_window) {
+bool Init(EmuWindow* emu_window) {
Pica::Init();
g_emu_window = emu_window;
- g_renderer = new RendererOpenGL();
+ g_renderer = Common::make_unique<RendererOpenGL>();
g_renderer->SetWindow(g_emu_window);
- g_renderer->Init();
-
- LOG_DEBUG(Render, "initialized OK");
+ if (g_renderer->Init()) {
+ LOG_DEBUG(Render, "initialized OK");
+ } else {
+ LOG_ERROR(Render, "initialization failed !");
+ return false;
+ }
+ return true;
}
/// Shutdown the video core
void Shutdown() {
Pica::Shutdown();
- delete g_renderer;
+ g_renderer.reset();
LOG_DEBUG(Render, "shutdown OK");
}
diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h
index 2867bf03e..bca67fb8c 100644
--- a/src/video_core/video_core.h
+++ b/src/video_core/video_core.h
@@ -5,6 +5,7 @@
#pragma once
#include <atomic>
+#include <memory>
class EmuWindow;
class RendererBase;
@@ -29,8 +30,8 @@ static const int kScreenBottomHeight = 240; ///< 3DS bottom screen height
// Video core renderer
// ---------------------
-extern RendererBase* g_renderer; ///< Renderer plugin
-extern EmuWindow* g_emu_window; ///< Emu window
+extern std::unique_ptr<RendererBase> g_renderer; ///< Renderer plugin
+extern EmuWindow* g_emu_window; ///< Emu window
// TODO: Wrap these in a user settings struct along with any other graphics settings (often set from qt ui)
extern std::atomic<bool> g_hw_renderer_enabled;
@@ -40,7 +41,7 @@ extern std::atomic<bool> g_shader_jit_enabled;
void Start();
/// Initialize the video core
-void Init(EmuWindow* emu_window);
+bool Init(EmuWindow* emu_window);
/// Shutdown the video core
void Shutdown();