#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
#include "Emu/Memory/vm_ptr.h"
#include "Emu/Memory/vm_reservation.h"
#include "Emu/Memory/vm_locking.h"

#include "Emu/Cell/PPUFunction.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/ErrorCodes.h"
#include "sys_sync.h"
#include "sys_lwmutex.h"
#include "sys_lwcond.h"
#include "sys_mutex.h"
#include "sys_cond.h"
#include "sys_event.h"
#include "sys_event_flag.h"
#include "sys_game.h"
#include "sys_interrupt.h"
#include "sys_memory.h"
#include "sys_mmapper.h"
#include "sys_net.h"
#include "sys_overlay.h"
#include "sys_ppu_thread.h"
#include "sys_process.h"
#include "sys_prx.h"
#include "sys_rsx.h"
#include "sys_rwlock.h"
#include "sys_semaphore.h"
#include "sys_spu.h"
#include "sys_time.h"
#include "sys_timer.h"
#include "sys_trace.h"
#include "sys_tty.h"
#include "sys_usbd.h"
#include "sys_vm.h"
#include "sys_fs.h"
#include "sys_dbg.h"
#include "sys_gamepad.h"
#include "sys_ss.h"
#include "sys_gpio.h"
#include "sys_config.h"
#include "sys_bdemu.h"
#include "sys_btsetting.h"
#include "sys_console.h"
#include "sys_hid.h"
#include "sys_io.h"
#include "sys_rsxaudio.h"
#include "sys_sm.h"
#include "sys_storage.h"
#include "sys_uart.h"
#include "sys_crypto_engine.h"

#include <algorithm>
#include <optional>
#include <deque>
#include <thread>
#include "util/tsc.hpp"
#include "util/sysinfo.hpp"
#include "util/init_mutex.hpp"

#if defined(ARCH_X64)
#ifdef _MSC_VER
#include <intrin.h>
#include <immintrin.h>
#else
#include <x86intrin.h>
#endif
#endif

extern std::string ppu_get_syscall_name(u64 code);

namespace rsx
{
	void set_rsx_yield_flag() noexcept;
}

using spu_rdata_t = decltype(spu_thread::rdata);
extern u32 compute_rdata_hash32(const spu_rdata_t& _src);

template <>
void fmt_class_string<ppu_syscall_code>::format(std::string& out, u64 arg)
{
	out += ppu_get_syscall_name(arg);
}

template <>
void fmt_class_string<lv2_protocol>::format(std::string& out, u64 arg)
{
	format_enum(out, arg, [](auto value)
		{
			switch (value)
			{
			case SYS_SYNC_FIFO: return "FIFO";
			case SYS_SYNC_PRIORITY: return "PRIO";
			case SYS_SYNC_PRIORITY_INHERIT: return "PRIO-INHER";
			case SYS_SYNC_RETRY: return "RETRY";
			}

			return unknown;
		});
}

template <>
void fmt_class_string<lv2_obj::name_64>::format(std::string& out, u64 arg)
{
	out += lv2_obj::name64(get_object(arg).data);
}

static void null_func_(ppu_thread& ppu, ppu_opcode_t, be_t<u32>* this_op, ppu_intrp_func*)
{
	ppu_log.todo("Unimplemented syscall %s -> CELL_OK (r3=0x%llx, r4=0x%llx, r5=0x%llx, r6=0x%llx, r7=0x%llx, r8=0x%llx, r9=0x%llx, r10=0x%llx)", ppu_syscall_code(ppu.gpr[11]),
		ppu.gpr[3], ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7], ppu.gpr[8], ppu.gpr[9], ppu.gpr[10]);

	ppu.gpr[3] = 0;
	ppu.cia = vm::get_addr(this_op) + 4;
}

static void uns_func_(ppu_thread& ppu, ppu_opcode_t, be_t<u32>* this_op, ppu_intrp_func*)
{
	ppu_log.trace("Unused syscall %d -> ENOSYS", ppu.gpr[11]);
	ppu.gpr[3] = CELL_ENOSYS;
	ppu.cia = vm::get_addr(this_op) + 4;
}

// Bind Syscall
#define BIND_SYSC(func) {BIND_FUNC(func), #func}
#define NULL_FUNC(name) {null_func_, #name}

constexpr std::pair<ppu_intrp_func_t, std::string_view> null_func{null_func_, ""};
constexpr std::pair<ppu_intrp_func_t, std::string_view> uns_func{uns_func_, ""};

// UNS = Unused
// ROOT = Root
// DBG = Debug
// DEX..DECR = Unavailable on retail consoles
// PM = Product Mode
// AuthID = Authentication ID
const std::array<std::pair<ppu_intrp_func_t, std::string_view>, 1024> g_ppu_syscall_table{
	null_func,
	BIND_SYSC(sys_process_getpid),         // 1   (0x001)
	BIND_SYSC(sys_process_wait_for_child), // 2   (0x002)  ROOT
	BIND_SYSC(sys_process_exit3),          // 3   (0x003)
	BIND_SYSC(sys_process_get_status),     // 4   (0x004)  DBG
	BIND_SYSC(sys_process_detach_child),   // 5   (0x005)  DBG

	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 6-11  UNS

	BIND_SYSC(sys_process_get_number_of_object),                 // 12  (0x00C)
	BIND_SYSC(sys_process_get_id),                               // 13  (0x00D)
	BIND_SYSC(sys_process_is_spu_lock_line_reservation_address), // 14  (0x00E)

	uns_func, uns_func, uns_func, // 15-17  UNS

	BIND_SYSC(sys_process_getppid),               // 18  (0x012)
	BIND_SYSC(sys_process_kill),                  // 19  (0x013)
	uns_func,                                     // 20  (0x014)  UNS
	NULL_FUNC(_sys_process_spawn),                // 21  (0x015)  DBG
	BIND_SYSC(_sys_process_exit),                 // 22  (0x016)
	BIND_SYSC(sys_process_wait_for_child2),       // 23  (0x017)  DBG
	null_func,                                    // BIND_SYSC(),                                //24  (0x018)  DBG
	BIND_SYSC(sys_process_get_sdk_version),       // 25  (0x019)
	BIND_SYSC(_sys_process_exit2),                // 26  (0x01A)
	BIND_SYSC(sys_process_spawns_a_self2),        // 27  (0x01B)  DBG
	NULL_FUNC(_sys_process_get_number_of_object), // 28  (0x01C)  ROOT
	BIND_SYSC(sys_process_get_id2),               // 29  (0x01D)  ROOT
	BIND_SYSC(_sys_process_get_paramsfo),         // 30  (0x01E)
	NULL_FUNC(sys_process_get_ppu_guid),          // 31  (0x01F)

	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 32-40  UNS

	BIND_SYSC(_sys_ppu_thread_exit),                  // 41  (0x029)
	uns_func,                                         // 42  (0x02A)  UNS
	BIND_SYSC(sys_ppu_thread_yield),                  // 43  (0x02B)
	BIND_SYSC(sys_ppu_thread_join),                   // 44  (0x02C)
	BIND_SYSC(sys_ppu_thread_detach),                 // 45  (0x02D)
	BIND_SYSC(sys_ppu_thread_get_join_state),         // 46  (0x02E)
	BIND_SYSC(sys_ppu_thread_set_priority),           // 47  (0x02F)  DBG
	BIND_SYSC(sys_ppu_thread_get_priority),           // 48  (0x030)
	BIND_SYSC(sys_ppu_thread_get_stack_information),  // 49  (0x031)
	BIND_SYSC(sys_ppu_thread_stop),                   // 50  (0x032)  ROOT
	BIND_SYSC(sys_ppu_thread_restart),                // 51  (0x033)  ROOT
	BIND_SYSC(_sys_ppu_thread_create),                // 52  (0x034)  DBG
	BIND_SYSC(sys_ppu_thread_start),                  // 53  (0x035)
	null_func,                                        // BIND_SYSC(sys_ppu_...),                     //54  (0x036)  ROOT
	null_func,                                        // BIND_SYSC(sys_ppu_...),                     //55  (0x037)  ROOT
	BIND_SYSC(sys_ppu_thread_rename),                 // 56  (0x038)
	BIND_SYSC(sys_ppu_thread_recover_page_fault),     // 57  (0x039)
	BIND_SYSC(sys_ppu_thread_get_page_fault_context), // 58  (0x03A)
	uns_func,                                         // 59  (0x03B)  UNS
	BIND_SYSC(sys_trace_create),                      // 60  (0x03C)
	BIND_SYSC(sys_trace_start),                       // 61  (0x03D)
	BIND_SYSC(sys_trace_stop),                        // 62  (0x03E)
	BIND_SYSC(sys_trace_update_top_index),            // 63  (0x03F)
	BIND_SYSC(sys_trace_destroy),                     // 64  (0x040)
	BIND_SYSC(sys_trace_drain),                       // 65  (0x041)
	BIND_SYSC(sys_trace_attach_process),              // 66  (0x042)
	BIND_SYSC(sys_trace_allocate_buffer),             // 67  (0x043)
	BIND_SYSC(sys_trace_free_buffer),                 // 68  (0x044)
	BIND_SYSC(sys_trace_create2),                     // 69  (0x045)
	BIND_SYSC(sys_timer_create),                      // 70  (0x046)
	BIND_SYSC(sys_timer_destroy),                     // 71  (0x047)
	BIND_SYSC(sys_timer_get_information),             // 72  (0x048)
	BIND_SYSC(_sys_timer_start),                      // 73  (0x049)
	BIND_SYSC(sys_timer_stop),                        // 74  (0x04A)
	BIND_SYSC(sys_timer_connect_event_queue),         // 75  (0x04B)
	BIND_SYSC(sys_timer_disconnect_event_queue),      // 76  (0x04C)
	NULL_FUNC(sys_trace_create2_in_cbepm),            // 77  (0x04D)
	null_func,                                        // BIND_SYSC(sys_trace_...),                   //78  (0x04E)
	uns_func,                                         // 79  (0x04F)  UNS
	NULL_FUNC(sys_interrupt_tag_create),              // 80  (0x050)
	BIND_SYSC(sys_interrupt_tag_destroy),             // 81  (0x051)
	BIND_SYSC(sys_event_flag_create),                 // 82  (0x052)
	BIND_SYSC(sys_event_flag_destroy),                // 83  (0x053)
	BIND_SYSC(_sys_interrupt_thread_establish),       // 84  (0x054)
	BIND_SYSC(sys_event_flag_wait),                   // 85  (0x055)
	BIND_SYSC(sys_event_flag_trywait),                // 86  (0x056)
	BIND_SYSC(sys_event_flag_set),                    // 87  (0x057)
	BIND_SYSC(sys_interrupt_thread_eoi),              // 88  (0x058)
	BIND_SYSC(_sys_interrupt_thread_disestablish),    // 89  (0x059)
	BIND_SYSC(sys_semaphore_create),                  // 90  (0x05A)
	BIND_SYSC(sys_semaphore_destroy),                 // 91  (0x05B)
	BIND_SYSC(sys_semaphore_wait),                    // 92  (0x05C)
	BIND_SYSC(sys_semaphore_trywait),                 // 93  (0x05D)
	BIND_SYSC(sys_semaphore_post),                    // 94  (0x05E)
	BIND_SYSC(_sys_lwmutex_create),                   // 95  (0x05F)
	BIND_SYSC(_sys_lwmutex_destroy),                  // 96  (0x060)
	BIND_SYSC(_sys_lwmutex_lock),                     // 97  (0x061)
	BIND_SYSC(_sys_lwmutex_unlock),                   // 98  (0x062)
	BIND_SYSC(_sys_lwmutex_trylock),                  // 99  (0x063)
	BIND_SYSC(sys_mutex_create),                      // 100 (0x064)
	BIND_SYSC(sys_mutex_destroy),                     // 101 (0x065)
	BIND_SYSC(sys_mutex_lock),                        // 102 (0x066)
	BIND_SYSC(sys_mutex_trylock),                     // 103 (0x067)
	BIND_SYSC(sys_mutex_unlock),                      // 104 (0x068)
	BIND_SYSC(sys_cond_create),                       // 105 (0x069)
	BIND_SYSC(sys_cond_destroy),                      // 106 (0x06A)
	BIND_SYSC(sys_cond_wait),                         // 107 (0x06B)
	BIND_SYSC(sys_cond_signal),                       // 108 (0x06C)
	BIND_SYSC(sys_cond_signal_all),                   // 109 (0x06D)
	BIND_SYSC(sys_cond_signal_to),                    // 110 (0x06E)
	BIND_SYSC(_sys_lwcond_create),                    // 111 (0x06F)
	BIND_SYSC(_sys_lwcond_destroy),                   // 112 (0x070)
	BIND_SYSC(_sys_lwcond_queue_wait),                // 113 (0x071)
	BIND_SYSC(sys_semaphore_get_value),               // 114 (0x072)
	BIND_SYSC(_sys_lwcond_signal),                    // 115 (0x073)
	BIND_SYSC(_sys_lwcond_signal_all),                // 116 (0x074)
	BIND_SYSC(_sys_lwmutex_unlock2),                  // 117 (0x075)
	BIND_SYSC(sys_event_flag_clear),                  // 118 (0x076)
	BIND_SYSC(sys_time_get_rtc),                      // 119 (0x077)  ROOT
	BIND_SYSC(sys_rwlock_create),                     // 120 (0x078)
	BIND_SYSC(sys_rwlock_destroy),                    // 121 (0x079)
	BIND_SYSC(sys_rwlock_rlock),                      // 122 (0x07A)
	BIND_SYSC(sys_rwlock_tryrlock),                   // 123 (0x07B)
	BIND_SYSC(sys_rwlock_runlock),                    // 124 (0x07C)
	BIND_SYSC(sys_rwlock_wlock),                      // 125 (0x07D)
	BIND_SYSC(sys_rwlock_trywlock),                   // 126 (0x07E)
	BIND_SYSC(sys_rwlock_wunlock),                    // 127 (0x07F)
	BIND_SYSC(sys_event_queue_create),                // 128 (0x080)
	BIND_SYSC(sys_event_queue_destroy),               // 129 (0x081)
	BIND_SYSC(sys_event_queue_receive),               // 130 (0x082)
	BIND_SYSC(sys_event_queue_tryreceive),            // 131 (0x083)
	BIND_SYSC(sys_event_flag_cancel),                 // 132 (0x084)
	BIND_SYSC(sys_event_queue_drain),                 // 133 (0x085)
	BIND_SYSC(sys_event_port_create),                 // 134 (0x086)
	BIND_SYSC(sys_event_port_destroy),                // 135 (0x087)
	BIND_SYSC(sys_event_port_connect_local),          // 136 (0x088)
	BIND_SYSC(sys_event_port_disconnect),             // 137 (0x089)
	BIND_SYSC(sys_event_port_send),                   // 138 (0x08A)
	BIND_SYSC(sys_event_flag_get),                    // 139 (0x08B)
	BIND_SYSC(sys_event_port_connect_ipc),            // 140 (0x08C)
	BIND_SYSC(sys_timer_usleep),                      // 141 (0x08D)
	BIND_SYSC(sys_timer_sleep),                       // 142 (0x08E)
	BIND_SYSC(sys_time_set_timezone),                 // 143 (0x08F)  ROOT
	BIND_SYSC(sys_time_get_timezone),                 // 144 (0x090)
	BIND_SYSC(sys_time_get_current_time),             // 145 (0x091)
	BIND_SYSC(sys_time_set_current_time),             // 146 (0x092)  ROOT
	BIND_SYSC(sys_time_get_timebase_frequency),       // 147 (0x093)
	BIND_SYSC(_sys_rwlock_trywlock),                  // 148 (0x094)
	NULL_FUNC(sys_time_get_system_time),              // 149 (0x095)
	BIND_SYSC(sys_raw_spu_create_interrupt_tag),      // 150 (0x096)
	BIND_SYSC(sys_raw_spu_set_int_mask),              // 151 (0x097)
	BIND_SYSC(sys_raw_spu_get_int_mask),              // 152 (0x098)
	BIND_SYSC(sys_raw_spu_set_int_stat),              // 153 (0x099)
	BIND_SYSC(sys_raw_spu_get_int_stat),              // 154 (0x09A)
	BIND_SYSC(_sys_spu_image_get_information),        // 155 (0x09B)
	BIND_SYSC(sys_spu_image_open),                    // 156 (0x09C)
	BIND_SYSC(_sys_spu_image_import),                 // 157 (0x09D)
	BIND_SYSC(_sys_spu_image_close),                  // 158 (0x09E)
	BIND_SYSC(_sys_spu_image_get_segments),           // 159 (0x09F)
	BIND_SYSC(sys_raw_spu_create),                    // 160 (0x0A0)
	BIND_SYSC(sys_raw_spu_destroy),                   // 161 (0x0A1)
	uns_func,                                         // 162 (0x0A2)  UNS
	BIND_SYSC(sys_raw_spu_read_puint_mb),             // 163 (0x0A3)
	uns_func,                                         // 164 (0x0A4)  UNS
	BIND_SYSC(sys_spu_thread_get_exit_status),        // 165 (0x0A5)
	BIND_SYSC(sys_spu_thread_set_argument),           // 166 (0x0A6)
	NULL_FUNC(sys_spu_thread_group_start_on_exit),    // 167 (0x0A7)
	uns_func,                                         // 168 (0x0A8)  UNS
	BIND_SYSC(sys_spu_initialize),                    // 169 (0x0A9)
	BIND_SYSC(sys_spu_thread_group_create),           // 170 (0x0AA)
	BIND_SYSC(sys_spu_thread_group_destroy),          // 171 (0x0AB)
	BIND_SYSC(sys_spu_thread_initialize),             // 172 (0x0AC)
	BIND_SYSC(sys_spu_thread_group_start),            // 173 (0x0AD)
	BIND_SYSC(sys_spu_thread_group_suspend),          // 174 (0x0AE)
	BIND_SYSC(sys_spu_thread_group_resume),           // 175 (0x0AF)
	BIND_SYSC(sys_spu_thread_group_yield),            // 176 (0x0B0)
	BIND_SYSC(sys_spu_thread_group_terminate),        // 177 (0x0B1)
	BIND_SYSC(sys_spu_thread_group_join),             // 178 (0x0B2)
	BIND_SYSC(sys_spu_thread_group_set_priority),     // 179 (0x0B3)
	BIND_SYSC(sys_spu_thread_group_get_priority),     // 180 (0x0B4)
	BIND_SYSC(sys_spu_thread_write_ls),               // 181 (0x0B5)
	BIND_SYSC(sys_spu_thread_read_ls),                // 182 (0x0B6)
	uns_func,                                         // 183 (0x0B7)  UNS
	BIND_SYSC(sys_spu_thread_write_snr),              // 184 (0x0B8)
	BIND_SYSC(sys_spu_thread_group_connect_event),    // 185 (0x0B9)
	BIND_SYSC(sys_spu_thread_group_disconnect_event), // 186 (0x0BA)
	BIND_SYSC(sys_spu_thread_set_spu_cfg),            // 187 (0x0BB)
	BIND_SYSC(sys_spu_thread_get_spu_cfg),            // 188 (0x0BC)
	uns_func,                                         // 189 (0x0BD)  UNS
	BIND_SYSC(sys_spu_thread_write_spu_mb),           // 190 (0x0BE)
	BIND_SYSC(sys_spu_thread_connect_event),          // 191 (0x0BF)
	BIND_SYSC(sys_spu_thread_disconnect_event),       // 192 (0x0C0)
	BIND_SYSC(sys_spu_thread_bind_queue),             // 193 (0x0C1)
	BIND_SYSC(sys_spu_thread_unbind_queue),           // 194 (0x0C2)
	uns_func,                                         // 195 (0x0C3)  UNS
	BIND_SYSC(sys_raw_spu_set_spu_cfg),               // 196 (0x0C4)
	BIND_SYSC(sys_raw_spu_get_spu_cfg),               // 197 (0x0C5)
	BIND_SYSC(sys_spu_thread_recover_page_fault),     // 198 (0x0C6)
	BIND_SYSC(sys_raw_spu_recover_page_fault),        // 199 (0x0C7)

	null_func, null_func, null_func, null_func, null_func, // 204  UNS?
	null_func, null_func, null_func, null_func, null_func, // 209  UNS?
	null_func, null_func, null_func,                       // 212  UNS?
	BIND_SYSC(sys_console_write2),                         // 213 (0x0D5)
	null_func,                                             // 214  UNS?

	NULL_FUNC(sys_dbg_mat_set_condition), // 215 (0x0D7)
	NULL_FUNC(sys_dbg_mat_get_condition), // 216 (0x0D8)
	uns_func,                             // BIND_SYSC(sys_dbg_...),                      //217 (0x0D9) DBG  UNS?
	uns_func,                             // BIND_SYSC(sys_dbg_...),                      //218 (0x0DA) DBG  UNS?
	uns_func,                             // BIND_SYSC(sys_dbg_...),                      //219 (0x0DB) DBG  UNS?

	null_func, null_func, null_func, null_func, null_func, // 224  UNS
	null_func, null_func, null_func, null_func, null_func, // 229  UNS?

	BIND_SYSC(sys_isolated_spu_create),                           // 230 (0x0E6)  ROOT
	BIND_SYSC(sys_isolated_spu_destroy),                          // 231 (0x0E7)  ROOT
	BIND_SYSC(sys_isolated_spu_start),                            // 232 (0x0E8)  ROOT
	BIND_SYSC(sys_isolated_spu_create_interrupt_tag),             // 233 (0x0E9)  ROOT
	BIND_SYSC(sys_isolated_spu_set_int_mask),                     // 234 (0x0EA)  ROOT
	BIND_SYSC(sys_isolated_spu_get_int_mask),                     // 235 (0x0EB)  ROOT
	BIND_SYSC(sys_isolated_spu_set_int_stat),                     // 236 (0x0EC)  ROOT
	BIND_SYSC(sys_isolated_spu_get_int_stat),                     // 237 (0x0ED)  ROOT
	BIND_SYSC(sys_isolated_spu_set_spu_cfg),                      // 238 (0x0EE)  ROOT
	BIND_SYSC(sys_isolated_spu_get_spu_cfg),                      // 239 (0x0EF)  ROOT
	BIND_SYSC(sys_isolated_spu_read_puint_mb),                    // 240 (0x0F0)  ROOT
	uns_func, uns_func, uns_func,                                 // 241-243 ROOT  UNS
	NULL_FUNC(sys_spu_thread_group_system_set_next_group),        // 244 (0x0F4)  ROOT
	NULL_FUNC(sys_spu_thread_group_system_unset_next_group),      // 245 (0x0F5)  ROOT
	NULL_FUNC(sys_spu_thread_group_system_set_switch_group),      // 246 (0x0F6)  ROOT
	NULL_FUNC(sys_spu_thread_group_system_unset_switch_group),    // 247 (0x0F7)  ROOT
	null_func,                                                    // BIND_SYSC(sys_spu_thread_group...),         //248 (0x0F8)  ROOT
	null_func,                                                    // BIND_SYSC(sys_spu_thread_group...),         //249 (0x0F9)  ROOT
	BIND_SYSC(sys_spu_thread_group_set_cooperative_victims),      // 250 (0x0FA)
	BIND_SYSC(sys_spu_thread_group_connect_event_all_threads),    // 251 (0x0FB)
	BIND_SYSC(sys_spu_thread_group_disconnect_event_all_threads), // 252 (0x0FC)
	BIND_SYSC(sys_spu_thread_group_syscall_253),                  // 253 (0x0FD)
	BIND_SYSC(sys_spu_thread_group_log),                          // 254 (0x0FE)

	uns_func, uns_func, uns_func, uns_func, uns_func, // 255-259  UNS

	NULL_FUNC(sys_spu_image_open_by_fd), // 260 (0x104)

	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func,           // 261-269  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 270-279  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 280-289  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 290-299  UNS

	BIND_SYSC(sys_vm_memory_map),           // 300 (0x12C)
	BIND_SYSC(sys_vm_unmap),                // 301 (0x12D)
	BIND_SYSC(sys_vm_append_memory),        // 302 (0x12E)
	BIND_SYSC(sys_vm_return_memory),        // 303 (0x12F)
	BIND_SYSC(sys_vm_lock),                 // 304 (0x130)
	BIND_SYSC(sys_vm_unlock),               // 305 (0x131)
	BIND_SYSC(sys_vm_touch),                // 306 (0x132)
	BIND_SYSC(sys_vm_flush),                // 307 (0x133)
	BIND_SYSC(sys_vm_invalidate),           // 308 (0x134)
	BIND_SYSC(sys_vm_store),                // 309 (0x135)
	BIND_SYSC(sys_vm_sync),                 // 310 (0x136)
	BIND_SYSC(sys_vm_test),                 // 311 (0x137)
	BIND_SYSC(sys_vm_get_statistics),       // 312 (0x138)
	BIND_SYSC(sys_vm_memory_map_different), // 313 (0x139)
	null_func,                              // BIND_SYSC(sys_...),                         //314 (0x13A)
	null_func,                              // BIND_SYSC(sys_...),                         //315 (0x13B)

	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 316-323  UNS

	BIND_SYSC(sys_memory_container_create),                           // 324 (0x144)  DBG
	BIND_SYSC(sys_memory_container_destroy),                          // 325 (0x145)  DBG
	BIND_SYSC(sys_mmapper_allocate_fixed_address),                    // 326 (0x146)
	BIND_SYSC(sys_mmapper_enable_page_fault_notification),            // 327 (0x147)
	BIND_SYSC(sys_mmapper_allocate_shared_memory_from_container_ext), // 328 (0x148)
	BIND_SYSC(sys_mmapper_free_shared_memory),                        // 329 (0x149)
	BIND_SYSC(sys_mmapper_allocate_address),                          // 330 (0x14A)
	BIND_SYSC(sys_mmapper_free_address),                              // 331 (0x14B)
	BIND_SYSC(sys_mmapper_allocate_shared_memory),                    // 332 (0x14C)
	NULL_FUNC(sys_mmapper_set_shared_memory_flag),                    // 333(0x14D)
	BIND_SYSC(sys_mmapper_map_shared_memory),                         // 334 (0x14E)
	BIND_SYSC(sys_mmapper_unmap_shared_memory),                       // 335 (0x14F)
	BIND_SYSC(sys_mmapper_change_address_access_right),               // 336 (0x150)
	BIND_SYSC(sys_mmapper_search_and_map),                            // 337 (0x151)
	NULL_FUNC(sys_mmapper_get_shared_memory_attribute),               // 338 (0x152)
	BIND_SYSC(sys_mmapper_allocate_shared_memory_ext),                // 339 (0x153)
	null_func,                                                        // BIND_SYSC(sys_...),                         //340 (0x154)
	BIND_SYSC(sys_memory_container_create),                           // 341 (0x155)
	BIND_SYSC(sys_memory_container_destroy),                          // 342 (0x156)
	BIND_SYSC(sys_memory_container_get_size),                         // 343 (0x157)
	NULL_FUNC(sys_memory_budget_set),                                 // 344 (0x158)
	BIND_SYSC(sys_memory_container_destroy_parent_with_childs),       // 345 (0x159)
	null_func,                                                        // BIND_SYSC(sys_memory_...),                  //346 (0x15A)
	uns_func,                                                         // 347 (0x15B)  UNS
	BIND_SYSC(sys_memory_allocate),                                   // 348 (0x15C)
	BIND_SYSC(sys_memory_free),                                       // 349 (0x15D)
	BIND_SYSC(sys_memory_allocate_from_container),                    // 350 (0x15E)
	BIND_SYSC(sys_memory_get_page_attribute),                         // 351 (0x15F)
	BIND_SYSC(sys_memory_get_user_memory_size),                       // 352 (0x160)
	BIND_SYSC(sys_memory_get_user_memory_stat),                       // 353 (0x161)
	null_func,                                                        // BIND_SYSC(sys_memory_...),                  //354 (0x162)
	null_func,                                                        // BIND_SYSC(sys_memory_...),                  //355 (0x163)
	NULL_FUNC(sys_memory_allocate_colored),                           // 356 (0x164)
	null_func,                                                        // BIND_SYSC(sys_memory_...),                  //357 (0x165)
	null_func,                                                        // BIND_SYSC(sys_memory_...),                  //358 (0x166)
	null_func,                                                        // BIND_SYSC(sys_memory_...),                  //359 (0x167)
	null_func,                                                        // BIND_SYSC(sys_memory_...),                  //360 (0x168)
	NULL_FUNC(sys_memory_allocate_from_container_colored),            // 361 (0x169)
	BIND_SYSC(sys_mmapper_allocate_shared_memory_from_container),     // 362 (0x16A)
	null_func,                                                        // BIND_SYSC(sys_mmapper_...),                 //363 (0x16B)
	null_func,                                                        // BIND_SYSC(sys_mmapper_...),                 //364 (0x16C)
	uns_func, uns_func,                                               // 366 (0x16E)  UNS
	BIND_SYSC(sys_uart_initialize),                                   // 367 (0x16F)  ROOT
	BIND_SYSC(sys_uart_receive),                                      // 368 (0x170)  ROOT
	BIND_SYSC(sys_uart_send),                                         // 369 (0x171)  ROOT
	BIND_SYSC(sys_uart_get_params),                                   // 370 (0x172)  ROOT
	uns_func,                                                         // 371 (0x173)  UNS
	BIND_SYSC(_sys_game_watchdog_start),                              // 372 (0x174)
	BIND_SYSC(_sys_game_watchdog_stop),                               // 373 (0x175)
	BIND_SYSC(_sys_game_watchdog_clear),                              // 374 (0x176)
	BIND_SYSC(_sys_game_set_system_sw_version),                       // 375 (0x177)  ROOT
	BIND_SYSC(_sys_game_get_system_sw_version),                       // 376 (0x178)  ROOT
	BIND_SYSC(sys_sm_set_shop_mode),                                  // 377 (0x179)  ROOT
	BIND_SYSC(sys_sm_get_ext_event2),                                 // 378 (0x17A)  ROOT
	BIND_SYSC(sys_sm_shutdown),                                       // 379 (0x17B)  ROOT
	BIND_SYSC(sys_sm_get_params),                                     // 380 (0x17C)  DBG
	NULL_FUNC(sys_sm_get_inter_lpar_parameter),                       // 381 (0x17D)  ROOT
	NULL_FUNC(sys_sm_initialize),                                     // 382 (0x17E)  ROOT
	NULL_FUNC(sys_game_get_temperature),                              // 383 (0x17F)  ROOT
	NULL_FUNC(sys_sm_get_tzpb),                                       // 384 (0x180)  ROOT
	NULL_FUNC(sys_sm_request_led),                                    // 385 (0x181)  ROOT
	BIND_SYSC(sys_sm_control_led),                                    // 386 (0x182)  ROOT
	NULL_FUNC(sys_sm_get_system_info),                                // 387 (0x183)  DBG
	BIND_SYSC(sys_sm_ring_buzzer2),                                   // 388 (0x184)  ROOT
	NULL_FUNC(sys_sm_set_fan_policy),                                 // 389 (0x185)  PM
	NULL_FUNC(sys_sm_request_error_log),                              // 390 (0x186)  ROOT
	NULL_FUNC(sys_sm_request_be_count),                               // 391 (0x187)  ROOT
	BIND_SYSC(sys_sm_ring_buzzer),                                    // 392 (0x188)  ROOT
	NULL_FUNC(sys_sm_get_hw_config),                                  // 393 (0x189)  ROOT
	NULL_FUNC(sys_sm_request_scversion),                              // 394 (0x18A)  ROOT
	NULL_FUNC(sys_sm_request_system_event_log),                       // 395 (0x18B)  PM
	NULL_FUNC(sys_sm_set_rtc_alarm),                                  // 396 (0x18C)  ROOT
	NULL_FUNC(sys_sm_get_rtc_alarm),                                  // 397 (0x18D)  ROOT
	BIND_SYSC(sys_console_write),                                     // 398 (0x18E)  ROOT
	uns_func,                                                         // 399 (0x18F)  UNS
	null_func,                                                        // BIND_SYSC(sys_sm_...),                      //400 (0x190)  PM
	null_func,                                                        // BIND_SYSC(sys_sm_...),                      //401 (0x191)  ROOT
	BIND_SYSC(sys_tty_read),                                          // 402 (0x192)
	BIND_SYSC(sys_tty_write),                                         // 403 (0x193)
	null_func,                                                        // BIND_SYSC(sys_...),                         //404 (0x194)  ROOT
	null_func,                                                        // BIND_SYSC(sys_...),                         //405 (0x195)  PM
	null_func,                                                        // BIND_SYSC(sys_...),                         //406 (0x196)  PM
	null_func,                                                        // BIND_SYSC(sys_...),                         //407 (0x197)  PM
	NULL_FUNC(sys_sm_get_tzpb),                                       // 408 (0x198)  PM
	NULL_FUNC(sys_sm_get_fan_policy),                                 // 409 (0x199)  PM
	BIND_SYSC(_sys_game_board_storage_read),                          // 410 (0x19A)
	BIND_SYSC(_sys_game_board_storage_write),                         // 411 (0x19B)
	BIND_SYSC(_sys_game_get_rtc_status),                              // 412 (0x19C)
	null_func,                                                        // BIND_SYSC(sys_...),                         //413 (0x19D)  ROOT
	null_func,                                                        // BIND_SYSC(sys_...),                         //414 (0x19E)  ROOT
	null_func,                                                        // BIND_SYSC(sys_...),                         //415 (0x19F)  ROOT

	uns_func, uns_func, uns_func, uns_func,                                                             // 416-419  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 420-429  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 430-439  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 440-449  UNS

	BIND_SYSC(sys_overlay_load_module),                    // 450 (0x1C2)
	BIND_SYSC(sys_overlay_unload_module),                  // 451 (0x1C3)
	NULL_FUNC(sys_overlay_get_module_list),                // 452 (0x1C4)
	NULL_FUNC(sys_overlay_get_module_info),                // 453 (0x1C5)
	BIND_SYSC(sys_overlay_load_module_by_fd),              // 454 (0x1C6)
	NULL_FUNC(sys_overlay_get_module_info2),               // 455 (0x1C7)
	NULL_FUNC(sys_overlay_get_sdk_version),                // 456 (0x1C8)
	NULL_FUNC(sys_overlay_get_module_dbg_info),            // 457 (0x1C9)
	NULL_FUNC(sys_overlay_get_module_dbg_info),            // 458 (0x1CA)
	uns_func,                                              // 459 (0x1CB)  UNS
	NULL_FUNC(sys_prx_dbg_get_module_id_list),             // 460 (0x1CC)  ROOT
	BIND_SYSC(_sys_prx_get_module_id_by_address),          // 461 (0x1CD)
	uns_func,                                              // 462 (0x1CE)  DEX
	BIND_SYSC(_sys_prx_load_module_by_fd),                 // 463 (0x1CF)
	BIND_SYSC(_sys_prx_load_module_on_memcontainer_by_fd), // 464 (0x1D0)
	BIND_SYSC(_sys_prx_load_module_list),                  // 465 (0x1D1)
	BIND_SYSC(_sys_prx_load_module_list_on_memcontainer),  // 466 (0x1D2)
	BIND_SYSC(sys_prx_get_ppu_guid),                       // 467 (0x1D3)
	null_func,                                             // BIND_SYSC(sys_...),                         //468 (0x1D4) ROOT
	uns_func,                                              // 469 (0x1D5)  UNS
	NULL_FUNC(sys_npdrm_check_ekc),                        // 470 (0x1D6)  ROOT
	NULL_FUNC(sys_npdrm_regist_ekc),                       // 471 (0x1D7)  ROOT
	null_func,                                             // BIND_SYSC(sys_...),                         //472 (0x1D8)  ROOT
	null_func,                                             // BIND_SYSC(sys_...),                         //473 (0x1D9)
	null_func,                                             // BIND_SYSC(sys_...),                         //474 (0x1DA)
	null_func,                                             // BIND_SYSC(sys_...),                         //475 (0x1DB)  ROOT
	null_func,                                             // BIND_SYSC(sys_...),                         //476 (0x1DC)  ROOT

	uns_func, uns_func, uns_func, // 477-479  UNS

	BIND_SYSC(_sys_prx_load_module),                        // 480 (0x1E0)
	BIND_SYSC(_sys_prx_start_module),                       // 481 (0x1E1)
	BIND_SYSC(_sys_prx_stop_module),                        // 482 (0x1E2)
	BIND_SYSC(_sys_prx_unload_module),                      // 483 (0x1E3)
	BIND_SYSC(_sys_prx_register_module),                    // 484 (0x1E4)
	BIND_SYSC(_sys_prx_query_module),                       // 485 (0x1E5)
	BIND_SYSC(_sys_prx_register_library),                   // 486 (0x1E6)
	BIND_SYSC(_sys_prx_unregister_library),                 // 487 (0x1E7)
	BIND_SYSC(_sys_prx_link_library),                       // 488 (0x1E8)
	BIND_SYSC(_sys_prx_unlink_library),                     // 489 (0x1E9)
	BIND_SYSC(_sys_prx_query_library),                      // 490 (0x1EA)
	uns_func,                                               // 491 (0x1EB)  UNS
	NULL_FUNC(sys_prx_dbg_get_module_list),                 // 492 (0x1EC)  DBG
	NULL_FUNC(sys_prx_dbg_get_module_info),                 // 493 (0x1ED)  DBG
	BIND_SYSC(_sys_prx_get_module_list),                    // 494 (0x1EE)
	BIND_SYSC(_sys_prx_get_module_info),                    // 495 (0x1EF)
	BIND_SYSC(_sys_prx_get_module_id_by_name),              // 496 (0x1F0)
	BIND_SYSC(_sys_prx_load_module_on_memcontainer),        // 497 (0x1F1)
	BIND_SYSC(_sys_prx_start),                              // 498 (0x1F2)
	BIND_SYSC(_sys_prx_stop),                               // 499 (0x1F3)
	BIND_SYSC(sys_hid_manager_open),                        // 500 (0x1F4)
	NULL_FUNC(sys_hid_manager_close),                       // 501 (0x1F5)
	BIND_SYSC(sys_hid_manager_read),                        // 502 (0x1F6)  ROOT
	BIND_SYSC(sys_hid_manager_ioctl),                       // 503 (0x1F7)
	NULL_FUNC(sys_hid_manager_map_logical_id_to_port_id),   // 504 (0x1F8)  ROOT
	NULL_FUNC(sys_hid_manager_unmap_logical_id_to_port_id), // 505 (0x1F9)  ROOT
	BIND_SYSC(sys_hid_manager_add_hot_key_observer),        // 506 (0x1FA)  ROOT
	NULL_FUNC(sys_hid_manager_remove_hot_key_observer),     // 507 (0x1FB)  ROOT
	NULL_FUNC(sys_hid_manager_grab_focus),                  // 508 (0x1FC)  ROOT
	NULL_FUNC(sys_hid_manager_release_focus),               // 509 (0x1FD)  ROOT
	BIND_SYSC(sys_hid_manager_check_focus),                 // 510 (0x1FE)
	NULL_FUNC(sys_hid_manager_set_master_process),          // 511 (0x1FF)  ROOT
	BIND_SYSC(sys_hid_manager_is_process_permission_root),  // 512 (0x200)  ROOT
	BIND_SYSC(sys_hid_manager_513),                         // 513 (0x201)
	BIND_SYSC(sys_hid_manager_514),                         // 514 (0x202)
	uns_func,                                               // 515 (0x203)  UNS
	BIND_SYSC(sys_config_open),                             // 516 (0x204)
	BIND_SYSC(sys_config_close),                            // 517 (0x205)
	BIND_SYSC(sys_config_get_service_event),                // 518 (0x206)
	BIND_SYSC(sys_config_add_service_listener),             // 519 (0x207)
	BIND_SYSC(sys_config_remove_service_listener),          // 520 (0x208)
	BIND_SYSC(sys_config_register_service),                 // 521 (0x209)
	BIND_SYSC(sys_config_unregister_service),               // 522 (0x20A)
	BIND_SYSC(sys_config_get_io_event),                     // 523 (0x20B)
	BIND_SYSC(sys_config_register_io_error_listener),       // 524 (0x20C)
	BIND_SYSC(sys_config_unregister_io_error_listener),     // 525 (0x20D)
	uns_func, uns_func, uns_func, uns_func,                 // 526-529  UNS
	BIND_SYSC(sys_usbd_initialize),                         // 530 (0x212)
	BIND_SYSC(sys_usbd_finalize),                           // 531 (0x213)
	BIND_SYSC(sys_usbd_get_device_list),                    // 532 (0x214)
	BIND_SYSC(sys_usbd_get_descriptor_size),                // 533 (0x215)
	BIND_SYSC(sys_usbd_get_descriptor),                     // 534 (0x216)
	BIND_SYSC(sys_usbd_register_ldd),                       // 535 (0x217)
	BIND_SYSC(sys_usbd_unregister_ldd),                     // 536 (0x218)
	BIND_SYSC(sys_usbd_open_pipe),                          // 537 (0x219)
	BIND_SYSC(sys_usbd_open_default_pipe),                  // 538 (0x21A)
	BIND_SYSC(sys_usbd_close_pipe),                         // 539 (0x21B)
	BIND_SYSC(sys_usbd_receive_event),                      // 540 (0x21C)
	BIND_SYSC(sys_usbd_detect_event),                       // 541 (0x21D)
	BIND_SYSC(sys_usbd_attach),                             // 542 (0x21E)
	BIND_SYSC(sys_usbd_transfer_data),                      // 543 (0x21F)
	BIND_SYSC(sys_usbd_isochronous_transfer_data),          // 544 (0x220)
	BIND_SYSC(sys_usbd_get_transfer_status),                // 545 (0x221)
	BIND_SYSC(sys_usbd_get_isochronous_transfer_status),    // 546 (0x222)
	BIND_SYSC(sys_usbd_get_device_location),                // 547 (0x223)
	BIND_SYSC(sys_usbd_send_event),                         // 548 (0x224)
	BIND_SYSC(sys_usbd_event_port_send),                    // 549 (0x225)
	BIND_SYSC(sys_usbd_allocate_memory),                    // 550 (0x226)
	BIND_SYSC(sys_usbd_free_memory),                        // 551 (0x227)
	null_func,                                              // BIND_SYSC(sys_usbd_...),                    //552 (0x228)
	null_func,                                              // BIND_SYSC(sys_usbd_...),                    //553 (0x229)
	null_func,                                              // BIND_SYSC(sys_usbd_...),                    //554 (0x22A)
	null_func,                                              // BIND_SYSC(sys_usbd_...),                    //555 (0x22B)
	BIND_SYSC(sys_usbd_get_device_speed),                   // 556 (0x22C)
	null_func,                                              // BIND_SYSC(sys_usbd_...),                    //557 (0x22D)
	BIND_SYSC(sys_usbd_unregister_extra_ldd),               // 558 (0x22E)
	BIND_SYSC(sys_usbd_register_extra_ldd),                 // 559 (0x22F)
	null_func,                                              // BIND_SYSC(sys_...),                         //560 (0x230)  ROOT
	null_func,                                              // BIND_SYSC(sys_...),                         //561 (0x231)  ROOT
	null_func,                                              // BIND_SYSC(sys_...),                         //562 (0x232)  ROOT
	null_func,                                              // BIND_SYSC(sys_...),                         //563 (0x233)
	null_func,                                              // BIND_SYSC(sys_...),                         //564 (0x234)
	null_func,                                              // BIND_SYSC(sys_...),                         //565 (0x235)
	null_func,                                              // BIND_SYSC(sys_...),                         //566 (0x236)
	null_func,                                              // BIND_SYSC(sys_...),                         //567 (0x237)
	null_func,                                              // BIND_SYSC(sys_...),                         //568 (0x238)
	null_func,                                              // BIND_SYSC(sys_...),                         //569 (0x239)
	NULL_FUNC(sys_pad_ldd_register_controller),             // 570 (0x23A)
	NULL_FUNC(sys_pad_ldd_unregister_controller),           // 571 (0x23B)
	NULL_FUNC(sys_pad_ldd_data_insert),                     // 572 (0x23C)
	NULL_FUNC(sys_pad_dbg_ldd_set_data_insert_mode),        // 573 (0x23D)
	NULL_FUNC(sys_pad_ldd_register_controller),             // 574 (0x23E)
	NULL_FUNC(sys_pad_ldd_get_port_no),                     // 575 (0x23F)
	uns_func,                                               // 576 (0x240)  UNS
	null_func,                                              // BIND_SYSC(sys_pad_manager_...),             //577 (0x241)  ROOT  PM
	null_func,                                              // BIND_SYSC(sys_bluetooth_...),               //578 (0x242)
	null_func,                                              // BIND_SYSC(sys_bluetooth_aud_serial_...),    //579 (0x243)
	null_func,                                              // BIND_SYSC(sys_bluetooth_...),               //580 (0x244)  ROOT
	null_func,                                              // BIND_SYSC(sys_bluetooth_...),               //581 (0x245)  ROOT
	null_func,                                              // BIND_SYSC(sys_bluetooth_...),               //582 (0x246)  ROOT
	NULL_FUNC(sys_bt_read_firmware_version),                // 583 (0x247)  ROOT
	NULL_FUNC(sys_bt_complete_wake_on_host),                // 584 (0x248)  ROOT
	NULL_FUNC(sys_bt_disable_bluetooth),                    // 585 (0x249)
	NULL_FUNC(sys_bt_enable_bluetooth),                     // 586 (0x24A)
	NULL_FUNC(sys_bt_bccmd),                                // 587 (0x24B)  ROOT
	NULL_FUNC(sys_bt_read_hq),                              // 588 (0x24C)
	NULL_FUNC(sys_bt_hid_get_remote_status),                // 589 (0x24D)
	NULL_FUNC(sys_bt_register_controller),                  // 590 (0x24E)  ROOT
	NULL_FUNC(sys_bt_clear_registered_contoller),           // 591 (0x24F)
	NULL_FUNC(sys_bt_connect_accept_controller),            // 592 (0x250)
	NULL_FUNC(sys_bt_get_local_bdaddress),                  // 593 (0x251)  ROOT
	NULL_FUNC(sys_bt_hid_get_data),                         // 594 (0x252)
	NULL_FUNC(sys_bt_hid_set_report),                       // 595 (0x253)
	NULL_FUNC(sys_bt_sched_log),                            // 596 (0x254)
	NULL_FUNC(sys_bt_cancel_connect_accept_controller),     // 597 (0x255)
	null_func,                                              // BIND_SYSC(sys_bluetooth_...),               //598 (0x256)  ROOT
	null_func,                                              // BIND_SYSC(sys_bluetooth_...),               //599 (0x257)  ROOT
	BIND_SYSC(sys_storage_open),                            // 600 (0x258)  ROOT
	BIND_SYSC(sys_storage_close),                           // 601 (0x259)
	BIND_SYSC(sys_storage_read),                            // 602 (0x25A)
	BIND_SYSC(sys_storage_write),                           // 603 (0x25B)
	BIND_SYSC(sys_storage_send_device_command),             // 604 (0x25C)
	BIND_SYSC(sys_storage_async_configure),                 // 605 (0x25D)
	BIND_SYSC(sys_storage_async_read),                      // 606 (0x25E)
	BIND_SYSC(sys_storage_async_write),                     // 607 (0x25F)
	BIND_SYSC(sys_storage_async_cancel),                    // 608 (0x260)
	BIND_SYSC(sys_storage_get_device_info),                 // 609 (0x261)  ROOT
	BIND_SYSC(sys_storage_get_device_config),               // 610 (0x262)  ROOT
	BIND_SYSC(sys_storage_report_devices),                  // 611 (0x263)  ROOT
	BIND_SYSC(sys_storage_configure_medium_event),          // 612 (0x264)  ROOT
	BIND_SYSC(sys_storage_set_medium_polling_interval),     // 613 (0x265)
	BIND_SYSC(sys_storage_create_region),                   // 614 (0x266)
	BIND_SYSC(sys_storage_delete_region),                   // 615 (0x267)
	BIND_SYSC(sys_storage_execute_device_command),          // 616 (0x268)
	BIND_SYSC(sys_storage_check_region_acl),                // 617 (0x269)
	BIND_SYSC(sys_storage_set_region_acl),                  // 618 (0x26A)
	BIND_SYSC(sys_storage_async_send_device_command),       // 619 (0x26B)
	null_func,                                              // BIND_SYSC(sys_...),                         //620 (0x26C)  ROOT
	BIND_SYSC(sys_gamepad_ycon_if),                         // 621 (0x26D)
	BIND_SYSC(sys_storage_get_region_offset),               // 622 (0x26E)
	BIND_SYSC(sys_storage_set_emulated_speed),              // 623 (0x26F)
	BIND_SYSC(sys_io_buffer_create),                        // 624 (0x270)
	BIND_SYSC(sys_io_buffer_destroy),                       // 625 (0x271)
	BIND_SYSC(sys_io_buffer_allocate),                      // 626 (0x272)
	BIND_SYSC(sys_io_buffer_free),                          // 627 (0x273)
	uns_func, uns_func,                                     // 629 (0x275)  UNS
	BIND_SYSC(sys_gpio_set),                                // 630 (0x276)
	BIND_SYSC(sys_gpio_get),                                // 631 (0x277)
	uns_func,                                               // 632 (0x278)  UNS
	NULL_FUNC(sys_fsw_connect_event),                       // 633 (0x279)
	NULL_FUNC(sys_fsw_disconnect_event),                    // 634 (0x27A)
	BIND_SYSC(sys_btsetting_if),                            // 635 (0x27B)
	null_func,                                              // BIND_SYSC(sys_...),                         //636 (0x27C)
	null_func,                                              // BIND_SYSC(sys_...),                         //637 (0x27D)
	null_func,                                              // BIND_SYSC(sys_...),                         //638 (0x27E)

	null_func,                                  // BIND_SYSC(sys...),                          //639  DEPRECATED
	NULL_FUNC(sys_usbbtaudio_initialize),       // 640  DEPRECATED
	NULL_FUNC(sys_usbbtaudio_finalize),         // 641  DEPRECATED
	NULL_FUNC(sys_usbbtaudio_discovery),        // 642  DEPRECATED
	NULL_FUNC(sys_usbbtaudio_cancel_discovery), // 643  DEPRECATED
	NULL_FUNC(sys_usbbtaudio_pairing),          // 644  DEPRECATED
	NULL_FUNC(sys_usbbtaudio_set_passkey),      // 645  DEPRECATED
	NULL_FUNC(sys_usbbtaudio_connect),          // 646  DEPRECATED
	NULL_FUNC(sys_usbbtaudio_disconnect),       // 647  DEPRECATED
	null_func,                                  // BIND_SYSC(sys_...),                         //648  DEPRECATED
	null_func,                                  // BIND_SYSC(sys_...),                         //649  DEPRECATED

	BIND_SYSC(sys_rsxaudio_initialize),             // 650 (0x28A)
	BIND_SYSC(sys_rsxaudio_finalize),               // 651 (0x28B)
	BIND_SYSC(sys_rsxaudio_import_shared_memory),   // 652 (0x28C)
	BIND_SYSC(sys_rsxaudio_unimport_shared_memory), // 653 (0x28D)
	BIND_SYSC(sys_rsxaudio_create_connection),      // 654 (0x28E)
	BIND_SYSC(sys_rsxaudio_close_connection),       // 655 (0x28F)
	BIND_SYSC(sys_rsxaudio_prepare_process),        // 656 (0x290)
	BIND_SYSC(sys_rsxaudio_start_process),          // 657 (0x291)
	BIND_SYSC(sys_rsxaudio_stop_process),           // 658 (0x292)
	BIND_SYSC(sys_rsxaudio_get_dma_param),          // 659 (0x293)

	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 660-665  UNS

	BIND_SYSC(sys_rsx_device_open),        // 666 (0x29A)
	BIND_SYSC(sys_rsx_device_close),       // 667 (0x29B)
	BIND_SYSC(sys_rsx_memory_allocate),    // 668 (0x29C)
	BIND_SYSC(sys_rsx_memory_free),        // 669 (0x29D)
	BIND_SYSC(sys_rsx_context_allocate),   // 670 (0x29E)
	BIND_SYSC(sys_rsx_context_free),       // 671 (0x29F)
	BIND_SYSC(sys_rsx_context_iomap),      // 672 (0x2A0)
	BIND_SYSC(sys_rsx_context_iounmap),    // 673 (0x2A1)
	BIND_SYSC(sys_rsx_context_attribute),  // 674 (0x2A2)
	BIND_SYSC(sys_rsx_device_map),         // 675 (0x2A3)
	BIND_SYSC(sys_rsx_device_unmap),       // 676 (0x2A4)
	BIND_SYSC(sys_rsx_attribute),          // 677 (0x2A5)
	null_func,                             // BIND_SYSC(sys_...),                         //678 (0x2A6)
	null_func,                             // BIND_SYSC(sys_...),                         //679 (0x2A7)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //680 (0x2A8)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //681 (0x2A9)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //682 (0x2AA)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //683 (0x2AB)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //684 (0x2AC)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //685 (0x2AD)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //686 (0x2AE)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //687 (0x2AF)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //688 (0x2B0)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //689 (0x2B1)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //690 (0x2B2)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //691 (0x2B3)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //692 (0x2B4)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //693 (0x2B5)  ROOT
	null_func,                             // BIND_SYSC(sys_...),                         //694 (0x2B6)  DEPRECATED
	null_func,                             // BIND_SYSC(sys_...),                         //695 (0x2B7)  DEPRECATED
	null_func,                             // BIND_SYSC(sys_...),                         //696 (0x2B8)  ROOT
	uns_func,                              // BIND_SYSC(sys_...),                          //697 (0x2B9)  UNS
	uns_func,                              // BIND_SYSC(sys_...),                          //698 (0x2BA)  UNS
	BIND_SYSC(sys_bdemu_send_command),     // 699 (0x2BB)
	BIND_SYSC(sys_net_bnet_accept),        // 700 (0x2BC)
	BIND_SYSC(sys_net_bnet_bind),          // 701 (0x2BD)
	BIND_SYSC(sys_net_bnet_connect),       // 702 (0x2BE)
	BIND_SYSC(sys_net_bnet_getpeername),   // 703 (0x2BF)
	BIND_SYSC(sys_net_bnet_getsockname),   // 704 (0x2C0)
	BIND_SYSC(sys_net_bnet_getsockopt),    // 705 (0x2C1)
	BIND_SYSC(sys_net_bnet_listen),        // 706 (0x2C2)
	BIND_SYSC(sys_net_bnet_recvfrom),      // 707 (0x2C3)
	BIND_SYSC(sys_net_bnet_recvmsg),       // 708 (0x2C4)
	BIND_SYSC(sys_net_bnet_sendmsg),       // 709 (0x2C5)
	BIND_SYSC(sys_net_bnet_sendto),        // 710 (0x2C6)
	BIND_SYSC(sys_net_bnet_setsockopt),    // 711 (0x2C7)
	BIND_SYSC(sys_net_bnet_shutdown),      // 712 (0x2C8)
	BIND_SYSC(sys_net_bnet_socket),        // 713 (0x2C9)
	BIND_SYSC(sys_net_bnet_close),         // 714 (0x2CA)
	BIND_SYSC(sys_net_bnet_poll),          // 715 (0x2CB)
	BIND_SYSC(sys_net_bnet_select),        // 716 (0x2CC)
	BIND_SYSC(_sys_net_open_dump),         // 717 (0x2CD)
	BIND_SYSC(_sys_net_read_dump),         // 718 (0x2CE)
	BIND_SYSC(_sys_net_close_dump),        // 719 (0x2CF)
	BIND_SYSC(_sys_net_write_dump),        // 720 (0x2D0)
	BIND_SYSC(sys_net_abort),              // 721 (0x2D1)
	BIND_SYSC(sys_net_infoctl),            // 722 (0x2D2)
	BIND_SYSC(sys_net_control),            // 723 (0x2D3)
	BIND_SYSC(sys_net_bnet_ioctl),         // 724 (0x2D4)
	BIND_SYSC(sys_net_bnet_sysctl),        // 725 (0x2D5)
	BIND_SYSC(sys_net_eurus_post_command), // 726 (0x2D6)

	uns_func, uns_func, uns_func,                                                                       // 727-729  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 730-739  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 740-749  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 750-759  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 760-769  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 770-779  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 780-789  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 790-799  UNS

	BIND_SYSC(sys_fs_test),                // 800 (0x320)
	BIND_SYSC(sys_fs_open),                // 801 (0x321)
	BIND_SYSC(sys_fs_read),                // 802 (0x322)
	BIND_SYSC(sys_fs_write),               // 803 (0x323)
	BIND_SYSC(sys_fs_close),               // 804 (0x324)
	BIND_SYSC(sys_fs_opendir),             // 805 (0x325)
	BIND_SYSC(sys_fs_readdir),             // 806 (0x326)
	BIND_SYSC(sys_fs_closedir),            // 807 (0x327)
	BIND_SYSC(sys_fs_stat),                // 808 (0x328)
	BIND_SYSC(sys_fs_fstat),               // 809 (0x329)
	BIND_SYSC(sys_fs_link),                // 810 (0x32A)
	BIND_SYSC(sys_fs_mkdir),               // 811 (0x32B)
	BIND_SYSC(sys_fs_rename),              // 812 (0x32C)
	BIND_SYSC(sys_fs_rmdir),               // 813 (0x32D)
	BIND_SYSC(sys_fs_unlink),              // 814 (0x32E)
	BIND_SYSC(sys_fs_utime),               // 815 (0x32F)
	BIND_SYSC(sys_fs_access),              // 816 (0x330)
	BIND_SYSC(sys_fs_fcntl),               // 817 (0x331)
	BIND_SYSC(sys_fs_lseek),               // 818 (0x332)
	BIND_SYSC(sys_fs_fdatasync),           // 819 (0x333)
	BIND_SYSC(sys_fs_fsync),               // 820 (0x334)
	BIND_SYSC(sys_fs_fget_block_size),     // 821 (0x335)
	BIND_SYSC(sys_fs_get_block_size),      // 822 (0x336)
	BIND_SYSC(sys_fs_acl_read),            // 823 (0x337)
	BIND_SYSC(sys_fs_acl_write),           // 824 (0x338)
	BIND_SYSC(sys_fs_lsn_get_cda_size),    // 825 (0x339)
	BIND_SYSC(sys_fs_lsn_get_cda),         // 826 (0x33A)
	BIND_SYSC(sys_fs_lsn_lock),            // 827 (0x33B)
	BIND_SYSC(sys_fs_lsn_unlock),          // 828 (0x33C)
	BIND_SYSC(sys_fs_lsn_read),            // 829 (0x33D)
	BIND_SYSC(sys_fs_lsn_write),           // 830 (0x33E)
	BIND_SYSC(sys_fs_truncate),            // 831 (0x33F)
	BIND_SYSC(sys_fs_ftruncate),           // 832 (0x340)
	BIND_SYSC(sys_fs_symbolic_link),       // 833 (0x341)
	BIND_SYSC(sys_fs_chmod),               // 834 (0x342)
	BIND_SYSC(sys_fs_chown),               // 835 (0x343)
	BIND_SYSC(sys_fs_newfs),               // 836 (0x344)
	BIND_SYSC(sys_fs_mount),               // 837 (0x345)
	BIND_SYSC(sys_fs_unmount),             // 838 (0x346)
	NULL_FUNC(sys_fs_sync),                // 839 (0x347)
	BIND_SYSC(sys_fs_disk_free),           // 840 (0x348)
	BIND_SYSC(sys_fs_get_mount_info_size), // 841 (0x349)
	BIND_SYSC(sys_fs_get_mount_info),      // 842 (0x34A)
	NULL_FUNC(sys_fs_get_fs_info_size),    // 843 (0x34B)
	NULL_FUNC(sys_fs_get_fs_info),         // 844 (0x34C)
	BIND_SYSC(sys_fs_mapped_allocate),     // 845 (0x34D)
	BIND_SYSC(sys_fs_mapped_free),         // 846 (0x34E)
	BIND_SYSC(sys_fs_truncate2),           // 847 (0x34F)

	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 848-853  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 854-859  UNS

	NULL_FUNC(sys_ss_get_cache_of_analog_sunset_flag),           // 860 (0x35C)  AUTHID
	NULL_FUNC(sys_ss_protected_file_db),                         // 861  ROOT
	BIND_SYSC(sys_ss_virtual_trm_manager),                       // 862  ROOT
	BIND_SYSC(sys_ss_update_manager),                            // 863  ROOT
	NULL_FUNC(sys_ss_sec_hw_framework),                          // 864  DBG
	BIND_SYSC(sys_ss_random_number_generator),                   // 865 (0x361)
	BIND_SYSC(sys_ss_secure_rtc),                                // 866  ROOT
	BIND_SYSC(sys_ss_appliance_info_manager),                    // 867  ROOT
	BIND_SYSC(sys_ss_individual_info_manager),                   // 868  ROOT / DBG  AUTHID
	NULL_FUNC(sys_ss_factory_data_manager),                      // 869  ROOT
	BIND_SYSC(sys_ss_get_console_id),                            // 870 (0x366)
	BIND_SYSC(sys_ss_access_control_engine),                     // 871 (0x367)  DBG
	BIND_SYSC(sys_ss_get_open_psid),                             // 872 (0x368)
	BIND_SYSC(sys_ss_get_cache_of_product_mode),                 // 873 (0x369)
	BIND_SYSC(sys_ss_get_cache_of_flash_ext_flag),               // 874 (0x36A)
	BIND_SYSC(sys_ss_get_boot_device),                           // 875 (0x36B)
	NULL_FUNC(sys_ss_disc_access_control),                       // 876 (0x36C)
	null_func,                                                   // BIND_SYSC(sys_ss_~utoken_if),              //877 (0x36D)  ROOT
	NULL_FUNC(sys_ss_ad_sign),                                   // 878 (0x36E)
	NULL_FUNC(sys_ss_media_id),                                  // 879 (0x36F)
	NULL_FUNC(sys_deci3_open),                                   // 880 (0x370)
	NULL_FUNC(sys_deci3_create_event_path),                      // 881 (0x371)
	NULL_FUNC(sys_deci3_close),                                  // 882 (0x372)
	NULL_FUNC(sys_deci3_send),                                   // 883 (0x373)
	NULL_FUNC(sys_deci3_receive),                                // 884 (0x374)
	NULL_FUNC(sys_deci3_open2),                                  // 885 (0x375)
	uns_func, uns_func, uns_func,                                // 886-888  UNS
	null_func,                                                   // BIND_SYSC(sys_...),                         //889 (0x379)  ROOT
	NULL_FUNC(sys_deci3_initialize),                             // 890 (0x37A)
	NULL_FUNC(sys_deci3_terminate),                              // 891 (0x37B)
	NULL_FUNC(sys_deci3_debug_mode),                             // 892 (0x37C)
	NULL_FUNC(sys_deci3_show_status),                            // 893 (0x37D)
	NULL_FUNC(sys_deci3_echo_test),                              // 894 (0x37E)
	NULL_FUNC(sys_deci3_send_dcmp_packet),                       // 895 (0x37F)
	NULL_FUNC(sys_deci3_dump_cp_register),                       // 896 (0x380)
	NULL_FUNC(sys_deci3_dump_cp_buffer),                         // 897 (0x381)
	uns_func,                                                    // 898 (0x382)  UNS
	NULL_FUNC(sys_deci3_test),                                   // 899 (0x383)
	NULL_FUNC(sys_dbg_stop_processes),                           // 900 (0x384)
	NULL_FUNC(sys_dbg_continue_processes),                       // 901 (0x385)
	NULL_FUNC(sys_dbg_stop_threads),                             // 902 (0x386)
	NULL_FUNC(sys_dbg_continue_threads),                         // 903 (0x387)
	BIND_SYSC(sys_dbg_read_process_memory),                      // 904 (0x388)
	BIND_SYSC(sys_dbg_write_process_memory),                     // 905 (0x389)
	NULL_FUNC(sys_dbg_read_thread_register),                     // 906 (0x38A)
	NULL_FUNC(sys_dbg_write_thread_register),                    // 907 (0x38B)
	NULL_FUNC(sys_dbg_get_process_list),                         // 908 (0x38C)
	NULL_FUNC(sys_dbg_get_thread_list),                          // 909 (0x38D)
	NULL_FUNC(sys_dbg_get_thread_info),                          // 910 (0x38E)
	NULL_FUNC(sys_dbg_spu_thread_read_from_ls),                  // 911 (0x38F)
	NULL_FUNC(sys_dbg_spu_thread_write_to_ls),                   // 912 (0x390)
	NULL_FUNC(sys_dbg_kill_process),                             // 913 (0x391)
	NULL_FUNC(sys_dbg_get_process_info),                         // 914 (0x392)
	NULL_FUNC(sys_dbg_set_run_control_bit_to_spu),               // 915 (0x393)
	NULL_FUNC(sys_dbg_spu_thread_get_exception_cause),           // 916 (0x394)
	NULL_FUNC(sys_dbg_create_kernel_event_queue),                // 917 (0x395)
	NULL_FUNC(sys_dbg_read_kernel_event_queue),                  // 918 (0x396)
	NULL_FUNC(sys_dbg_destroy_kernel_event_queue),               // 919 (0x397)
	NULL_FUNC(sys_dbg_get_process_event_ctrl_flag),              // 920 (0x398)
	NULL_FUNC(sys_dbg_set_process_event_cntl_flag),              // 921 (0x399)
	NULL_FUNC(sys_dbg_get_spu_thread_group_event_cntl_flag),     // 922 (0x39A)
	NULL_FUNC(sys_dbg_set_spu_thread_group_event_cntl_flag),     // 923 (0x39B)
	NULL_FUNC(sys_dbg_get_module_list),                          // 924 (0x39C)
	NULL_FUNC(sys_dbg_get_raw_spu_list),                         // 925 (0x39D)
	NULL_FUNC(sys_dbg_initialize_scratch_executable_area),       // 926 (0x39E)
	NULL_FUNC(sys_dbg_terminate_scratch_executable_area),        // 927 (0x3A0)
	NULL_FUNC(sys_dbg_initialize_scratch_data_area),             // 928 (0x3A1)
	NULL_FUNC(sys_dbg_terminate_scratch_data_area),              // 929 (0x3A2)
	NULL_FUNC(sys_dbg_get_user_memory_stat),                     // 930 (0x3A3)
	NULL_FUNC(sys_dbg_get_shared_memory_attribute_list),         // 931 (0x3A4)
	NULL_FUNC(sys_dbg_get_mutex_list),                           // 932 (0x3A4)
	NULL_FUNC(sys_dbg_get_mutex_information),                    // 933 (0x3A5)
	NULL_FUNC(sys_dbg_get_cond_list),                            // 934 (0x3A6)
	NULL_FUNC(sys_dbg_get_cond_information),                     // 935 (0x3A7)
	NULL_FUNC(sys_dbg_get_rwlock_list),                          // 936 (0x3A8)
	NULL_FUNC(sys_dbg_get_rwlock_information),                   // 937 (0x3A9)
	NULL_FUNC(sys_dbg_get_lwmutex_list),                         // 938 (0x3AA)
	NULL_FUNC(sys_dbg_get_address_from_dabr),                    // 939 (0x3AB)
	NULL_FUNC(sys_dbg_set_address_to_dabr),                      // 940 (0x3AC)
	NULL_FUNC(sys_dbg_get_lwmutex_information),                  // 941 (0x3AD)
	NULL_FUNC(sys_dbg_get_event_queue_list),                     // 942 (0x3AE)
	NULL_FUNC(sys_dbg_get_event_queue_information),              // 943 (0x3AF)
	NULL_FUNC(sys_dbg_initialize_ppu_exception_handler),         // 944 (0x3B0)
	NULL_FUNC(sys_dbg_finalize_ppu_exception_handler),           // 945 (0x3B1)  DBG
	NULL_FUNC(sys_dbg_get_semaphore_list),                       // 946 (0x3B2)
	NULL_FUNC(sys_dbg_get_semaphore_information),                // 947 (0x3B3)
	NULL_FUNC(sys_dbg_get_kernel_thread_list),                   // 948 (0x3B4)
	NULL_FUNC(sys_dbg_get_kernel_thread_info),                   // 949 (0x3B5)
	NULL_FUNC(sys_dbg_get_lwcond_list),                          // 950 (0x3B6)
	NULL_FUNC(sys_dbg_get_lwcond_information),                   // 951 (0x3B7)
	NULL_FUNC(sys_dbg_create_scratch_data_area_ext),             // 952 (0x3B8)
	NULL_FUNC(sys_dbg_vm_get_page_information),                  // 953 (0x3B9)
	NULL_FUNC(sys_dbg_vm_get_info),                              // 954 (0x3BA)
	NULL_FUNC(sys_dbg_enable_floating_point_enabled_exception),  // 955 (0x3BB)
	NULL_FUNC(sys_dbg_disable_floating_point_enabled_exception), // 956 (0x3BC)
	NULL_FUNC(sys_dbg_get_process_memory_container_information), // 957 (0x3BD)
	uns_func,                                                    // 958 (0x3BE)  UNS
	null_func,                                                   // BIND_SYSC(sys_dbg_...),                     //959 (0x3BF)
	NULL_FUNC(sys_control_performance_monitor),                  // 960 (0x3C0)
	NULL_FUNC(sys_performance_monitor_hidden),                   // 961 (0x3C1)
	NULL_FUNC(sys_performance_monitor_bookmark),                 // 962 (0x3C2)
	NULL_FUNC(sys_lv1_pc_trace_create),                          // 963 (0x3C3)
	NULL_FUNC(sys_lv1_pc_trace_start),                           // 964 (0x3C4)
	NULL_FUNC(sys_lv1_pc_trace_stop),                            // 965 (0x3C5)
	NULL_FUNC(sys_lv1_pc_trace_get_status),                      // 966 (0x3C6)
	NULL_FUNC(sys_lv1_pc_trace_destroy),                         // 967 (0x3C7)
	NULL_FUNC(sys_rsx_trace_ioctl),                              // 968 (0x3C8)
	null_func,                                                   // BIND_SYSC(sys_dbg_...),                     //969 (0x3C9)
	NULL_FUNC(sys_dbg_get_event_flag_list),                      // 970 (0x3CA)
	NULL_FUNC(sys_dbg_get_event_flag_information),               // 971 (0x3CB)
	null_func,                                                   // BIND_SYSC(sys_dbg_...),                     //972 (0x3CC)
	uns_func,                                                    // BIND_SYSC(sys_dbg_...),                      //973 (0x3CD)
	null_func,                                                   // BIND_SYSC(sys_dbg_...),                     //974 (0x3CE)
	NULL_FUNC(sys_dbg_read_spu_thread_context2),                 // 975 (0x3CF)
	BIND_SYSC(sys_crypto_engine_create),                         // 976 (0x3D0)
	BIND_SYSC(sys_crypto_engine_destroy),                        // 977 (0x3D1)
	NULL_FUNC(sys_crypto_engine_hasher_prepare),                 // 978 (0x3D2)  ROOT
	NULL_FUNC(sys_crypto_engine_hasher_run),                     // 979 (0x3D3)
	NULL_FUNC(sys_crypto_engine_hasher_get_hash),                // 980 (0x3D4)
	NULL_FUNC(sys_crypto_engine_cipher_prepare),                 // 981 (0x3D5)  ROOT
	NULL_FUNC(sys_crypto_engine_cipher_run),                     // 982 (0x3D6)
	NULL_FUNC(sys_crypto_engine_cipher_get_hash),                // 983 (0x3D7)
	BIND_SYSC(sys_crypto_engine_random_generate),                // 984 (0x3D8)
	NULL_FUNC(sys_dbg_get_console_type),                         // 985 (0x3D9)  ROOT
	null_func,                                                   // BIND_SYSC(sys_dbg_...),                     //986 (0x3DA)  ROOT  DBG
	null_func,                                                   // BIND_SYSC(sys_dbg_...),                     //987 (0x3DB)  ROOT
	null_func,                                                   // BIND_SYSC(sys_dbg_..._ppu_exception_handler) //988 (0x3DC)
	null_func,                                                   // BIND_SYSC(sys_dbg_...),                     //989 (0x3DD)

	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 990-998  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 999-1007  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, // 1008-1016  UNS
	uns_func, uns_func, uns_func, uns_func, uns_func, uns_func, uns_func,                     // 1020-1023  UNS
};

#undef BIND_SYSC
#undef NULL_FUNC

// TODO: more enums
enum CellAdecError : u32;
enum CellAtracError : u32;
enum CellAtracMultiError : u32;
enum CellAudioError : u32;
enum CellAudioOutError : u32;
enum CellAudioInError : u32;

enum CellVideoOutError : u32;

enum CellSpursCoreError : u32;
enum CellSpursPolicyModuleError : u32;
enum CellSpursTaskError : u32;
enum CellSpursJobError : u32;
enum CellSyncError : u32;

enum CellGameError : u32;
enum CellGameDataError : u32;
enum CellDiscGameError : u32;
enum CellHddGameError : u32;

enum SceNpTrophyError : u32;
enum SceNpError : u32;

template <u64 EnumMin, typename E>
constexpr auto formatter_of = std::make_pair(EnumMin, &fmt_class_string<E>::format);

const std::map<u64, void (*)(std::string&, u64)> s_error_codes_formatting_by_type{
	formatter_of<0x80610000, CellAdecError>,
	formatter_of<0x80612100, CellAdecError>,
	formatter_of<0x80610300, CellAtracError>,
	formatter_of<0x80610b00, CellAtracMultiError>,
	formatter_of<0x80310700, CellAudioError>,
	formatter_of<0x8002b240, CellAudioOutError>,
	formatter_of<0x8002b260, CellAudioInError>,
	formatter_of<0x8002b220, CellVideoOutError>,

	formatter_of<0x80410100, CellSyncError>,
	formatter_of<0x80410700, CellSpursCoreError>,
	formatter_of<0x80410800, CellSpursPolicyModuleError>,
	formatter_of<0x80410900, CellSpursTaskError>,
	formatter_of<0x80410A00, CellSpursJobError>,

	formatter_of<0x8002cb00, CellGameError>,
	formatter_of<0x8002b600, CellGameDataError>,
	formatter_of<0x8002bd00, CellDiscGameError>,
	formatter_of<0x8002ba00, CellHddGameError>,

	formatter_of<0x80022900, SceNpTrophyError>,
	formatter_of<0x80029500, SceNpError>,
};

template <>
void fmt_class_string<CellError>::format(std::string& out, u64 arg)
{
	// Test if can be formatted by this formatter
	const bool lv2_cell_error = (arg >> 8) == 0x800100u;

	if (!lv2_cell_error)
	{
		// Format by external enum formatters
		auto upper = s_error_codes_formatting_by_type.upper_bound(arg);

		if (upper == s_error_codes_formatting_by_type.begin())
		{
			// Format as unknown by another enum formatter
			upper->second(out, arg);
			return;
		}

		// Find the formatter whose base is the highest that is not more than arg
		const auto found = std::prev(upper);
		found->second(out, arg);
		return;
	}

	format_enum(out, arg, [](auto error)
		{
			switch (error)
			{
				STR_CASE(CELL_EAGAIN);
				STR_CASE(CELL_EINVAL);
				STR_CASE(CELL_ENOSYS);
				STR_CASE(CELL_ENOMEM);
				STR_CASE(CELL_ESRCH);
				STR_CASE(CELL_ENOENT);
				STR_CASE(CELL_ENOEXEC);
				STR_CASE(CELL_EDEADLK);
				STR_CASE(CELL_EPERM);
				STR_CASE(CELL_EBUSY);
				STR_CASE(CELL_ETIMEDOUT);
				STR_CASE(CELL_EABORT);
				STR_CASE(CELL_EFAULT);
				STR_CASE(CELL_ENOCHILD);
				STR_CASE(CELL_ESTAT);
				STR_CASE(CELL_EALIGN);
				STR_CASE(CELL_EKRESOURCE);
				STR_CASE(CELL_EISDIR);
				STR_CASE(CELL_ECANCELED);
				STR_CASE(CELL_EEXIST);
				STR_CASE(CELL_EISCONN);
				STR_CASE(CELL_ENOTCONN);
				STR_CASE(CELL_EAUTHFAIL);
				STR_CASE(CELL_ENOTMSELF);
				STR_CASE(CELL_ESYSVER);
				STR_CASE(CELL_EAUTHFATAL);
				STR_CASE(CELL_EDOM);
				STR_CASE(CELL_ERANGE);
				STR_CASE(CELL_EILSEQ);
				STR_CASE(CELL_EFPOS);
				STR_CASE(CELL_EINTR);
				STR_CASE(CELL_EFBIG);
				STR_CASE(CELL_EMLINK);
				STR_CASE(CELL_ENFILE);
				STR_CASE(CELL_ENOSPC);
				STR_CASE(CELL_ENOTTY);
				STR_CASE(CELL_EPIPE);
				STR_CASE(CELL_EROFS);
				STR_CASE(CELL_ESPIPE);
				STR_CASE(CELL_E2BIG);
				STR_CASE(CELL_EACCES);
				STR_CASE(CELL_EBADF);
				STR_CASE(CELL_EIO);
				STR_CASE(CELL_EMFILE);
				STR_CASE(CELL_ENODEV);
				STR_CASE(CELL_ENOTDIR);
				STR_CASE(CELL_ENXIO);
				STR_CASE(CELL_EXDEV);
				STR_CASE(CELL_EBADMSG);
				STR_CASE(CELL_EINPROGRESS);
				STR_CASE(CELL_EMSGSIZE);
				STR_CASE(CELL_ENAMETOOLONG);
				STR_CASE(CELL_ENOLCK);
				STR_CASE(CELL_ENOTEMPTY);
				STR_CASE(CELL_ENOTSUP);
				STR_CASE(CELL_EFSSPECIFIC);
				STR_CASE(CELL_EOVERFLOW);
				STR_CASE(CELL_ENOTMOUNTED);
				STR_CASE(CELL_ENOTSDATA);
				STR_CASE(CELL_ESDKVER);
				STR_CASE(CELL_ENOLICDISC);
				STR_CASE(CELL_ENOLICENT);
			}

			return unknown;
		});
}

stx::init_lock acquire_lock(stx::init_mutex& mtx, ppu_thread* ppu)
{
	if (!ppu)
	{
		ppu = ensure(cpu_thread::get_current<ppu_thread>());
	}

	return mtx.init([](int invoke_count, const stx::init_lock&, ppu_thread* ppu)
		{
			if (!invoke_count)
			{
				// Sleep before waiting on lock
				lv2_obj::sleep(*ppu);
			}
			else
			{
				// Wake up after acquistion or failure to acquire
				ppu->check_state();
			}
		},
		ppu);
}

stx::access_lock acquire_access_lock(stx::init_mutex& mtx, ppu_thread* ppu)
{
	if (!ppu)
	{
		ppu = ensure(cpu_thread::get_current<ppu_thread>());
	}

	// TODO: Check if needs to wait
	return mtx.access();
}

stx::reset_lock acquire_reset_lock(stx::init_mutex& mtx, ppu_thread* ppu)
{
	if (!ppu)
	{
		ppu = ensure(cpu_thread::get_current<ppu_thread>());
	}

	return mtx.reset([](int invoke_count, const stx::init_lock&, ppu_thread* ppu)
		{
			if (!invoke_count)
			{
				// Sleep before waiting on lock
				lv2_obj::sleep(*ppu);
			}
			else
			{
				// Wake up after acquistion or failure to acquire
				ppu->check_state();
			}
		},
		ppu);
}

class ppu_syscall_usage
{
	// Internal buffer
	std::string m_stats;
	u64 m_old_stat[1024]{};

public:
	// Public info collection buffers
	atomic_t<u64> stat[1024]{};

	void print_stats(bool force_print) noexcept
	{
		std::multimap<u64, u64, std::greater<u64>> usage;

		for (u32 i = 0; i < 1024; i++)
		{
			if (u64 v = stat[i]; m_old_stat[i] != v || (force_print && v))
			{
				// Only add syscalls with non-zero usage counter and only if caught new calls since last print
				usage.emplace(v, i);
				m_old_stat[i] = v;
			}
		}

		m_stats.clear();

		for (auto&& pair : usage)
		{
			fmt::append(m_stats, u8"\n\t⁂ %s [%u]", ppu_get_syscall_name(pair.second), pair.first);
		}

		if (!m_stats.empty())
		{
			ppu_log.notice("PPU Syscall Usage Stats:%s", m_stats);
		}
	}

	void operator()()
	{
		bool was_paused = false;
		u64 sleep_until = get_system_time();

		for (u32 i = 1; thread_ctrl::state() != thread_state::aborting; i++)
		{
			thread_ctrl::wait_until(&sleep_until, 1'000'000);

			const bool is_paused = Emu.IsPaused();

			// Force-print all if paused
			const bool force_print = is_paused && !was_paused;

			if (force_print || i % 10 == 0)
			{
				was_paused = is_paused;
				print_stats(force_print);
			}
		}
	}

	~ppu_syscall_usage()
	{
		print_stats(true);
	}

	static constexpr auto thread_name = "PPU Syscall Usage Thread"sv;
};

extern void ppu_execute_syscall(ppu_thread& ppu, u64 code)
{
	if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm_legacy)
	{
		code = ppu.gpr[11];
	}

	if (code < g_ppu_syscall_table.size())
	{
		g_fxo->get<named_thread<ppu_syscall_usage>>().stat[code]++;

		if (const auto func = g_ppu_syscall_table[code].first)
		{
#ifdef __APPLE__
			pthread_jit_write_protect_np(false);
#endif
			func(ppu, {}, vm::_ptr<u32>(ppu.cia), nullptr);
			ppu_log.trace("Syscall '%s' (%llu) finished, r3=0x%llx", ppu_syscall_code(code), code, ppu.gpr[3]);

#ifdef __APPLE__
			pthread_jit_write_protect_np(true);
			// No need to flush cache lines after a syscall, since we didn't generate any code.
#endif
			return;
		}
	}

	fmt::throw_exception("Invalid syscall number (%llu)", code);
}

extern ppu_intrp_func_t ppu_get_syscall(u64 code)
{
	if (code < g_ppu_syscall_table.size())
	{
		return g_ppu_syscall_table[code].first;
	}

	return nullptr;
}

std::string ppu_get_syscall_name(u64 code)
{
	if (code < g_ppu_syscall_table.size() && !g_ppu_syscall_table[code].second.empty())
	{
		return std::string(g_ppu_syscall_table[code].second);
	}

	return fmt::format("syscall_%u", code);
}

DECLARE(lv2_obj::g_mutex);
DECLARE(lv2_obj::g_ppu){};
DECLARE(lv2_obj::g_pending){};
DECLARE(lv2_obj::g_priority_order_tag){};

thread_local DECLARE(lv2_obj::g_to_notify){};
thread_local DECLARE(lv2_obj::g_postpone_notify_barrier){};
thread_local DECLARE(lv2_obj::g_to_awake);

// Scheduler queue for timeouts (wait until -> thread)
static std::deque<std::pair<u64, class cpu_thread*>> g_waiting;

// Threads which must call lv2_obj::sleep before the scheduler starts
static std::deque<class cpu_thread*> g_to_sleep;
static atomic_t<bool> g_scheduler_ready = false;
static atomic_t<u64> s_yield_frequency = 0;
static atomic_t<u64> s_max_allowed_yield_tsc = 0;
static u64 s_last_yield_tsc = 0;
atomic_t<u32> g_lv2_preempts_taken = 0;

namespace cpu_counter
{
	void remove(cpu_thread*) noexcept;
}

std::string lv2_obj::name64(u64 name_u64)
{
	const auto ptr = reinterpret_cast<const char*>(&name_u64);

	// NTS string, ignore invalid/newline characters
	// Example: "lv2\n\0tx" will be printed as "lv2"
	std::string str{ptr, std::find(ptr, ptr + 7, '\0')};
	str.erase(std::remove_if(str.begin(), str.end(), [](uchar c)
				  {
					  return !std::isprint(c);
				  }),
		str.end());

	return str;
}

bool lv2_obj::sleep(cpu_thread& cpu, const u64 timeout)
{
	// Should already be performed when using this flag
	if (!g_postpone_notify_barrier)
	{
		prepare_for_sleep(cpu);
	}

	if (cpu.get_class() == thread_class::ppu)
	{
		if (u32 addr = static_cast<ppu_thread&>(cpu).res_notify)
		{
			static_cast<ppu_thread&>(cpu).res_notify = 0;

			if (static_cast<ppu_thread&>(cpu).res_notify_time != vm::reservation_notifier_count_index(addr).second)
			{
				// Ignore outdated notification request
			}
			else if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t<const void>{}); it != std::end(g_to_notify))
			{
				*it++ = vm::reservation_notifier_notify(addr, true);

				if (it < std::end(g_to_notify))
				{
					// Null-terminate the list if it ends before last slot
					*it = nullptr;
				}
			}
			else
			{
				vm::reservation_notifier_notify(addr);
			}
		}
	}

	bool result = false;
	const u64 current_time = get_guest_system_time();
	{
		std::lock_guard lock{g_mutex};
		result = sleep_unlocked(cpu, timeout, current_time);

		if (!g_to_awake.empty())
		{
			// Schedule pending entries
			awake_unlocked({});
		}

		schedule_all(current_time);
	}

	if (!g_postpone_notify_barrier)
	{
		notify_all();
	}

	g_to_awake.clear();
	return result;
}

bool lv2_obj::awake(cpu_thread* thread, s32 prio)
{
	if (ppu_thread* ppu = cpu_thread::get_current<ppu_thread>())
	{
		if (u32 addr = ppu->res_notify)
		{
			ppu->res_notify = 0;

			if (ppu->res_notify_time != vm::reservation_notifier_count_index(addr).second)
			{
				// Ignore outdated notification request
			}
			else if (auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t<const void>{}); it != std::end(g_to_notify))
			{
				*it++ = vm::reservation_notifier_notify(addr, true);

				if (it < std::end(g_to_notify))
				{
					// Null-terminate the list if it ends before last slot
					*it = nullptr;
				}
			}
			else
			{
				vm::reservation_notifier_notify(addr);
			}
		}
	}

	bool result = false;
	{
		std::lock_guard lock(g_mutex);
		result = awake_unlocked(thread, prio);
		schedule_all();
	}

	if (result)
	{
		if (auto cpu = cpu_thread::get_current(); cpu && cpu->is_paused())
		{
			vm::temporary_unlock();
		}
	}

	if (!g_postpone_notify_barrier)
	{
		notify_all();
	}

	return result;
}

bool lv2_obj::yield(cpu_thread& thread)
{
	if (auto ppu = thread.try_get<ppu_thread>())
	{
		ppu->raddr = 0; // Clear reservation

		if (!atomic_storage<ppu_thread*>::load(ppu->next_ppu))
		{
			// Nothing to do
			return false;
		}
	}

	return awake(&thread, yield_cmd);
}

bool lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout, u64 current_time)
{
	const u64 start_time = current_time;

	auto on_to_sleep_update = [&]()
	{
		if (g_to_sleep.size() > 5u)
		{
			ppu_log.warning("Threads (%d)", g_to_sleep.size());
		}
		else if (!g_to_sleep.empty())
		{
			// In case there is a deadlock (PPU threads not sleeping)
			// Print-out their IDs for further inspection (focus at 5 at max for now to avoid log spam)
			std::string out = fmt::format("Threads (%d):", g_to_sleep.size());
			for (auto thread : g_to_sleep)
			{
				fmt::append(out, " 0x%x,", thread->id);
			}

			out.resize(out.size() - 1);

			ppu_log.warning("%s", out);
		}
		else
		{
			ppu_log.warning("Final Thread");

			// All threads are ready, wake threads
			Emu.CallFromMainThread([]
				{
					if (Emu.IsStarting())
					{
						// It uses lv2_obj::g_mutex, run it on main thread
						Emu.FinalizeRunRequest();
					}
				});
		}
	};

	bool return_val = true;

	if (auto ppu = thread.try_get<ppu_thread>())
	{
		ppu_log.trace("sleep() - waiting (%zu)", g_pending);

		if (ppu->ack_suspend)
		{
			ppu->ack_suspend = false;
			g_pending--;
		}

		if (std::exchange(ppu->cancel_sleep, 0) == 2)
		{
			// Signal that the underlying LV2 operation has been cancelled and replaced with a short yield
			return_val = false;
		}

		const auto [_, ok] = ppu->state.fetch_op([&](bs_t<cpu_flag>& val)
			{
				if (!(val & cpu_flag::signal))
				{
					val += cpu_flag::suspend;

					// Flag used for forced timeout notification
					ensure(!timeout || !(val & cpu_flag::notify));
					return true;
				}

				return false;
			});

		if (!ok)
		{
			ppu_log.fatal("sleep() failed (signaled) (%s)", ppu->current_function);
			return false;
		}

		// Find and remove the thread
		if (!unqueue(g_ppu, ppu, &ppu_thread::next_ppu))
		{
			if (auto it = std::find(g_to_sleep.begin(), g_to_sleep.end(), ppu); it != g_to_sleep.end())
			{
				g_to_sleep.erase(it);
				ppu->start_time = start_time;
				on_to_sleep_update();
				return true;
			}

			// Already sleeping
			ppu_log.trace("sleep(): called on already sleeping thread.");
			return false;
		}

		ppu->raddr = 0; // Clear reservation
		ppu->start_time = start_time;
		ppu->end_time = timeout ? start_time + std::min<u64>(timeout, ~start_time) : u64{umax};
	}
	else if (auto spu = thread.try_get<spu_thread>())
	{
		if (auto it = std::find(g_to_sleep.begin(), g_to_sleep.end(), spu); it != g_to_sleep.end())
		{
			g_to_sleep.erase(it);
			on_to_sleep_update();
			return true;
		}

		return false;
	}

	if (timeout)
	{
		const u64 wait_until = start_time + std::min<u64>(timeout, ~start_time);

		// Register timeout if necessary
		for (auto it = g_waiting.cbegin(), end = g_waiting.cend();; it++)
		{
			if (it == end || it->first > wait_until)
			{
				g_waiting.emplace(it, wait_until, &thread);
				break;
			}
		}
	}

	return return_val;
}

bool lv2_obj::awake_unlocked(cpu_thread* cpu, s32 prio)
{
	// Check thread type
	AUDIT(!cpu || cpu->get_class() == thread_class::ppu);

	bool push_first = false;

	switch (prio)
	{
	default:
	{
		// Priority set
		auto set_prio = [](atomic_t<ppu_thread::ppu_prio_t>& prio, s32 value, bool increment_order_last, bool increment_order_first)
		{
			s64 tag = 0;

			if (increment_order_first || increment_order_last)
			{
				tag = ++g_priority_order_tag;
			}

			prio.atomic_op([&](ppu_thread::ppu_prio_t& prio)
				{
					prio.prio = value;

					if (increment_order_first)
					{
						prio.order = ~tag;
					}
					else if (increment_order_last)
					{
						prio.order = tag;
					}
				});
		};

		const s64 old_prio = static_cast<ppu_thread*>(cpu)->prio.load().prio;

		// If priority is the same, push ONPROC/RUNNABLE thread to the back of the priority list if it is not the current thread
		if (old_prio == prio && cpu == cpu_thread::get_current())
		{
			set_prio(static_cast<ppu_thread*>(cpu)->prio, prio, false, false);
			return true;
		}

		if (!unqueue(g_ppu, static_cast<ppu_thread*>(cpu), &ppu_thread::next_ppu))
		{
			set_prio(static_cast<ppu_thread*>(cpu)->prio, prio, old_prio > prio, old_prio < prio);
			return true;
		}

		set_prio(static_cast<ppu_thread*>(cpu)->prio, prio, false, false);
		break;
	}
	case yield_cmd:
	{
		usz i = 0;

		// Yield command
		for (auto ppu_next = &g_ppu;; i++)
		{
			const auto ppu = +*ppu_next;

			if (!ppu)
			{
				return false;
			}

			if (ppu == cpu)
			{
				auto ppu2 = ppu->next_ppu;

				if (!ppu2 || ppu2->prio.load().prio != ppu->prio.load().prio)
				{
					// Empty 'same prio' threads list
					return false;
				}

				for (i++;; i++)
				{
					const auto next = ppu2->next_ppu;

					if (!next || next->prio.load().prio != ppu->prio.load().prio)
					{
						break;
					}

					ppu2 = next;
				}

				// Rotate current thread to the last position of the 'same prio' threads list
				// Exchange forward pointers
				*ppu_next = std::exchange(ppu->next_ppu, std::exchange(ppu2->next_ppu, ppu));

				if (i < g_cfg.core.ppu_threads + 0u)
				{
					// Threads were rotated, but no context switch was made
					return false;
				}

				ppu->start_time = get_guest_system_time();
				break;
			}

			ppu_next = &ppu->next_ppu;
		}

		break;
	}
	case enqueue_cmd:
	{
		break;
	}
	}

	const auto emplace_thread = [push_first](cpu_thread* const cpu)
	{
		for (auto it = &g_ppu;;)
		{
			const auto next = +*it;

			if (next == cpu)
			{
				ppu_log.trace("sleep() - suspended (p=%zu)", g_pending);

				if (static_cast<ppu_thread*>(cpu)->cancel_sleep == 1)
				{
					// The next sleep call of the thread is cancelled
					static_cast<ppu_thread*>(cpu)->cancel_sleep = 2;
				}

				return false;
			}

			// Use priority, also preserve FIFO order
			if (!next || (push_first ? next->prio.load().prio >= static_cast<ppu_thread*>(cpu)->prio.load().prio : next->prio.load().prio > static_cast<ppu_thread*>(cpu)->prio.load().prio))
			{
				atomic_storage<ppu_thread*>::release(static_cast<ppu_thread*>(cpu)->next_ppu, next);
				atomic_storage<ppu_thread*>::release(*it, static_cast<ppu_thread*>(cpu));
				break;
			}

			it = &next->next_ppu;
		}

		// Unregister timeout if necessary
		for (auto it = g_waiting.cbegin(), end = g_waiting.cend(); it != end; it++)
		{
			if (it->second == cpu)
			{
				g_waiting.erase(it);
				break;
			}
		}

		ppu_log.trace("awake(): %s", cpu->id);
		return true;
	};

	// Yield changed the queue before
	bool changed_queue = prio == yield_cmd;

	s32 lowest_new_priority = smax;
	const bool has_free_hw_thread_space = count_non_sleeping_threads().onproc_count < g_cfg.core.ppu_threads + 0u;

	if (cpu && prio != yield_cmd)
	{
		// Emplace current thread
		if (emplace_thread(cpu))
		{
			changed_queue = true;
			lowest_new_priority = std::min<s32>(static_cast<ppu_thread*>(cpu)->prio.load().prio, lowest_new_priority);
		}
	}
	else
		for (const auto _cpu : g_to_awake)
		{
			// Emplace threads from list
			if (emplace_thread(_cpu))
			{
				changed_queue = true;
				lowest_new_priority = std::min<s32>(static_cast<ppu_thread*>(_cpu)->prio.load().prio, lowest_new_priority);
			}
		}

	auto target = +g_ppu;
	usz i = 0;

	// Suspend threads if necessary
	for (usz thread_count = g_cfg.core.ppu_threads; target; target = target->next_ppu, i++)
	{
		if (i >= thread_count && cpu_flag::suspend - target->state)
		{
			ppu_log.trace("suspend(): %s", target->id);
			target->ack_suspend = true;
			g_pending++;
			ensure(!target->state.test_and_set(cpu_flag::suspend));

			if (is_paused(target->state - cpu_flag::suspend))
			{
				target->state.notify_one();
			}
		}
	}

	const auto current_ppu = cpu_thread::get_current<ppu_thread>();

	// Remove pending if necessary
	if (current_ppu)
	{
		if (std::exchange(current_ppu->ack_suspend, false))
		{
			ensure(g_pending)--;
		}
	}

	// In real PS3 (it seems), when a thread with a higher priority than the caller is signaled and -
	// - that there is available space on the running queue for the other hardware thread to start
	// It prioritizes signaled thread - caller's hardware thread switches instantly to the new thread code
	// While signaling to the other hardware thread to execute the caller's code.
	// Resulting in a delay to the caller after such thread is signaled

	if (current_ppu && changed_queue && has_free_hw_thread_space)
	{
		if (current_ppu->prio.load().prio > lowest_new_priority)
		{
			const bool is_create_thread = current_ppu->gpr[11] == 0x35;

			// When not being set to All timers - activate only for sys_ppu_thread_start
			if (is_create_thread || g_cfg.core.sleep_timers_accuracy == sleep_timers_accuracy_level::_all_timers)
			{
				if (!current_ppu->state.test_and_set(cpu_flag::yield) || current_ppu->hw_sleep_time != 0)
				{
					current_ppu->hw_sleep_time += (is_create_thread ? 51 : 35);
				}
				else
				{
					current_ppu->hw_sleep_time = 30000; // In addition to another flag's use (TODO: Refactor and clean this)
				}
			}
		}
	}

	return changed_queue;
}

void lv2_obj::cleanup()
{
	g_ppu = nullptr;
	g_scheduler_ready = false;
	g_to_sleep.clear();
	g_waiting.clear();
	g_pending = 0;
	s_yield_frequency = 0;
}

void lv2_obj::schedule_all(u64 current_time)
{
	auto it = std::find(g_to_notify, std::end(g_to_notify), std::add_pointer_t<const void>{});

	if (!g_pending && g_scheduler_ready)
	{
		auto target = +g_ppu;

		// Wake up threads
		for (usz x = g_cfg.core.ppu_threads; target && x; target = target->next_ppu, x--)
		{
			if (target->state & cpu_flag::suspend)
			{
				ppu_log.trace("schedule(): %s", target->id);

				// Remove yield if it was sleeping until now
				const bs_t<cpu_flag> remove_yield = target->start_time == 0 ? +cpu_flag::suspend : (cpu_flag::yield + cpu_flag::preempt);

				target->start_time = 0;

				if ((target->state.fetch_op(AOFN(x += cpu_flag::signal, x -= cpu_flag::suspend, x -= remove_yield, void())) & (cpu_flag::wait + cpu_flag::signal)) != cpu_flag::wait)
				{
					continue;
				}

				if (it == std::end(g_to_notify))
				{
					// Out of notification slots, notify locally (resizable container is not worth it)
					target->state.notify_one();
				}
				else
				{
					*it++ = &target->state;
				}
			}
		}
	}

	// Check registered timeouts
	while (!g_waiting.empty())
	{
		const auto pair = &g_waiting.front();

		if (!current_time)
		{
			current_time = get_guest_system_time();
		}

		if (pair->first <= current_time)
		{
			const auto target = pair->second;
			g_waiting.pop_front();

			if (target != cpu_thread::get_current())
			{
				// Change cpu_thread::state for the lightweight notification to work
				ensure(!target->state.test_and_set(cpu_flag::notify));

				// Otherwise notify it to wake itself
				if (it == std::end(g_to_notify))
				{
					// Out of notification slots, notify locally (resizable container is not worth it)
					target->state.notify_one();
				}
				else
				{
					*it++ = &target->state;
				}
			}
		}
		else
		{
			// The list is sorted so assume no more timeouts
			break;
		}
	}

	if (it < std::end(g_to_notify))
	{
		// Null-terminate the list if it ends before last slot
		*it = nullptr;
	}

	if (const u64 freq = s_yield_frequency)
	{
		const u64 tsc = utils::get_tsc();
		const u64 last_tsc = s_last_yield_tsc;

		if (tsc >= last_tsc && tsc <= s_max_allowed_yield_tsc && tsc - last_tsc >= freq)
		{
			auto target = +g_ppu;
			cpu_thread* cpu = nullptr;

			for (usz x = g_cfg.core.ppu_threads;; target = target->next_ppu, x--)
			{
				if (!target || !x)
				{
					if (g_ppu && cpu_flag::preempt - g_ppu->state)
					{
						// Don't be picky, pick up any running PPU thread even it has a wait flag
						cpu = g_ppu;
					}
					// TODO: If this case is common enough it may be valuable to iterate over all CPU threads to find a perfect candidate (one without a wait or suspend flag)
					else if (auto current = cpu_thread::get_current(); current && cpu_flag::suspend - current->state)
					{
						// May be an SPU or RSX thread, use them as a last resort
						cpu = current;
					}

					break;
				}

				if (target->state.none_of(cpu_flag::preempt + cpu_flag::wait))
				{
					cpu = target;
					break;
				}
			}

			if (cpu && cpu_flag::preempt - cpu->state && !cpu->state.test_and_set(cpu_flag::preempt))
			{
				s_last_yield_tsc = tsc;
				g_lv2_preempts_taken.release(g_lv2_preempts_taken.load() + 1); // Has a minor race but performance is more important
				rsx::set_rsx_yield_flag();
			}
		}
	}
}

void lv2_obj::make_scheduler_ready()
{
	g_scheduler_ready.release(true);
	lv2_obj::awake_all();
}

std::pair<ppu_thread_status, u32> lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_lv2)
{
	std::optional<reader_lock> opt_lock[2];

	if (lock_idm)
	{
		opt_lock[0].emplace(id_manager::g_mutex);
	}

	if (!Emu.IsReady() ? ppu->state.all_of(cpu_flag::stop) : ppu->stop_flag_removal_protection)
	{
		return {PPU_THREAD_STATUS_IDLE, 0};
	}

	switch (ppu->joiner)
	{
	case ppu_join_status::zombie: return {PPU_THREAD_STATUS_ZOMBIE, 0};
	case ppu_join_status::exited: return {PPU_THREAD_STATUS_DELETED, 0};
	default: break;
	}

	if (lock_lv2)
	{
		opt_lock[1].emplace(lv2_obj::g_mutex);
	}

	u32 pos = umax;
	u32 i = 0;

	for (auto target = +g_ppu; target; target = target->next_ppu, i++)
	{
		if (target == ppu)
		{
			pos = i;
			break;
		}
	}

	if (pos == umax)
	{
		if (!ppu->interrupt_thread_executing)
		{
			return {PPU_THREAD_STATUS_STOP, 0};
		}

		return {PPU_THREAD_STATUS_SLEEP, 0};
	}

	if (pos >= g_cfg.core.ppu_threads + 0u)
	{
		return {PPU_THREAD_STATUS_RUNNABLE, pos};
	}

	return {PPU_THREAD_STATUS_ONPROC, pos};
}

void lv2_obj::set_future_sleep(cpu_thread* cpu)
{
	g_to_sleep.emplace_back(cpu);
}

bool lv2_obj::is_scheduler_ready()
{
	reader_lock lock(g_mutex);
	return g_to_sleep.empty();
}

ppu_non_sleeping_count_t lv2_obj::count_non_sleeping_threads()
{
	ppu_non_sleeping_count_t total{};

	auto target = atomic_storage<ppu_thread*>::load(g_ppu);

	for (usz thread_count = g_cfg.core.ppu_threads; target; target = atomic_storage<ppu_thread*>::load(target->next_ppu))
	{
		if (total.onproc_count == thread_count)
		{
			total.has_running = true;
			break;
		}

		total.onproc_count++;
	}

	return total;
}

void lv2_obj::set_yield_frequency(u64 freq, u64 max_allowed_tsc)
{
	s_yield_frequency.release(freq);
	s_max_allowed_yield_tsc.release(max_allowed_tsc);
	g_lv2_preempts_taken.release(0);
}

#if defined(_MSC_VER)
#define mwaitx_func
#define waitpkg_func
#else
#define mwaitx_func __attribute__((__target__("mwaitx")))
#define waitpkg_func __attribute__((__target__("waitpkg")))
#endif

#if defined(ARCH_X64)
// Waits for a number of TSC clock cycles in power optimized state
// Cstate is represented in bits [7:4]+1 cstate. So C0 requires bits [7:4] to be set to 0xf, C1 requires bits [7:4] to be set to 0.
mwaitx_func static void __mwaitx(u32 cycles, u32 cstate)
{
	constexpr u32 timer_enable = 0x2;

	// monitorx will wake if the cache line is written to. We don't want this, so place the monitor value on it's own cache line.
	alignas(64) u64 monitor_var{};
	_mm_monitorx(&monitor_var, 0, 0);
	_mm_mwaitx(timer_enable, cstate, cycles);
}

// First bit indicates cstate, 0x0 for C.02 state (lower power) or 0x1 for C.01 state (higher power)
waitpkg_func static void __tpause(u32 cycles, u32 cstate)
{
	const u64 tsc = utils::get_tsc() + cycles;
	_tpause(cstate, tsc);
}
#endif

bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep)
{
	static_assert(u64{umax} / max_timeout >= 100, "max timeout is not valid for scaling");

	const u64 start_time = get_system_time();

	if (cpu)
	{
		if (u64 end_time = cpu->end_time; end_time != umax)
		{
			const u64 guest_start = get_guest_system_time(start_time);

			if (end_time <= guest_start)
			{
				return true;
			}

			usec = end_time - guest_start;
			scale = true;
		}
	}

	if (scale)
	{
		// Scale time
		usec = std::min<u64>(usec, u64{umax} / 100) * 100 / g_cfg.core.clocks_scale;
	}

	// Clamp
	usec = std::min<u64>(usec, max_timeout);

	u64 passed = 0;

	atomic_bs_t<cpu_flag> dummy{};
	const auto& state = cpu ? cpu->state : dummy;
	auto old_state = +state;

	auto wait_for = [&](u64 timeout)
	{
		thread_ctrl::wait_on(state, old_state, timeout);
	};

	for (;; old_state = state)
	{
		if (old_state & cpu_flag::notify)
		{
			// Timeout notification has been forced
			break;
		}

		if (old_state & cpu_flag::signal)
		{
			return false;
		}

		if (::is_stopped(old_state) || thread_ctrl::state() == thread_state::aborting)
		{
			return passed >= usec;
		}

		if (passed >= usec)
		{
			break;
		}

		u64 remaining = usec - passed;
#ifdef __linux__
		// NOTE: Assumption that timer initialization has succeeded
		constexpr u64 host_min_quantum = 10;
#else
		// Host scheduler quantum for windows (worst case)
		// NOTE: On ps3 this function has very high accuracy
		constexpr u64 host_min_quantum = 500;
#endif
		// TODO: Tune for other non windows operating sytems

		if (g_cfg.core.sleep_timers_accuracy < (is_usleep ? sleep_timers_accuracy_level::_usleep : sleep_timers_accuracy_level::_all_timers))
		{
			wait_for(remaining);
		}
		else
		{
			if (remaining > host_min_quantum)
			{
#ifdef __linux__
				// With timerslack set low, Linux is precise for all values above
				wait_for(remaining);
#else
				// Wait on multiple of min quantum for large durations to avoid overloading low thread cpus
				wait_for(remaining - (remaining % host_min_quantum));
#endif
			}
			// TODO: Determine best value for yield delay
#if defined(ARCH_X64)
			else if (utils::has_appropriate_um_wait())
			{
				const u32 us_in_tsc_clocks = ::narrow<u32>(remaining * (utils::get_tsc_freq() / 1000000ULL));

				if (utils::has_waitpkg())
				{
					__tpause(us_in_tsc_clocks, 0x1);
				}
				else
				{
					__mwaitx(us_in_tsc_clocks, 0xf0);
				}
			}
#endif
			else
			{
				// Try yielding. May cause long wake latency but helps weaker CPUs a lot by alleviating resource pressure
				std::this_thread::yield();
			}
		}

		passed = get_system_time() - start_time;
	}

	return true;
}

void lv2_obj::prepare_for_sleep(cpu_thread& cpu)
{
	vm::temporary_unlock(cpu);
	cpu_counter::remove(&cpu);
}

void lv2_obj::notify_all() noexcept
{
	for (auto cpu : g_to_notify)
	{
		if (!cpu)
		{
			break;
		}

		if (cpu != &g_to_notify)
		{
			const auto res_start = vm::reservation_notifier(0).second;
			const auto res_end = vm::reservation_notifier(umax).second;

			if (cpu >= res_start && cpu <= res_end)
			{
				atomic_wait_engine::notify_all(cpu);
			}
			else
			{
				// Note: by the time of notification the thread could have been deallocated which is why the direct function is used
				atomic_wait_engine::notify_one(cpu);
			}
		}
	}

	g_to_notify[0] = nullptr;
	g_postpone_notify_barrier = false;

	const auto cpu = cpu_thread::get_current();

	if (!cpu)
	{
		return;
	}

	if (cpu->get_class() != thread_class::spu && cpu->state.none_of(cpu_flag::suspend))
	{
		return;
	}

	std::optional<vm::writer_lock> lock;

	constexpr usz total_waiters = std::size(spu_thread::g_spu_waiters_by_value);

	u32 notifies[total_waiters]{};

	// There may be 6 waiters, but checking them all may be performance expensive
	// Instead, check 2 at max, but use the CPU ID index to tell which index to start checking so the work would be distributed across all threads

	atomic_t<u64, 64>* range_lock = nullptr;

	for (usz i = 0, checked = 0; checked < 3 && i < total_waiters; i++)
	{
		auto& waiter = spu_thread::g_spu_waiters_by_value[(i + cpu->id) % total_waiters];
		const u64 value = waiter.load();
		u32 raddr = static_cast<u32>(value) & -128;

		if (vm::check_addr(raddr))
		{
			if (((raddr >> 28) < 2 || (raddr >> 28) == 0xd))
			{
				checked++;

				if (compute_rdata_hash32(*vm::get_super_ptr<spu_rdata_t>(raddr)) != static_cast<u32>(value >> 32))
				{
					// Clear address to avoid a race, keep waiter counter
					if (waiter.fetch_op([&](u64& x)
								  {
									  if ((x & -128) == (value & -128))
									  {
										  x &= 127;
										  return true;
									  }

									  return false;
								  })
							.second)
					{
						notifies[i] = raddr;
					}
				}

				continue;
			}

			if (!range_lock)
			{
				range_lock = vm::alloc_range_lock();
			}

			checked++;

			if (spu_thread::reservation_check(raddr, static_cast<u32>(value >> 32), range_lock))
			{
				// Clear address to avoid a race, keep waiter counter
				if (waiter.fetch_op([&](u64& x)
							  {
								  if ((x & -128) == (value & -128))
								  {
									  x &= 127;
									  return true;
								  }

								  return false;
							  })
						.second)
				{
					notifies[i] = raddr;
				}
			}
		}
	}

	if (range_lock)
	{
		vm::free_range_lock(range_lock);
	}

	for (u32 addr : notifies)
	{
		if (addr)
		{
			vm::reservation_notifier_notify(addr);
		}
	}
}
