From f4324ee59938ec6631534b76b5bde9a20e9019c8 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Wed, 22 Mar 2023 01:08:48 -0400 Subject: [PATCH] Split OoT into its own repo --- disable_warnings.h | 4 - recomp.h | 239 - sections.h | 23 - src/main.cpp | 2 + test/RecompFuncs/RecompFuncs.vcxproj | 185 - test/RecompFuncs/RecompFuncs.vcxproj.filters | 13232 ------- test/RecompTest.sln | 44 - test/RecompTest.vcxproj | 223 - test/RecompTest.vcxproj.filters | 30279 ----------------- test/portultra/audio.cpp | 88 - test/portultra/events.cpp | 335 - test/portultra/mesgqueue.cpp | 194 - test/portultra/misc_ultra.cpp | 22 - test/portultra/multilibultra.hpp | 70 - test/portultra/platform_specific.h | 32 - test/portultra/port_main.c | 83 - test/portultra/scheduler.cpp | 286 - test/portultra/task_pthreads.cpp | 60 - test/portultra/task_win32.cpp | 32 - test/portultra/threads.cpp | 193 - test/portultra/timer.cpp | 187 - test/portultra/ultra64.h | 182 - test/portultra/ultrainit.cpp | 15 - test/rsp/.gitignore | 2 - test/src/ai.cpp | 29 - test/src/cont.cpp | 108 - test/src/dp.cpp | 5 - test/src/eep.cpp | 21 - test/src/euc-jp.cpp | 2587 -- test/src/euc-jp.h | 11 - test/src/math_routines.cpp | 80 - test/src/overlays.cpp | 110 - test/src/pak.cpp | 31 - test/src/pi.cpp | 208 - test/src/portultra_translation.cpp | 158 - test/src/print.cpp | 70 - test/src/recomp.cpp | 165 - test/src/rsp.h | 65 - test/src/rsp_vu.h | 199 - test/src/rsp_vu_impl.h | 1537 - test/src/sp.cpp | 49 - test/src/vi.cpp | 38 - test/thirdparty/blockingconcurrentqueue.h | 582 - test/thirdparty/concurrentqueue.h | 3747 -- test/thirdparty/lightweightsemaphore.h | 425 - 45 files changed, 2 insertions(+), 56235 deletions(-) delete mode 100644 disable_warnings.h delete mode 100644 recomp.h delete mode 100644 sections.h delete mode 100644 test/RecompFuncs/RecompFuncs.vcxproj delete mode 100644 test/RecompFuncs/RecompFuncs.vcxproj.filters delete mode 100644 test/RecompTest.sln delete mode 100644 test/RecompTest.vcxproj delete mode 100644 test/RecompTest.vcxproj.filters delete mode 100644 test/portultra/audio.cpp delete mode 100644 test/portultra/events.cpp delete mode 100644 test/portultra/mesgqueue.cpp delete mode 100644 test/portultra/misc_ultra.cpp delete mode 100644 test/portultra/multilibultra.hpp delete mode 100644 test/portultra/platform_specific.h delete mode 100644 test/portultra/port_main.c delete mode 100644 test/portultra/scheduler.cpp delete mode 100644 test/portultra/task_pthreads.cpp delete mode 100644 test/portultra/task_win32.cpp delete mode 100644 test/portultra/threads.cpp delete mode 100644 test/portultra/timer.cpp delete mode 100644 test/portultra/ultra64.h delete mode 100644 test/portultra/ultrainit.cpp delete mode 100644 test/rsp/.gitignore delete mode 100644 test/src/ai.cpp delete mode 100644 test/src/cont.cpp delete mode 100644 test/src/dp.cpp delete mode 100644 test/src/eep.cpp delete mode 100644 test/src/euc-jp.cpp delete mode 100644 test/src/euc-jp.h delete mode 100644 test/src/math_routines.cpp delete mode 100644 test/src/overlays.cpp delete mode 100644 test/src/pak.cpp delete mode 100644 test/src/pi.cpp delete mode 100644 test/src/portultra_translation.cpp delete mode 100644 test/src/print.cpp delete mode 100644 test/src/recomp.cpp delete mode 100644 test/src/rsp.h delete mode 100644 test/src/rsp_vu.h delete mode 100644 test/src/rsp_vu_impl.h delete mode 100644 test/src/sp.cpp delete mode 100644 test/src/vi.cpp delete mode 100644 test/thirdparty/blockingconcurrentqueue.h delete mode 100644 test/thirdparty/concurrentqueue.h delete mode 100644 test/thirdparty/lightweightsemaphore.h diff --git a/disable_warnings.h b/disable_warnings.h deleted file mode 100644 index ff36f12..0000000 --- a/disable_warnings.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wunused-variable" -#pragma clang diagnostic ignored "-Wimplicit-function-declaration" -#endif diff --git a/recomp.h b/recomp.h deleted file mode 100644 index 2b17d41..0000000 --- a/recomp.h +++ /dev/null @@ -1,239 +0,0 @@ -#ifndef __RECOMP_H__ -#define __RECOMP_H__ - -#include -#include -#include -#include -#include - -#if 0 // treat GPRs as 32-bit, should be better codegen -typedef uint32_t gpr; - -#define SIGNED(val) \ - ((int32_t)(val)) -#else -typedef uint64_t gpr; - -#define SIGNED(val) \ - ((int64_t)(val)) -#endif - -#define ADD32(a, b) \ - ((gpr)(int32_t)((a) + (b))) - -#define SUB32(a, b) \ - ((gpr)(int32_t)((a) - (b))) - -#define MEM_W(offset, reg) \ - (*(int32_t*)(rdram + ((((reg) + (offset))) - 0xFFFFFFFF80000000))) - //(*(int32_t*)(rdram + ((((reg) + (offset))) & 0x3FFFFFF))) - -#define MEM_H(offset, reg) \ - (*(int16_t*)(rdram + ((((reg) + (offset)) ^ 2) - 0xFFFFFFFF80000000))) - //(*(int16_t*)(rdram + ((((reg) + (offset)) ^ 2) & 0x3FFFFFF))) - -#define MEM_B(offset, reg) \ - (*(int8_t*)(rdram + ((((reg) + (offset)) ^ 3) - 0xFFFFFFFF80000000))) - //(*(int8_t*)(rdram + ((((reg) + (offset)) ^ 3) & 0x3FFFFFF))) - -#define MEM_HU(offset, reg) \ - (*(uint16_t*)(rdram + ((((reg) + (offset)) ^ 2) - 0xFFFFFFFF80000000))) - //(*(uint16_t*)(rdram + ((((reg) + (offset)) ^ 2) & 0x3FFFFFF))) - -#define MEM_BU(offset, reg) \ - (*(uint8_t*)(rdram + ((((reg) + (offset)) ^ 3) - 0xFFFFFFFF80000000))) - //(*(uint8_t*)(rdram + ((((reg) + (offset)) ^ 3) & 0x3FFFFFF))) - -#define SD(val, offset, reg) { \ - *(uint32_t*)(rdram + ((((reg) + (offset) + 4)) - 0xFFFFFFFF80000000)) = (uint32_t)((val) >> 0); \ - *(uint32_t*)(rdram + ((((reg) + (offset) + 0)) - 0xFFFFFFFF80000000)) = (uint32_t)((val) >> 32); \ -} - -//#define SD(val, offset, reg) { \ -// *(uint32_t*)(rdram + ((((reg) + (offset) + 4)) & 0x3FFFFFF)) = (uint32_t)((val) >> 32); \ -// *(uint32_t*)(rdram + ((((reg) + (offset) + 0)) & 0x3FFFFFF)) = (uint32_t)((val) >> 0); \ -//} - -static inline uint64_t load_doubleword(uint8_t* rdram, gpr reg, gpr offset) { - uint64_t ret = 0; - uint64_t lo = (uint64_t)(uint32_t)MEM_W(reg, offset + 4); - uint64_t hi = (uint64_t)(uint32_t)MEM_W(reg, offset + 0); - ret = (lo << 0) | (hi << 32); - return ret; -} - -#define LD(offset, reg) \ - load_doubleword(rdram, offset, reg) - -// TODO proper lwl/lwr/swl/swr -static inline void do_swl(uint8_t* rdram, gpr offset, gpr reg, gpr val) { - uint8_t byte0 = val >> 24; - uint8_t byte1 = val >> 16; - uint8_t byte2 = val >> 8; - uint8_t byte3 = val >> 0; - - MEM_B(offset + 0, reg) = byte0; - MEM_B(offset + 1, reg) = byte1; - MEM_B(offset + 2, reg) = byte2; - MEM_B(offset + 3, reg) = byte3; -} - -static inline gpr do_lwl(uint8_t* rdram, gpr offset, gpr reg) { - uint8_t byte0 = MEM_B(offset + 0, reg); - uint8_t byte1 = MEM_B(offset + 1, reg); - uint8_t byte2 = MEM_B(offset + 2, reg); - uint8_t byte3 = MEM_B(offset + 3, reg); - - // Cast to int32_t to sign extend first - return (gpr)(int32_t)((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | (byte3 << 0)); -} - -#define S32(val) \ - ((int32_t)(val)) - -#define U32(val) \ - ((uint32_t)(val)) - -#define S64(val) \ - ((int64_t)(val)) - -#define U64(val) \ - ((uint64_t)(val)) - -#define MUL_S(val1, val2) \ - ((val1) * (val2)) - -#define MUL_D(val1, val2) \ - ((val1) * (val2)) - -#define DIV_S(val1, val2) \ - ((val1) / (val2)) - -#define DIV_D(val1, val2) \ - ((val1) / (val2)) - -#define CVT_S_W(val) \ - ((float)((int32_t)(val))) - -#define CVT_D_W(val) \ - ((double)((int32_t)(val))) - -#define CVT_D_S(val) \ - ((double)(val)) - -#define CVT_S_D(val) \ - ((float)(val)) - -#define TRUNC_W_S(val) \ - ((int32_t)(val)) - -#define TRUNC_W_D(val) \ - ((int32_t)(val)) - -#define TRUNC_L_S(val) \ - ((int64_t)(val)) - -#define TRUNC_L_D(val) \ - ((int64_t)(val)) - -// TODO rounding mode -#define CVT_W_S(val) \ - ((int32_t)(val)) - -#define CVT_W_D(val) \ - ((int32_t)(val)) - -#define NAN_CHECK(val) \ - assert(val == val) - -//#define NAN_CHECK(val) - -typedef union { - double d; - struct { - float fl; - float fh; - }; - struct { - uint32_t u32l; - uint32_t u32h; - }; - uint64_t u64; -} fpr; - -typedef struct { - gpr r0, r1, r2, r3, r4, r5, r6, r7, - r8, r9, r10, r11, r12, r13, r14, r15, - r16, r17, r18, r19, r20, r21, r22, r23, - r24, r25, r26, r27, r28, r29, r30, r31; - fpr f0, f2, f4, f6, f8, f10, f12, f14, - f16, f18, f20, f22, f24, f26, f28, f30; - uint64_t hi, lo; -} recomp_context; - -#ifdef __cplusplus -extern "C" { -#endif - -void switch_error(const char* func, uint32_t vram, uint32_t jtbl); -void do_break(uint32_t vram); - -typedef void (recomp_func_t)(uint8_t* rdram, recomp_context* ctx); - -recomp_func_t* get_function(int32_t vram); - -#define LOOKUP_FUNC(val) \ - get_function((int32_t)(val)) - -extern int32_t section_addresses[]; - -#define LO16(x) \ - ((x) & 0xFFFF) - -#define HI16(x) \ - (((x) >> 16) + (((x) >> 15) & 1)) - -#define RELOC_HI16(section_index, offset) \ - HI16(section_addresses[section_index] + (offset)) - -#define RELOC_LO16(section_index, offset) \ - LO16(section_addresses[section_index] + (offset)) - -// For the Mario Party games (not working) -//// This has to be in this file so it can be inlined -//struct jmp_buf_storage { -// jmp_buf buffer; -//}; -// -//struct RecompJmpBuf { -// int32_t owner; -// struct jmp_buf_storage* storage; -// uint64_t magic; -//}; -// -//// Randomly generated constant -//#define SETJMP_MAGIC 0xe17afdfa939a437bu -// -//int32_t osGetThreadEx(void); -// -//#define setjmp_recomp(rdram, ctx) { \ -// struct RecompJmpBuf* buf = (struct RecompJmpBuf*)(&rdram[(uint64_t)ctx->r4 - 0xFFFFFFFF80000000]); \ -// \ -// /* Check if this jump buffer was previously set up */ \ -// if (buf->magic == SETJMP_MAGIC) { \ -// /* If so, free the old jmp_buf */ \ -// free(buf->storage); \ -// } \ -// \ -// buf->magic = SETJMP_MAGIC; \ -// buf->owner = osGetThreadEx(); \ -// buf->storage = (struct jmp_buf_storage*)calloc(1, sizeof(struct jmp_buf_storage)); \ -// ctx->r2 = setjmp(buf->storage->buffer); \ -//} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/sections.h b/sections.h deleted file mode 100644 index 1d1b228..0000000 --- a/sections.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef __SECTIONS_H__ -#define __SECTIONS_H__ - -#include -#include "recomp.h" - -#define ARRLEN(x) (sizeof(x) / sizeof((x)[0])) - -typedef struct { - recomp_func_t* func; - uint32_t offset; -} FuncEntry; - -typedef struct { - uint32_t rom_addr; - uint32_t ram_addr; - uint32_t size; - FuncEntry *funcs; - size_t num_funcs; - size_t index; -} SectionTableEntry; - -#endif diff --git a/src/main.cpp b/src/main.cpp index 907a20c..87dbfb5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -975,6 +975,8 @@ bool read_list_file(const char* filename, std::unordered_set& entri while (input_file >> entry) { entries_out.emplace(std::move(entry)); } + + return true; } int main(int argc, char** argv) { diff --git a/test/RecompFuncs/RecompFuncs.vcxproj b/test/RecompFuncs/RecompFuncs.vcxproj deleted file mode 100644 index 91f3d18..0000000 --- a/test/RecompFuncs/RecompFuncs.vcxproj +++ /dev/null @@ -1,185 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - Win32Proj - {d080900b-5c12-45fc-941b-515a5fea7594} - RecompFuncs - 10.0 - - - - StaticLibrary - true - v142 - Unicode - - - StaticLibrary - false - v142 - true - Unicode - - - StaticLibrary - true - v142 - Unicode - - - StaticLibrary - false - v142 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - false - - - true - - - false - - - - Level1 - true - WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) - true - NotUsing - - - $(SolutionDir).. - true - Disabled - - - EnableFastChecks - - - - - true - - - - - Level1 - true - true - true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - true - NotUsing - - - $(SolutionDir).. - true - false - - - - - true - true - true - - - - - Level1 - true - _DEBUG;_LIB;%(PreprocessorDefinitions) - true - NotUsing - - - $(SolutionDir).. - - true - Disabled - EnableFastChecks - - - - - true - - - - - Level1 - true - true - true - NDEBUG;_LIB;%(PreprocessorDefinitions) - true - NotUsing - - - $(SolutionDir).. - - true - false - - - - - true - true - true - - - - - - - <_WildCardClCompile Include="..\funcs\*.c" /> - - - - - - \ No newline at end of file diff --git a/test/RecompFuncs/RecompFuncs.vcxproj.filters b/test/RecompFuncs/RecompFuncs.vcxproj.filters deleted file mode 100644 index c052c85..0000000 --- a/test/RecompFuncs/RecompFuncs.vcxproj.filters +++ /dev/null @@ -1,13232 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Header Files - - - \ No newline at end of file diff --git a/test/RecompTest.sln b/test/RecompTest.sln deleted file mode 100644 index 7632d3c..0000000 --- a/test/RecompTest.sln +++ /dev/null @@ -1,44 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.32929.386 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RecompTest", "RecompTest.vcxproj", "{73819ED8-8A5B-4554-B3F3-60257A43F296}" - ProjectSection(ProjectDependencies) = postProject - {D080900B-5C12-45FC-941B-515A5FEA7594} = {D080900B-5C12-45FC-941B-515A5FEA7594} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RecompFuncs", "RecompFuncs\RecompFuncs.vcxproj", "{D080900B-5C12-45FC-941B-515A5FEA7594}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {73819ED8-8A5B-4554-B3F3-60257A43F296}.Debug|x64.ActiveCfg = Debug|x64 - {73819ED8-8A5B-4554-B3F3-60257A43F296}.Debug|x64.Build.0 = Debug|x64 - {73819ED8-8A5B-4554-B3F3-60257A43F296}.Debug|x86.ActiveCfg = Debug|Win32 - {73819ED8-8A5B-4554-B3F3-60257A43F296}.Debug|x86.Build.0 = Debug|Win32 - {73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x64.ActiveCfg = Release|x64 - {73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x64.Build.0 = Release|x64 - {73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x86.ActiveCfg = Release|Win32 - {73819ED8-8A5B-4554-B3F3-60257A43F296}.Release|x86.Build.0 = Release|Win32 - {D080900B-5C12-45FC-941B-515A5FEA7594}.Debug|x64.ActiveCfg = Debug|x64 - {D080900B-5C12-45FC-941B-515A5FEA7594}.Debug|x64.Build.0 = Debug|x64 - {D080900B-5C12-45FC-941B-515A5FEA7594}.Debug|x86.ActiveCfg = Debug|Win32 - {D080900B-5C12-45FC-941B-515A5FEA7594}.Debug|x86.Build.0 = Debug|Win32 - {D080900B-5C12-45FC-941B-515A5FEA7594}.Release|x64.ActiveCfg = Release|x64 - {D080900B-5C12-45FC-941B-515A5FEA7594}.Release|x64.Build.0 = Release|x64 - {D080900B-5C12-45FC-941B-515A5FEA7594}.Release|x86.ActiveCfg = Release|Win32 - {D080900B-5C12-45FC-941B-515A5FEA7594}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {DD740FE2-99F7-4C80-B124-06D14447F873} - EndGlobalSection -EndGlobal diff --git a/test/RecompTest.vcxproj b/test/RecompTest.vcxproj deleted file mode 100644 index 3171e2e..0000000 --- a/test/RecompTest.vcxproj +++ /dev/null @@ -1,223 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 16.0 - {73819ED8-8A5B-4554-B3F3-60257A43F296} - Win32Proj - 10.0 - - - - Application - true - ClangCL - NotSet - - - Application - false - ClangCL - NotSet - - - Application - true - v142 - NotSet - - - Application - false - v142 - NotSet - - - - - - - - - - - - - - - - - - - - - true - - - true - - - true - true - - - true - - - - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDebugDLL - Level1 - ProgramDatabase - Disabled - $(ProjectDir)..;$(ProjectDir)thirdparty;%(AdditionalIncludeDirectories) - stdcpp20 - /utf-8 - - - MachineX86 - true - Console - - - - - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - MultiThreadedDLL - Level1 - ProgramDatabase - $(ProjectDir)..;$(ProjectDir)thirdparty;%(AdditionalIncludeDirectories) - stdcpp20 - /utf-8 - - - MachineX86 - true - Console - true - true - - - - - $(ProjectDir)..;$(ProjectDir)thirdparty;$(ProjectDir)Lib\SDL2-2.24.0\include;%(AdditionalIncludeDirectories) - stdcpplatest - /utf-8 - true - Level1 - - - Console - $(ProjectDir)RT64\$(Configuration)\RT64.lib;$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.lib;%(AdditionalDependencies) - mainCRTStartup - - - XCOPY "$(ProjectDir)RT64\$(Configuration)\*.dll" "$(TargetDir)" /S /Y -XCOPY "$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.dll" "$(TargetDir)" /S /Y - - - - - $(ProjectDir)..;$(ProjectDir)thirdparty;$(ProjectDir)Lib\SDL2-2.24.0\include;%(AdditionalIncludeDirectories) - stdcpplatest - /utf-8 - true - Level1 - - - Console - $(ProjectDir)RT64\$(Configuration)\RT64.lib;$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.lib;%(AdditionalDependencies) - mainCRTStartup - - - XCOPY "$(ProjectDir)RT64\$(Configuration)\*.dll" "$(TargetDir)" /S /Y -XCOPY "$(ProjectDir)Lib\SDL2-2.24.0\lib\$(Platform)\SDL2.dll" "$(TargetDir)" /S /Y - - - - - - MaxSpeed - MaxSpeed - Default - - - - - - - - - - - - MaxSpeed - MaxSpeed - Default - false - AnySuitable - AnySuitable - - - MaxSpeed - MaxSpeed - Default - false - AnySuitable - AnySuitable - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {d080900b-5c12-45fc-941b-515a5fea7594} - - - - - - \ No newline at end of file diff --git a/test/RecompTest.vcxproj.filters b/test/RecompTest.vcxproj.filters deleted file mode 100644 index e40ba5f..0000000 --- a/test/RecompTest.vcxproj.filters +++ /dev/null @@ -1,30279 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav - - - {686e650c-616a-4d1a-84c9-f967294cc727} - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Source Files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Source Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/test/portultra/audio.cpp b/test/portultra/audio.cpp deleted file mode 100644 index ccd35c4..0000000 --- a/test/portultra/audio.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "ultra64.h" -#include "multilibultra.hpp" -#include "SDL.h" -#include "SDL_audio.h" -#include - -static SDL_AudioDeviceID audio_device = 0; -static uint32_t sample_rate = 48000; - -void Multilibultra::init_audio() { - // Initialize SDL audio. - SDL_InitSubSystem(SDL_INIT_AUDIO); - // Pick an initial dummy sample rate; this will be set by the game later to the true sample rate. - set_audio_frequency(48000); -} - -void Multilibultra::set_audio_frequency(uint32_t freq) { - if (audio_device != 0) { - SDL_CloseAudioDevice(audio_device); - } - SDL_AudioSpec spec_desired{ - .freq = (int)freq, - .format = AUDIO_S16, - .channels = 2, - .silence = 0, // calculated - .samples = 0x100, // Fairly small sample count to reduce the latency of internal buffering - .padding = 0, // unused - .size = 0, // calculated - .callback = nullptr,//feed_audio, // Use a callback as QueueAudio causes popping - .userdata = nullptr - }; - - audio_device = SDL_OpenAudioDevice(nullptr, false, &spec_desired, nullptr, 0); - if (audio_device == 0) { - printf("SDL Error: %s\n", SDL_GetError()); - fflush(stdout); - assert(false); - } - SDL_PauseAudioDevice(audio_device, 0); - sample_rate = freq; -} - -void Multilibultra::queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data_, uint32_t byte_count) { - // Buffer for holding the output of swapping the audio channels. This is reused across - // calls to reduce runtime allocations. - static std::vector swap_buffer; - - // Ensure that the byte count is an integer multiple of samples. - assert((byte_count & 1) == 0); - - // Calculate the number of samples from the number of bytes. - uint32_t sample_count = byte_count / sizeof(s16); - - // Make sure the swap buffer is large enough to hold all the incoming audio data. - if (sample_count > swap_buffer.size()) { - swap_buffer.resize(sample_count); - } - - // Swap the audio channels into the swap buffer to correct for the address xor caused by endianness handling. - s16* audio_data = TO_PTR(s16, audio_data_); - for (size_t i = 0; i < sample_count; i += 2) { - swap_buffer[i + 0] = audio_data[i + 1]; - swap_buffer[i + 1] = audio_data[i + 0]; - } - - // Queue the swapped audio data. - SDL_QueueAudio(audio_device, swap_buffer.data(), byte_count); -} - -// If there's ever any audio popping, check here first. Some games are very sensitive to -// the remaining sample count and reporting a number that's too high here can lead to issues. -// Reporting a number that's too low can lead to audio lag in some games. -uint32_t Multilibultra::get_remaining_audio_bytes() { - // Get the number of remaining buffered audio bytes. - uint32_t buffered_byte_count = SDL_GetQueuedAudioSize(audio_device); - - // Adjust the reported count to be four refreshes in the future, which helps ensure that - // there are enough samples even if the game experiences a small amount of lag. This prevents - // audio popping on games that use the buffered audio byte count to determine how many samples - // to generate. - uint32_t samples_per_vi = (sample_rate / 60); - if (buffered_byte_count > (4u * samples_per_vi)) { - buffered_byte_count -= (4u * samples_per_vi); - } else { - buffered_byte_count = 0; - } - return buffered_byte_count; -} diff --git a/test/portultra/events.cpp b/test/portultra/events.cpp deleted file mode 100644 index de337bd..0000000 --- a/test/portultra/events.cpp +++ /dev/null @@ -1,335 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "SDL.h" -#include "blockingconcurrentqueue.h" - -#include "ultra64.h" -#include "multilibultra.hpp" -#include "recomp.h" -#include "../src/rsp.h" - -struct SpTaskAction { - OSTask task; -}; - -struct SwapBuffersAction { - uint32_t origin; -}; - -using Action = std::variant; - -static struct { - struct { - std::thread thread; - PTR(OSMesgQueue) mq = NULLPTR; - PTR(void) current_buffer = NULLPTR; - PTR(void) next_buffer = NULLPTR; - OSMesg msg = (OSMesg)0; - int retrace_count = 1; - } vi; - struct { - std::thread thread; - PTR(OSMesgQueue) mq = NULLPTR; - OSMesg msg = (OSMesg)0; - } sp; - struct { - std::thread thread; - PTR(OSMesgQueue) mq = NULLPTR; - OSMesg msg = (OSMesg)0; - } dp; - struct { - std::thread thread; - PTR(OSMesgQueue) mq = NULLPTR; - OSMesg msg = (OSMesg)0; - } ai; - struct { - std::thread thread; - PTR(OSMesgQueue) mq = NULLPTR; - OSMesg msg = (OSMesg)0; - } si; - // The same message queue may be used for multiple events, so share a mutex for all of them - std::mutex message_mutex; - uint8_t* rdram; - moodycamel::BlockingConcurrentQueue action_queue{}; -} events_context{}; - -extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) { - OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_); - std::lock_guard lock{ events_context.message_mutex }; - - switch (event_id) { - case OS_EVENT_SP: - events_context.sp.msg = msg; - events_context.sp.mq = mq_; - break; - case OS_EVENT_DP: - events_context.dp.msg = msg; - events_context.dp.mq = mq_; - break; - case OS_EVENT_AI: - events_context.ai.msg = msg; - events_context.ai.mq = mq_; - break; - case OS_EVENT_SI: - events_context.si.msg = msg; - events_context.si.mq = mq_; - } -} - -extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 retrace_count) { - std::lock_guard lock{ events_context.message_mutex }; - events_context.vi.mq = mq_; - events_context.vi.msg = msg; - events_context.vi.retrace_count = retrace_count; -} - -void vi_thread_func() { - using namespace std::chrono_literals; - - uint64_t total_vis = 0; - int remaining_retraces = events_context.vi.retrace_count; - - while (true) { - // Determine the next VI time (more accurate than adding 16ms each VI interrupt) - auto next = Multilibultra::get_start() + (total_vis * 1000000us) / (60 * Multilibultra::get_speed_multiplier()); - //if (next > std::chrono::system_clock::now()) { - // printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n", - // (next - std::chrono::system_clock::now()) / 1us, - // (std::chrono::system_clock::now() - events_context.start) / 1us, - // (next - events_context.start) / 1us); - //} else { - // printf("No need to sleep\n"); - //} - std::this_thread::sleep_until(next); - // Calculate how many VIs have passed - uint64_t new_total_vis = (Multilibultra::time_since_start() * (60 * Multilibultra::get_speed_multiplier()) / 1000ms) + 1; - if (new_total_vis > total_vis + 1) { - //printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1); - } - total_vis = new_total_vis; - - remaining_retraces--; - - { - std::lock_guard lock{ events_context.message_mutex }; - uint8_t* rdram = events_context.rdram; - if (remaining_retraces == 0) { - remaining_retraces = events_context.vi.retrace_count; - - if (events_context.vi.mq != NULLPTR) { - if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) { - //printf("Game skipped a VI frame!\n"); - } - } - } - if (events_context.ai.mq != NULLPTR) { - if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) { - //printf("Game skipped a AI frame!\n"); - } - } - } - } -} - -void sp_complete() { - uint8_t* rdram = events_context.rdram; - std::lock_guard lock{ events_context.message_mutex }; - osSendMesg(PASS_RDRAM events_context.sp.mq, events_context.sp.msg, OS_MESG_NOBLOCK); -} - -void dp_complete() { - uint8_t* rdram = events_context.rdram; - std::lock_guard lock{ events_context.message_mutex }; - osSendMesg(PASS_RDRAM events_context.dp.mq, events_context.dp.msg, OS_MESG_NOBLOCK); -} - -void RT64Init(uint8_t* rom, uint8_t* rdram); -void RT64SendDL(uint8_t* rdram, const OSTask* task); -void RT64UpdateScreen(uint32_t vi_origin); - -std::unordered_map button_map{ - { SDL_Scancode::SDL_SCANCODE_LEFT, 0x0002 }, // c left - { SDL_Scancode::SDL_SCANCODE_RIGHT, 0x0001 }, // c right - { SDL_Scancode::SDL_SCANCODE_UP, 0x0008 }, // c up - { SDL_Scancode::SDL_SCANCODE_DOWN, 0x0004 }, // c down - { SDL_Scancode::SDL_SCANCODE_RETURN, 0x1000 }, // start - { SDL_Scancode::SDL_SCANCODE_SPACE, 0x8000 }, // a - { SDL_Scancode::SDL_SCANCODE_LSHIFT, 0x4000 }, // b - { SDL_Scancode::SDL_SCANCODE_Q, 0x2000 }, // z - { SDL_Scancode::SDL_SCANCODE_E, 0x0020 }, // l - { SDL_Scancode::SDL_SCANCODE_R, 0x0010 }, // r - { SDL_Scancode::SDL_SCANCODE_J, 0x0200 }, // dpad left - { SDL_Scancode::SDL_SCANCODE_L, 0x0100 }, // dpad right - { SDL_Scancode::SDL_SCANCODE_I, 0x0800 }, // dpad up - { SDL_Scancode::SDL_SCANCODE_K, 0x0400 }, // dpad down -}; - -extern int button; -extern int stick_x; -extern int stick_y; - -int sdl_event_filter(void* userdata, SDL_Event* event) { - switch (event->type) { - case SDL_EventType::SDL_KEYUP: - case SDL_EventType::SDL_KEYDOWN: - { - const Uint8* key_states = SDL_GetKeyboardState(nullptr); - int new_button = 0; - - for (const auto& mapping : button_map) { - if (key_states[mapping.first]) { - new_button |= mapping.second; - } - } - - button = new_button; - - stick_x = 127 * (key_states[SDL_Scancode::SDL_SCANCODE_D] - key_states[SDL_Scancode::SDL_SCANCODE_A]); - stick_y = 127 * (key_states[SDL_Scancode::SDL_SCANCODE_W] - key_states[SDL_Scancode::SDL_SCANCODE_S]); - } - break; - case SDL_EventType::SDL_QUIT: - std::quick_exit(ERROR_SUCCESS); - break; - } - return 1; -} - -uint8_t dmem[0x1000]; -uint16_t rspReciprocals[512]; -uint16_t rspInverseSquareRoots[512]; - -using RspUcodeFunc = RspExitReason(uint8_t* rdram); -extern RspUcodeFunc njpgdspMain; -extern RspUcodeFunc aspMain; - -// From Ares emulator. For license details, see rsp_vu.h -void rsp_constants_init() { - rspReciprocals[0] = u16(~0); - for (u16 index = 1; index < 512; index++) { - u64 a = index + 512; - u64 b = (u64(1) << 34) / a; - rspReciprocals[index] = u16(b + 1 >> 8); - } - - for (u16 index = 0; index < 512; index++) { - u64 a = index + 512 >> ((index % 2 == 1) ? 1 : 0); - u64 b = 1 << 17; - //find the largest b where b < 1.0 / sqrt(a) - while (a * (b + 1) * (b + 1) < (u64(1) << 44)) b++; - rspInverseSquareRoots[index] = u16(b >> 1); - } -} - -// Runs a recompiled RSP microcode -void run_rsp_microcode(uint8_t* rdram, const OSTask* task, RspUcodeFunc* ucode_func) { - // Load the OSTask into DMEM - memcpy(&dmem[0xFC0], task, sizeof(OSTask)); - // Load the ucode data into DMEM - dma_rdram_to_dmem(rdram, 0x0000, task->t.ucode_data, 0xF80 - 1); - // Run the ucode - RspExitReason exit_reason = ucode_func(rdram); - // Ensure that the ucode exited correctly - assert(exit_reason == RspExitReason::Broke); - sp_complete(); -} - -void event_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* events_thread_ready) { - using namespace std::chrono_literals; - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { - fprintf(stderr, "Failed to initialize SDL2: %s\n", SDL_GetError()); - std::quick_exit(EXIT_FAILURE); - } - RT64Init(rom, rdram); - SDL_Window* window = SDL_GetWindowFromID(1); - // TODO set this window title in RT64, create the window here and send it to RT64, or something else entirely - // as the current window name visibly changes as RT64 is initialized - SDL_SetWindowTitle(window, "Recomp"); - //SDL_SetEventFilter(sdl_event_filter, nullptr); - - rsp_constants_init(); - - // Notify the caller thread that this thread is ready. - events_thread_ready->test_and_set(); - events_thread_ready->notify_all(); - - while (true) { - // Try to pull an action from the queue - Action action; - if (events_context.action_queue.wait_dequeue_timed(action, 1ms)) { - // Determine the action type and act on it - if (const auto* task_action = std::get_if(&action)) { - if (task_action->task.t.type == M_GFXTASK) { - // (TODO let RT64 do this) Tell the game that the RSP and RDP tasks are complete - RT64SendDL(rdram, &task_action->task); - sp_complete(); - dp_complete(); - } else if (task_action->task.t.type == M_AUDTASK) { - run_rsp_microcode(rdram, &task_action->task, aspMain); - } else if (task_action->task.t.type == M_NJPEGTASK) { - run_rsp_microcode(rdram, &task_action->task, njpgdspMain); - } else { - fprintf(stderr, "Unknown task type: %" PRIu32 "\n", task_action->task.t.type); - assert(false); - std::quick_exit(EXIT_FAILURE); - } - } else if (const auto* swap_action = std::get_if(&action)) { - events_context.vi.current_buffer = events_context.vi.next_buffer; - RT64UpdateScreen(swap_action->origin); - } - } - - // Handle events - constexpr int max_events_per_frame = 16; - SDL_Event cur_event; - int i = 0; - while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event)) { - sdl_event_filter(nullptr, &cur_event); - } - //SDL_PumpEvents(); - } -} - -extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) { - events_context.vi.next_buffer = frameBufPtr; - events_context.action_queue.enqueue(SwapBuffersAction{ osVirtualToPhysical(frameBufPtr) + 640 }); -} - -extern "C" PTR(void) osViGetNextFramebuffer() { - return events_context.vi.next_buffer; -} - -extern "C" PTR(void) osViGetCurrentFramebuffer() { - return events_context.vi.current_buffer; -} - -void Multilibultra::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) { - OSTask* task = TO_PTR(OSTask, task_); - events_context.action_queue.enqueue(SpTaskAction{ *task }); -} - -void Multilibultra::send_si_message() { - uint8_t* rdram = events_context.rdram; - osSendMesg(PASS_RDRAM events_context.si.mq, events_context.si.msg, OS_MESG_NOBLOCK); -} - -void Multilibultra::init_events(uint8_t* rdram, uint8_t* rom) { - std::atomic_flag events_thread_ready; - events_context.rdram = rdram; - events_context.sp.thread = std::thread{ event_thread_func, rdram, rom, &events_thread_ready }; - - // Wait for the event thread to be ready before continuing to prevent the game from - // running before we're able to handle RSP tasks. - events_thread_ready.wait(false); - - events_context.vi.thread = std::thread{ vi_thread_func }; -} diff --git a/test/portultra/mesgqueue.cpp b/test/portultra/mesgqueue.cpp deleted file mode 100644 index fd7a0bb..0000000 --- a/test/portultra/mesgqueue.cpp +++ /dev/null @@ -1,194 +0,0 @@ -#include -#include - -#include "ultra64.h" -#include "multilibultra.hpp" -#include "recomp.h" - -extern "C" void osCreateMesgQueue(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg, s32 count) { - OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_); - mq->blocked_on_recv = NULLPTR; - mq->blocked_on_send = NULLPTR; - mq->msgCount = count; - mq->msg = msg; - mq->validCount = 0; - mq->first = 0; -} - -s32 MQ_GET_COUNT(OSMesgQueue *mq) { - return mq->validCount; -} - -s32 MQ_IS_EMPTY(OSMesgQueue *mq) { - return mq->validCount == 0; -} - -s32 MQ_IS_FULL(OSMesgQueue* mq) { - return MQ_GET_COUNT(mq) >= mq->msgCount; -} - -void thread_queue_insert(RDRAM_ARG PTR(OSThread)* queue, PTR(OSThread) toadd_) { - PTR(OSThread)* cur = queue; - OSThread* toadd = TO_PTR(OSThread, toadd_); - while (*cur && TO_PTR(OSThread, *cur)->priority > toadd->priority) { - cur = &TO_PTR(OSThread, *cur)->next; - } - toadd->next = (*cur); - *cur = toadd_; -} - -OSThread* thread_queue_pop(RDRAM_ARG PTR(OSThread)* queue) { - PTR(OSThread) ret = *queue; - *queue = TO_PTR(OSThread, ret)->next; - return TO_PTR(OSThread, ret); -} - -bool thread_queue_empty(RDRAM_ARG PTR(OSThread)* queue) { - return *queue == NULLPTR; -} - -extern "C" s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) { - OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_); - - // Prevent accidentally blocking anything that isn't a game thread - if (!Multilibultra::is_game_thread()) { - flags = OS_MESG_NOBLOCK; - } - - Multilibultra::disable_preemption(); - - if (flags == OS_MESG_NOBLOCK) { - // If non-blocking, fail if the queue is full - if (MQ_IS_FULL(mq)) { - Multilibultra::enable_preemption(); - return -1; - } - } else { - // Otherwise, yield this thread until the queue has room - while (MQ_IS_FULL(mq)) { - debug_printf("[Message Queue] Thread %d is blocked on send\n", TO_PTR(OSThread, Multilibultra::this_thread())->id); - thread_queue_insert(PASS_RDRAM &mq->blocked_on_send, Multilibultra::this_thread()); - Multilibultra::enable_preemption(); - Multilibultra::pause_self(PASS_RDRAM1); - Multilibultra::disable_preemption(); - } - } - - s32 last = (mq->first + mq->validCount) % mq->msgCount; - TO_PTR(OSMesg, mq->msg)[last] = msg; - mq->validCount++; - - OSThread* to_run = nullptr; - - if (!thread_queue_empty(PASS_RDRAM &mq->blocked_on_recv)) { - to_run = thread_queue_pop(PASS_RDRAM &mq->blocked_on_recv); - } - - Multilibultra::enable_preemption(); - if (to_run) { - debug_printf("[Message Queue] Thread %d is unblocked\n", to_run->id); - if (Multilibultra::is_game_thread()) { - OSThread* self = TO_PTR(OSThread, Multilibultra::this_thread()); - if (to_run->priority > self->priority) { - Multilibultra::swap_to_thread(PASS_RDRAM to_run); - } else { - Multilibultra::schedule_running_thread(to_run); - } - } else { - Multilibultra::schedule_running_thread(to_run); - } - } - return 0; -} - -extern "C" s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) { - OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_); - Multilibultra::disable_preemption(); - - if (flags == OS_MESG_NOBLOCK) { - // If non-blocking, fail if the queue is full - if (MQ_IS_FULL(mq)) { - Multilibultra::enable_preemption(); - return -1; - } - } else { - // Otherwise, yield this thread in a loop until the queue is no longer full - while (MQ_IS_FULL(mq)) { - debug_printf("[Message Queue] Thread %d is blocked on jam\n", TO_PTR(OSThread, Multilibultra::this_thread())->id); - thread_queue_insert(PASS_RDRAM &mq->blocked_on_send, Multilibultra::this_thread()); - Multilibultra::enable_preemption(); - Multilibultra::pause_self(PASS_RDRAM1); - Multilibultra::disable_preemption(); - } - } - - mq->first = (mq->first + mq->msgCount - 1) % mq->msgCount; - TO_PTR(OSMesg, mq->msg)[mq->first] = msg; - mq->validCount++; - - OSThread *to_run = nullptr; - - if (!thread_queue_empty(PASS_RDRAM &mq->blocked_on_recv)) { - to_run = thread_queue_pop(PASS_RDRAM &mq->blocked_on_recv); - } - - Multilibultra::enable_preemption(); - if (to_run) { - debug_printf("[Message Queue] Thread %d is unblocked\n", to_run->id); - OSThread *self = TO_PTR(OSThread, Multilibultra::this_thread()); - if (to_run->priority > self->priority) { - Multilibultra::swap_to_thread(PASS_RDRAM to_run); - } else { - Multilibultra::schedule_running_thread(to_run); - } - } - return 0; -} - -extern "C" s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, s32 flags) { - OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_); - OSMesg *msg = TO_PTR(OSMesg, msg_); - Multilibultra::disable_preemption(); - - if (flags == OS_MESG_NOBLOCK) { - // If non-blocking, fail if the queue is empty - if (MQ_IS_EMPTY(mq)) { - Multilibultra::enable_preemption(); - return -1; - } - } else { - // Otherwise, yield this thread in a loop until the queue is no longer full - while (MQ_IS_EMPTY(mq)) { - debug_printf("[Message Queue] Thread %d is blocked on receive\n", TO_PTR(OSThread, Multilibultra::this_thread())->id); - thread_queue_insert(PASS_RDRAM &mq->blocked_on_recv, Multilibultra::this_thread()); - Multilibultra::enable_preemption(); - Multilibultra::pause_self(PASS_RDRAM1); - Multilibultra::disable_preemption(); - } - } - - if (msg_ != NULLPTR) { - *msg = TO_PTR(OSMesg, mq->msg)[mq->first]; - } - - mq->first = (mq->first + 1) % mq->msgCount; - mq->validCount--; - - OSThread *to_run = nullptr; - - if (!thread_queue_empty(PASS_RDRAM &mq->blocked_on_send)) { - to_run = thread_queue_pop(PASS_RDRAM &mq->blocked_on_send); - } - - Multilibultra::enable_preemption(); - if (to_run) { - debug_printf("[Message Queue] Thread %d is unblocked\n", to_run->id); - OSThread *self = TO_PTR(OSThread, Multilibultra::this_thread()); - if (to_run->priority > self->priority) { - Multilibultra::swap_to_thread(PASS_RDRAM to_run); - } else { - Multilibultra::schedule_running_thread(to_run); - } - } - return 0; -} diff --git a/test/portultra/misc_ultra.cpp b/test/portultra/misc_ultra.cpp deleted file mode 100644 index 2b794b7..0000000 --- a/test/portultra/misc_ultra.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "ultra64.h" - -#define K0BASE 0x80000000 -#define K1BASE 0xA0000000 -#define K2BASE 0xC0000000 -#define IS_KSEG0(x) ((u32)(x) >= K0BASE && (u32)(x) < K1BASE) -#define IS_KSEG1(x) ((u32)(x) >= K1BASE && (u32)(x) < K2BASE) -#define K0_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF) /* kseg0 to physical */ -#define K1_TO_PHYS(x) ((u32)(x)&0x1FFFFFFF) /* kseg1 to physical */ - -u32 osVirtualToPhysical(PTR(void) addr) { - uintptr_t addr_val = (uintptr_t)addr; - if (IS_KSEG0(addr_val)) { - return K0_TO_PHYS(addr_val); - } else if (IS_KSEG1(addr_val)) { - return K1_TO_PHYS(addr_val); - } else { - // TODO handle TLB mappings - return (u32)addr_val; - } -} - diff --git a/test/portultra/multilibultra.hpp b/test/portultra/multilibultra.hpp deleted file mode 100644 index 7845eb2..0000000 --- a/test/portultra/multilibultra.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef __MULTILIBULTRA_HPP__ -#define __MULTILIBULTRA_HPP__ - -#include -#include -#include -#include - -#include "ultra64.h" -#include "platform_specific.h" - -struct UltraThreadContext { - std::thread host_thread; - std::atomic_bool running; - std::atomic_bool initialized; -}; - -namespace Multilibultra { - -void preinit(uint8_t* rdram, uint8_t* rom); -void save_init(); -void native_init(); -void init_scheduler(); -void init_events(uint8_t* rdram, uint8_t* rom); -void init_timers(RDRAM_ARG1); -void native_thread_init(OSThread *t); -void set_self_paused(RDRAM_ARG1); -void wait_for_resumed(RDRAM_ARG1); -void swap_to_thread(RDRAM_ARG OSThread *to); -void pause_thread_impl(OSThread *t); -void pause_thread_native_impl(OSThread *t); -void resume_thread_impl(OSThread *t); -void resume_thread_native_impl(OSThread *t); -void schedule_running_thread(OSThread *t); -void stop_thread(OSThread *t); -void pause_self(RDRAM_ARG1); -void cleanup_thread(OSThread *t); -PTR(OSThread) this_thread(); -void disable_preemption(); -void enable_preemption(); -void notify_scheduler(); -void reprioritize_thread(OSThread *t, OSPri pri); -void set_main_thread(); -bool is_game_thread(); -void submit_rsp_task(RDRAM_ARG PTR(OSTask) task); -void send_si_message(); -uint32_t get_speed_multiplier(); -std::chrono::system_clock::time_point get_start(); -std::chrono::system_clock::duration time_since_start(); -void init_audio(); -void set_audio_frequency(uint32_t freq); -void queue_audio_buffer(RDRAM_ARG PTR(s16) audio_data, uint32_t byte_count); -uint32_t get_remaining_audio_bytes(); - -class preemption_guard { -public: - preemption_guard(); - ~preemption_guard(); -private: - std::lock_guard lock; -}; - -} // namespace Multilibultra - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -#define debug_printf(...) -//#define debug_printf(...) printf(__VA_ARGS__); - -#endif diff --git a/test/portultra/platform_specific.h b/test/portultra/platform_specific.h deleted file mode 100644 index b9d1823..0000000 --- a/test/portultra/platform_specific.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __PLATFORM_SPECIFIC_H__ -#define __PLATFORM_SPECIFIC_H__ - -#if defined(__linux__) - -//#include -// -//typedef struct { -// pthread_t t; -// pthread_barrier_t pause_barrier; -// pthread_mutex_t pause_mutex; -// pthread_cond_t pause_cond; -// void (*entrypoint)(void *); -// void *arg; -//} OSThreadNative; - -#elif defined(_WIN32) - -//#include -// -//typedef struct { -// pthread_t t; -// pthread_barrier_t pause_barrier; -// pthread_mutex_t pause_mutex; -// pthread_cond_t pause_cond; -// void (*entrypoint)(void*); -// void* arg; -//} OSThreadNative; - -#endif - -#endif \ No newline at end of file diff --git a/test/portultra/port_main.c b/test/portultra/port_main.c deleted file mode 100644 index 968b4d3..0000000 --- a/test/portultra/port_main.c +++ /dev/null @@ -1,83 +0,0 @@ -#if 0 - -#include -#include -#include "ultra64.h" - -#define THREAD_STACK_SIZE 0x1000 - -u8 idle_stack[THREAD_STACK_SIZE] ALIGNED(16); -u8 main_stack[THREAD_STACK_SIZE] ALIGNED(16); -u8 thread3_stack[THREAD_STACK_SIZE] ALIGNED(16); -u8 thread4_stack[THREAD_STACK_SIZE] ALIGNED(16); - -OSThread idle_thread; -OSThread main_thread; -OSThread thread3; -OSThread thread4; - -OSMesgQueue queue; -OSMesg buf[1]; - -void thread3_func(UNUSED void *arg) { - OSMesg val; - printf("Thread3 recv\n"); - fflush(stdout); - osRecvMesg(&queue, &val, OS_MESG_BLOCK); - printf("Thread3 complete: %d\n", (int)(intptr_t)val); - fflush(stdout); -} - -void thread4_func(void *arg) { - printf("Thread4 send %d\n", (int)(intptr_t)arg); - fflush(stdout); - osSendMesg(&queue, arg, OS_MESG_BLOCK); - printf("Thread4 complete\n"); - fflush(stdout); -} - -void main_thread_func(UNUSED void* arg) { - osCreateMesgQueue(&queue, buf, sizeof(buf) / sizeof(buf[0])); - - printf("main thread creating thread 3\n"); - osCreateThread(&thread3, 3, thread3_func, NULL, &thread3_stack[THREAD_STACK_SIZE], 14); - printf("main thread starting thread 3\n"); - osStartThread(&thread3); - - printf("main thread creating thread 4\n"); - osCreateThread(&thread4, 4, thread4_func, (void*)10, &thread4_stack[THREAD_STACK_SIZE], 13); - printf("main thread starting thread 4\n"); - osStartThread(&thread4); - - while (1) { - printf("main thread doin stuff\n"); - sleep(1); - } -} - -void idle_thread_func(UNUSED void* arg) { - printf("idle thread\n"); - printf("creating main thread\n"); - osCreateThread(&main_thread, 2, main_thread_func, NULL, &main_stack[THREAD_STACK_SIZE], 11); - printf("starting main thread\n"); - osStartThread(&main_thread); - - // Set this thread's priority to 0, making it the idle thread - osSetThreadPri(NULL, 0); - - // idle - while (1) { - printf("idle thread doin stuff\n"); - sleep(1); - } -} - -void bootproc(void) { - osInitialize(); - - osCreateThread(&idle_thread, 1, idle_thread_func, NULL, &idle_stack[THREAD_STACK_SIZE], 127); - printf("Starting idle thread\n"); - osStartThread(&idle_thread); -} - -#endif diff --git a/test/portultra/scheduler.cpp b/test/portultra/scheduler.cpp deleted file mode 100644 index 014739c..0000000 --- a/test/portultra/scheduler.cpp +++ /dev/null @@ -1,286 +0,0 @@ -#include -#include -#include -#include - -#include "multilibultra.hpp" - -class OSThreadComparator { -public: - bool operator() (OSThread *a, OSThread *b) const { - return a->priority < b->priority; - } -}; - -class thread_queue_t : public std::priority_queue, OSThreadComparator> { -public: - // TODO comment this - bool remove(const OSThread* value) { - auto it = std::find(this->c.begin(), this->c.end(), value); - - if (it == this->c.end()) { - return false; - } - - if (it == this->c.begin()) { - // deque the top element - this->pop(); - } else { - // remove element and re-heap - this->c.erase(it); - std::make_heap(this->c.begin(), this->c.end(), this->comp); - } - - return true; - } -}; - -static struct { - std::vector to_schedule; - std::vector to_stop; - std::vector to_cleanup; - std::vector> to_reprioritize; - std::mutex mutex; - // OSThread* running_thread; - std::atomic_int notify_count; - std::atomic_int action_count; - - bool can_preempt; - std::mutex premption_mutex; -} scheduler_context{}; - -void handle_thread_queueing(thread_queue_t& running_thread_queue) { - std::lock_guard lock{scheduler_context.mutex}; - - if (!scheduler_context.to_schedule.empty()) { - OSThread* to_schedule = scheduler_context.to_schedule.back(); - scheduler_context.to_schedule.pop_back(); - scheduler_context.action_count.fetch_sub(1); - debug_printf("[Scheduler] Scheduling thread %d\n", to_schedule->id); - running_thread_queue.push(to_schedule); - } -} - -void handle_thread_stopping(thread_queue_t& running_thread_queue) { - std::lock_guard lock{scheduler_context.mutex}; - - while (!scheduler_context.to_stop.empty()) { - OSThread* to_stop = scheduler_context.to_stop.back(); - scheduler_context.to_stop.pop_back(); - scheduler_context.action_count.fetch_sub(1); - debug_printf("[Scheduler] Stopping thread %d\n", to_stop->id); - running_thread_queue.remove(to_stop); - } -} - -void handle_thread_cleanup(thread_queue_t& running_thread_queue) { - std::lock_guard lock{scheduler_context.mutex}; - - while (!scheduler_context.to_cleanup.empty()) { - OSThread* to_cleanup = scheduler_context.to_cleanup.back(); - scheduler_context.to_cleanup.pop_back(); - scheduler_context.action_count.fetch_sub(1); - - debug_printf("[Scheduler] Destroying thread %d\n", to_cleanup->id); - running_thread_queue.remove(to_cleanup); - to_cleanup->context->host_thread.join(); - delete to_cleanup->context; - } -} - -void handle_thread_reprioritization(thread_queue_t& running_thread_queue) { - std::lock_guard lock{scheduler_context.mutex}; - - while (!scheduler_context.to_reprioritize.empty()) { - const std::pair to_reprioritize = scheduler_context.to_reprioritize.back(); - scheduler_context.to_reprioritize.pop_back(); - scheduler_context.action_count.fetch_sub(1); - - debug_printf("[Scheduler] Reprioritizing thread %d to %d\n", to_reprioritize.first->id, to_reprioritize.second); - running_thread_queue.remove(to_reprioritize.first); - to_reprioritize.first->priority = to_reprioritize.second; - running_thread_queue.push(to_reprioritize.first); - } -} - -void handle_scheduler_notifications() { - std::lock_guard lock{scheduler_context.mutex}; - int32_t notify_count = scheduler_context.notify_count.exchange(0); - if (notify_count) { - debug_printf("Received %d notifications\n", notify_count); - scheduler_context.action_count.fetch_sub(notify_count); - } -} - -void swap_running_thread(thread_queue_t& running_thread_queue, OSThread*& cur_running_thread) { - if (running_thread_queue.size() > 0) { - OSThread* new_running_thread = running_thread_queue.top(); - if (cur_running_thread != new_running_thread) { - if (cur_running_thread && cur_running_thread->state == OSThreadState::RUNNING) { - debug_printf("[Scheduler] Need to wait for thread %d to pause itself\n", cur_running_thread->id); - return; - //debug_printf("[Scheduler] Switching execution from thread %d (%d) to thread %d (%d)\n", - // cur_running_thread->id, cur_running_thread->priority, - // new_running_thread->id, new_running_thread->priority); - //Multilibultra::pause_thread_impl(cur_running_thread); - } else { - debug_printf("[Scheduler] Switching execution to thread %d (%d)\n", new_running_thread->id, new_running_thread->priority); - } - Multilibultra::resume_thread_impl(new_running_thread); - cur_running_thread = new_running_thread; - } else if (cur_running_thread && cur_running_thread->state != OSThreadState::RUNNING) { - Multilibultra::resume_thread_impl(cur_running_thread); - } - } else { - cur_running_thread = nullptr; - } -} - -void scheduler_func() { - thread_queue_t running_thread_queue{}; - OSThread* cur_running_thread = nullptr; - - while (true) { - OSThread* old_running_thread = cur_running_thread; - scheduler_context.action_count.wait(0); - - std::lock_guard lock{scheduler_context.premption_mutex}; - - // Handle notifications - handle_scheduler_notifications(); - - // Handle stopping threads - handle_thread_stopping(running_thread_queue); - - // Handle cleaning up threads - handle_thread_cleanup(running_thread_queue); - - // Handle queueing threads to run - handle_thread_queueing(running_thread_queue); - - // Handle threads that have changed priority - handle_thread_reprioritization(running_thread_queue); - - // Determine which thread to run, stopping the current running thread if necessary - swap_running_thread(running_thread_queue, cur_running_thread); - - std::this_thread::yield(); - if (old_running_thread != cur_running_thread && old_running_thread && cur_running_thread) { - debug_printf("[Scheduler] Swapped from Thread %d (%d) to Thread %d (%d)\n", - old_running_thread->id, old_running_thread->priority, cur_running_thread->id, cur_running_thread->priority); - } - } -} - -extern "C" void do_yield() { - std::this_thread::yield(); -} - -namespace Multilibultra { - -void init_scheduler() { - scheduler_context.can_preempt = true; - std::thread scheduler_thread{scheduler_func}; - scheduler_thread.detach(); -} - -void schedule_running_thread(OSThread *t) { - debug_printf("[Scheduler] Queuing Thread %d to be scheduled\n", t->id); - std::lock_guard lock{scheduler_context.mutex}; - scheduler_context.to_schedule.push_back(t); - scheduler_context.action_count.fetch_add(1); - scheduler_context.action_count.notify_all(); -} - -void swap_to_thread(RDRAM_ARG OSThread *to) { - OSThread *self = TO_PTR(OSThread, Multilibultra::this_thread()); - debug_printf("[Scheduler] Scheduling swap from thread %d to %d\n", self->id, to->id); - { - std::lock_guard lock{scheduler_context.mutex}; - scheduler_context.to_schedule.push_back(to); - Multilibultra::set_self_paused(PASS_RDRAM1); - scheduler_context.action_count.fetch_add(1); - scheduler_context.action_count.notify_all(); - } - Multilibultra::wait_for_resumed(PASS_RDRAM1); -} - -void reprioritize_thread(OSThread *t, OSPri pri) { - debug_printf("[Scheduler] Adjusting Thread %d priority to %d\n", t->id, pri); - std::lock_guard lock{scheduler_context.mutex}; - scheduler_context.to_reprioritize.emplace_back(t, pri); - scheduler_context.action_count.fetch_add(1); - scheduler_context.action_count.notify_all(); -} - -void pause_self(RDRAM_ARG1) { - OSThread *self = TO_PTR(OSThread, Multilibultra::this_thread()); - debug_printf("[Scheduler] Thread %d pausing itself\n", self->id); - { - std::lock_guard lock{scheduler_context.mutex}; - Multilibultra::set_self_paused(PASS_RDRAM1); - scheduler_context.to_stop.push_back(self); - scheduler_context.action_count.fetch_add(1); - scheduler_context.action_count.notify_all(); - } - Multilibultra::wait_for_resumed(PASS_RDRAM1); -} - -void stop_thread(OSThread *t) { - debug_printf("[Scheduler] Queuing Thread %d to be stopped\n", t->id); - { - std::lock_guard lock{scheduler_context.mutex}; - scheduler_context.to_stop.push_back(t); - scheduler_context.action_count.fetch_add(1); - scheduler_context.action_count.notify_all(); - } - Multilibultra::pause_thread_impl(t); -} - -void cleanup_thread(OSThread *t) { - std::lock_guard lock{scheduler_context.mutex}; - scheduler_context.to_cleanup.push_back(t); - scheduler_context.action_count.fetch_add(1); - scheduler_context.action_count.notify_all(); -} - -void disable_preemption() { - scheduler_context.premption_mutex.lock(); - if (Multilibultra::is_game_thread()) { - scheduler_context.can_preempt = false; - } -} - -void enable_preemption() { - if (Multilibultra::is_game_thread()) { - scheduler_context.can_preempt = true; - } -#pragma warning(push) -#pragma warning( disable : 26110) - scheduler_context.premption_mutex.unlock(); -#pragma warning( pop ) -} - -// lock's constructor is called first, so can_preempt is set after locking -preemption_guard::preemption_guard() : lock{scheduler_context.premption_mutex} { - scheduler_context.can_preempt = false; -} - -// lock's destructor is called last, so can_preempt is set before unlocking -preemption_guard::~preemption_guard() { - scheduler_context.can_preempt = true; -} - -void notify_scheduler() { - std::lock_guard lock{scheduler_context.mutex}; - scheduler_context.notify_count.fetch_add(1); - scheduler_context.action_count.fetch_add(1); - scheduler_context.action_count.notify_all(); -} - -} - -extern "C" void pause_self(uint8_t* rdram) { - Multilibultra::pause_self(rdram); -} - diff --git a/test/portultra/task_pthreads.cpp b/test/portultra/task_pthreads.cpp deleted file mode 100644 index 667f022..0000000 --- a/test/portultra/task_pthreads.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _WIN32 - -// #include -// #include -// #include - -#include -#include -#include - -#include "ultra64.h" -#include "multilibultra.hpp" - -constexpr int pause_thread_signum = SIGUSR1; - -// void cleanup_current_thread(OSThread *t) { -// debug_printf("Thread cleanup %d\n", t->id); - -// // delete t->context; -// } - -void sig_handler(int signum, siginfo_t *info, void *context) { - if (signum == pause_thread_signum) { - OSThread *t = Multilibultra::this_thread(); - - debug_printf("[Sig] Thread %d paused\n", t->id); - - // Wait until the thread is marked as running again - t->context->running.wait(false); - - debug_printf("[Sig] Thread %d resumed\n", t->id); - } -} - -void Multilibultra::native_init(void) { - // Set up a signal handler to capture pause signals - struct sigaction sigact; - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_SIGINFO | SA_RESTART; - sigact.sa_sigaction = sig_handler; - - sigaction(pause_thread_signum, &sigact, nullptr); -} - -void Multilibultra::native_thread_init(OSThread *t) { - debug_printf("[Native] Init thread %d\n", t->id); -} - -void Multilibultra::pause_thread_native_impl(OSThread *t) { - debug_printf("[Native] Pause thread %d\n", t->id); - // Send a pause signal to the thread, which will trigger it to wait on its pause barrier in the signal handler - pthread_kill(t->context->host_thread.native_handle(), pause_thread_signum); -} - -void Multilibultra::resume_thread_native_impl(UNUSED OSThread *t) { - debug_printf("[Native] Resume thread %d\n", t->id); - // Nothing to do here -} - -#endif \ No newline at end of file diff --git a/test/portultra/task_win32.cpp b/test/portultra/task_win32.cpp deleted file mode 100644 index 06079ac..0000000 --- a/test/portultra/task_win32.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include - -#include "ultra64.h" -#include "multilibultra.hpp" - -extern "C" unsigned int sleep(unsigned int seconds) { - Sleep(seconds * 1000); - return 0; -} - -void Multilibultra::native_init(void) { -} - -void Multilibultra::native_thread_init(OSThread *t) { - debug_printf("[Native] Init thread %d\n", t->id); -} - -void Multilibultra::pause_thread_native_impl(OSThread *t) { - debug_printf("[Native] Pause thread %d\n", t->id); - // Pause the thread via the win32 API - SuspendThread(t->context->host_thread.native_handle()); - // Perform a synchronous action to ensure that the thread is suspended - // see: https://devblogs.microsoft.com/oldnewthing/20150205-00/?p=44743 - CONTEXT threadContext{}; - GetThreadContext(t->context->host_thread.native_handle(), &threadContext); -} - -void Multilibultra::resume_thread_native_impl(UNUSED OSThread *t) { - debug_printf("[Native] Resume thread %d\n", t->id); - // Resume the thread - ResumeThread(t->context->host_thread.native_handle()); -} diff --git a/test/portultra/threads.cpp b/test/portultra/threads.cpp deleted file mode 100644 index b83f561..0000000 --- a/test/portultra/threads.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include -#include -#include -#include - -#include "ultra64.h" -#include "multilibultra.hpp" - -// Native APIs only used to set thread names for easier debugging -#ifdef _WIN32 -#include -#endif - -extern "C" void bootproc(); - -thread_local bool is_main_thread = false; -// Whether this thread is part of the game (i.e. the start thread or one spawned by osCreateThread) -thread_local bool is_game_thread = false; -thread_local PTR(OSThread) thread_self = NULLPTR; - -void Multilibultra::set_main_thread() { - ::is_game_thread = true; - is_main_thread = true; -} - -bool Multilibultra::is_game_thread() { - return ::is_game_thread; -} - -#if 0 -int main(int argc, char** argv) { - Multilibultra::set_main_thread(); - - bootproc(); -} -#endif - -#if 1 -void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t arg); -#else -#define run_thread_function(func, sp, arg) func(arg) -#endif - -static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entrypoint, PTR(void) arg) { - OSThread *self = TO_PTR(OSThread, self_); - debug_printf("[Thread] Thread created: %d\n", self->id); - thread_self = self_; - is_game_thread = true; - - // Set the thread name -#ifdef _WIN32 - std::wstring thread_name = L"Game Thread " + std::to_wstring(self->id); - HRESULT r; - r = SetThreadDescription( - GetCurrentThread(), - thread_name.c_str() - ); -#endif - - // Perform any necessary native thread initialization. - Multilibultra::native_thread_init(self); - - // Set initialized to false to indicate that this thread can be started. - self->context->initialized.store(true); - self->context->initialized.notify_all(); - - debug_printf("[Thread] Thread waiting to be started: %d\n", self->id); - - // Wait until the thread is marked as running. - Multilibultra::set_self_paused(PASS_RDRAM1); - Multilibultra::wait_for_resumed(PASS_RDRAM1); - - debug_printf("[Thread] Thread started: %d\n", self->id); - - // Run the thread's function with the provided argument. - run_thread_function(PASS_RDRAM entrypoint, self->sp, arg); - - // Dispose of this thread after it completes. - Multilibultra::cleanup_thread(self); -} - -extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) { - OSThread* t = TO_PTR(OSThread, t_); - debug_printf("[os] Start Thread %d\n", t->id); - - // Wait until the thread is initialized to indicate that it's action_queued to be started. - t->context->initialized.wait(false); - - debug_printf("[os] Thread %d is ready to be started\n", t->id); - - if (thread_self && (t->priority > TO_PTR(OSThread, thread_self)->priority)) { - Multilibultra::swap_to_thread(PASS_RDRAM t); - } else { - Multilibultra::schedule_running_thread(t); - } - - // The main thread "becomes" the first thread started, so join on it and exit after it completes. - if (is_main_thread) { - t->context->host_thread.join(); - std::exit(EXIT_SUCCESS); - } -} - -extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_func_t) entrypoint, PTR(void) arg, PTR(void) sp, OSPri pri) { - debug_printf("[os] Create Thread %d\n", id); - OSThread *t = TO_PTR(OSThread, t_); - - t->next = NULLPTR; - t->priority = pri; - t->id = id; - t->state = OSThreadState::PAUSED; - t->sp = sp - 0x10; // Set up the first stack frame - - // Spawn a new thread, which will immediately pause itself and wait until it's been started. - t->context = new UltraThreadContext{}; - t->context->initialized.store(false); - t->context->running.store(false); - - t->context->host_thread = std::thread{_thread_func, PASS_RDRAM t_, entrypoint, arg}; -} - -extern "C" void osStopThread(RDRAM_ARG PTR(OSThread) t_) { - assert(false); -} - -extern "C" void osDestroyThread(RDRAM_ARG PTR(OSThread) t_) { - assert(false); -} - -extern "C" void osSetThreadPri(RDRAM_ARG PTR(OSThread) t, OSPri pri) { - if (t == NULLPTR) { - t = thread_self; - } - bool pause_self = false; - if (pri > TO_PTR(OSThread, thread_self)->priority) { - pause_self = true; - Multilibultra::set_self_paused(PASS_RDRAM1); - } else if (t == thread_self && pri < TO_PTR(OSThread, thread_self)->priority) { - pause_self = true; - Multilibultra::set_self_paused(PASS_RDRAM1); - } - Multilibultra::reprioritize_thread(TO_PTR(OSThread, t), pri); - if (pause_self) { - Multilibultra::wait_for_resumed(PASS_RDRAM1); - } -} - -extern "C" OSPri osGetThreadPri(RDRAM_ARG PTR(OSThread) t) { - if (t == NULLPTR) { - t = thread_self; - } - return TO_PTR(OSThread, t)->priority; -} - -extern "C" OSId osGetThreadId(RDRAM_ARG PTR(OSThread) t) { - if (t == NULLPTR) { - t = thread_self; - } - return TO_PTR(OSThread, t)->id; -} - -// TODO yield thread, need a stable priority queue in the scheduler - -void Multilibultra::set_self_paused(RDRAM_ARG1) { - debug_printf("[Thread] Thread pausing itself: %d\n", TO_PTR(OSThread, thread_self)->id); - TO_PTR(OSThread, thread_self)->state = OSThreadState::PAUSED; - TO_PTR(OSThread, thread_self)->context->running.store(false); - TO_PTR(OSThread, thread_self)->context->running.notify_all(); -} - -void Multilibultra::wait_for_resumed(RDRAM_ARG1) { - TO_PTR(OSThread, thread_self)->context->running.wait(false); -} - -void Multilibultra::pause_thread_impl(OSThread* t) { - t->state = OSThreadState::PREEMPTED; - t->context->running.store(false); - t->context->running.notify_all(); - Multilibultra::pause_thread_native_impl(t); -} - -void Multilibultra::resume_thread_impl(OSThread *t) { - if (t->state == OSThreadState::PREEMPTED) { - Multilibultra::resume_thread_native_impl(t); - } - t->state = OSThreadState::RUNNING; - t->context->running.store(true); - t->context->running.notify_all(); -} - -PTR(OSThread) Multilibultra::this_thread() { - return thread_self; -} diff --git a/test/portultra/timer.cpp b/test/portultra/timer.cpp deleted file mode 100644 index 5379139..0000000 --- a/test/portultra/timer.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include -#include -#include -#include "blockingconcurrentqueue.h" - -#include "ultra64.h" -#include "multilibultra.hpp" -#include "recomp.h" - -// Start time for the program -static std::chrono::system_clock::time_point start = std::chrono::system_clock::now(); -// Game speed multiplier (1 means no speedup) -constexpr uint32_t speed_multiplier = 1; -// N64 CPU counter ticks per millisecond -constexpr uint32_t counter_per_ms = 46'875 * speed_multiplier; - -struct OSTimer { - PTR(OSTimer) unused1; - PTR(OSTimer) unused2; - OSTime interval; - OSTime timestamp; - PTR(OSMesgQueue) mq; - OSMesg msg; -}; - -struct AddTimerAction { - PTR(OSTask) timer; -}; - -struct RemoveTimerAction { - PTR(OSTimer) timer; -}; - -using Action = std::variant; - -struct { - std::thread thread; - moodycamel::BlockingConcurrentQueue action_queue{}; -} timer_context; - -uint64_t duration_to_ticks(std::chrono::system_clock::duration duration) { - uint64_t delta_micros = std::chrono::duration_cast(duration).count(); - // More accurate than using a floating point timer, will only overflow after running for 12.47 years - // Units: (micros * (counts/millis)) / (micros/millis) = counts - uint64_t total_count = (delta_micros * counter_per_ms) / 1000; - - return total_count; -} - -std::chrono::microseconds ticks_to_duration(uint64_t ticks) { - using namespace std::chrono_literals; - return ticks * 1000us / counter_per_ms; -} - -std::chrono::system_clock::time_point ticks_to_timepoint(uint64_t ticks) { - return start + ticks_to_duration(ticks); -} - -uint64_t time_now() { - return duration_to_ticks(std::chrono::system_clock::now() - start); -} - -void timer_thread(RDRAM_ARG1) { - // Lambda comparator function to keep the set ordered - auto timer_sort = [PASS_RDRAM1](PTR(OSTimer) a_, PTR(OSTimer) b_) { - OSTimer* a = TO_PTR(OSTimer, a_); - OSTimer* b = TO_PTR(OSTimer, b_); - - // Order by timestamp if the timers have different timestamps - if (a->timestamp != b->timestamp) { - return a->timestamp < b->timestamp; - } - - // If they have the exact same timestamp then order by address instead - return a < b; - }; - - // Ordered set of timers that are currently active - std::set active_timers{timer_sort}; - - // Lambda to process a timer action to handle adding and removing timers - auto process_timer_action = [&](const Action& action) { - // Determine the action type and act on it - if (const auto* add_action = std::get_if(&action)) { - active_timers.insert(add_action->timer); - } else if (const auto* remove_action = std::get_if(&action)) { - active_timers.erase(remove_action->timer); - } - }; - - while (true) { - // Empty the action queue - Action cur_action; - while (timer_context.action_queue.try_dequeue(cur_action)) { - process_timer_action(cur_action); - } - - // If there's no timer to act on, wait for one to come in from the action queue - while (active_timers.empty()) { - timer_context.action_queue.wait_dequeue(cur_action); - process_timer_action(cur_action); - } - - // Get the timer that's closest to running out - PTR(OSTimer) cur_timer_ = *active_timers.begin(); - OSTimer* cur_timer = TO_PTR(OSTimer, cur_timer_); - - // Remove the timer from the queue (it may get readded if waiting is interrupted) - active_timers.erase(cur_timer_); - - // Determine how long to wait to reach the timer's timestamp - auto wait_duration = ticks_to_timepoint(cur_timer->timestamp) - std::chrono::system_clock::now(); - auto wait_us = std::chrono::duration_cast(wait_duration); - - // Wait for either the duration to complete or a new action to come through - if (timer_context.action_queue.wait_dequeue_timed(cur_action, wait_duration)) { - // Timer was interrupted by a new action - // Add the current timer back to the queue (done first in case the action is to remove this timer) - active_timers.insert(cur_timer_); - // Process the new action - process_timer_action(cur_action); - } else { - // Waiting for the timer completed, so send the timer's message to its message queue - osSendMesg(PASS_RDRAM cur_timer->mq, cur_timer->msg, OS_MESG_NOBLOCK); - // If the timer has a specified interval then reload it with that value - if (cur_timer->interval != 0) { - cur_timer->timestamp = cur_timer->interval + time_now(); - active_timers.insert(cur_timer_); - } - } - } -} - -void Multilibultra::init_timers(RDRAM_ARG1) { - timer_context.thread = std::thread{ timer_thread, PASS_RDRAM1 }; -} - -uint32_t Multilibultra::get_speed_multiplier() { - return speed_multiplier; -} - -std::chrono::system_clock::time_point Multilibultra::get_start() { - return start; -} - -std::chrono::system_clock::duration Multilibultra::time_since_start() { - return std::chrono::system_clock::now() - start; -} - -extern "C" u32 osGetCount() { - uint64_t total_count = time_now(); - - // Allow for overflows, which is how osGetCount behaves - return (uint32_t)total_count; -} - -extern "C" OSTime osGetTime() { - uint64_t total_count = time_now(); - - return total_count; -} - -extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) { - OSTimer* t = TO_PTR(OSTimer, t_); - - // Determine the time when this timer will trigger off - if (countdown == 0) { - // Set the timestamp based on the interval - t->timestamp = interval + time_now(); - } else { - t->timestamp = countdown + time_now(); - } - t->interval = interval; - t->mq = mq; - t->msg = msg; - - timer_context.action_queue.enqueue(AddTimerAction{ t_ }); - - return 0; -} - -extern "C" int osStopTimer(RDRAM_ARG PTR(OSTimer) t_) { - timer_context.action_queue.enqueue(RemoveTimerAction{ t_ }); - - // TODO don't blindly return 0 here; requires some response from the timer thread to know what the returned value was - return 0; -} diff --git a/test/portultra/ultra64.h b/test/portultra/ultra64.h deleted file mode 100644 index 84e3939..0000000 --- a/test/portultra/ultra64.h +++ /dev/null @@ -1,182 +0,0 @@ -#ifndef __ULTRA64_MULTILIBULTRA_H__ -#define __ULTRA64_MULTILIBULTRA_H__ - -#include -#include "platform_specific.h" - -#ifdef __cplusplus -#include -#endif - -#ifdef __GNUC__ -#define UNUSED __attribute__((unused)) -#define ALIGNED(x) __attribute__((aligned(x))) -#else -#define UNUSED -#define ALIGNED(x) -#endif - -typedef int64_t s64; -typedef uint64_t u64; -typedef int32_t s32; -typedef uint32_t u32; -typedef int16_t s16; -typedef uint16_t u16; -typedef int8_t s8; -typedef uint8_t u8; - -#define PTR(x) int32_t -#define RDRAM_ARG uint8_t *rdram, -#define RDRAM_ARG1 uint8_t *rdram -#define PASS_RDRAM rdram, -#define PASS_RDRAM1 rdram -#define TO_PTR(type, var) ((type*)(&rdram[(uint64_t)var - 0xFFFFFFFF80000000])) -#ifdef __cplusplus -#define NULLPTR (PTR(void))0 -#endif - -#ifndef NULL -#define NULL (PTR(void) 0) -#endif - -#define OS_MESG_NOBLOCK 0 -#define OS_MESG_BLOCK 1 - -typedef s32 OSPri; -typedef s32 OSId; - -typedef u64 OSTime; - -#define OS_EVENT_SW1 0 /* CPU SW1 interrupt */ -#define OS_EVENT_SW2 1 /* CPU SW2 interrupt */ -#define OS_EVENT_CART 2 /* Cartridge interrupt: used by rmon */ -#define OS_EVENT_COUNTER 3 /* Counter int: used by VI/Timer Mgr */ -#define OS_EVENT_SP 4 /* SP task done interrupt */ -#define OS_EVENT_SI 5 /* SI (controller) interrupt */ -#define OS_EVENT_AI 6 /* AI interrupt */ -#define OS_EVENT_VI 7 /* VI interrupt: used by VI/Timer Mgr */ -#define OS_EVENT_PI 8 /* PI interrupt: used by PI Manager */ -#define OS_EVENT_DP 9 /* DP full sync interrupt */ -#define OS_EVENT_CPU_BREAK 10 /* CPU breakpoint: used by rmon */ -#define OS_EVENT_SP_BREAK 11 /* SP breakpoint: used by rmon */ -#define OS_EVENT_FAULT 12 /* CPU fault event: used by rmon */ -#define OS_EVENT_THREADSTATUS 13 /* CPU thread status: used by rmon */ -#define OS_EVENT_PRENMI 14 /* Pre NMI interrupt */ - -#define M_GFXTASK 1 -#define M_AUDTASK 2 -#define M_VIDTASK 3 -#define M_NJPEGTASK 4 - -///////////// -// Structs // -///////////// - -typedef struct UltraThreadContext UltraThreadContext; - -typedef enum { - RUNNING, - PAUSED, - PREEMPTED -} OSThreadState; - -typedef struct OSThread_t { - PTR(struct OSThread_t) next; // Next thread in the given queue - OSPri priority; - uint32_t pad1; - uint32_t pad2; - uint16_t flags; // These two are swapped to reflect rdram byteswapping - uint16_t state; - OSId id; - int32_t pad3; - UltraThreadContext* context; // An actual pointer regardless of platform - int32_t sp; -} OSThread; - -typedef u32 OSEvent; -typedef PTR(void) OSMesg; - -typedef struct OSMesgQueue { - PTR(OSThread) blocked_on_recv; /* Linked list of threads blocked on receiving from this queue */ - PTR(OSThread) blocked_on_send; /* Linked list of threads blocked on sending to this queue */ - s32 validCount; /* Number of messages in the queue */ - s32 first; /* Index of the first message in the ring buffer */ - s32 msgCount; /* Size of message buffer */ - PTR(OSMesg) msg; /* Pointer to circular buffer to store messages */ -} OSMesgQueue; - -typedef struct { - u32 type; - u32 flags; - - PTR(u64) ucode_boot; - u32 ucode_boot_size; - - PTR(u64) ucode; - u32 ucode_size; - - PTR(u64) ucode_data; - u32 ucode_data_size; - - PTR(u64) dram_stack; - u32 dram_stack_size; - - PTR(u64) output_buff; - PTR(u64) output_buff_size; - - PTR(u64) data_ptr; - u32 data_size; - - PTR(u64) yield_data_ptr; - u32 yield_data_size; -} OSTask_t; - -typedef union { - OSTask_t t; - int64_t force_structure_alignment; -} OSTask; - -/////////////// -// Functions // -/////////////// - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -void osInitialize(void); - -typedef void (thread_func_t)(PTR(void)); - -void osCreateThread(RDRAM_ARG PTR(OSThread) t, OSId id, PTR(thread_func_t) entry, PTR(void) arg, PTR(void) sp, OSPri p); -void osStartThread(RDRAM_ARG PTR(OSThread) t); -void osStopThread(RDRAM_ARG PTR(OSThread) t); -void osDestroyThread(RDRAM_ARG PTR(OSThread) t); -void osSetThreadPri(RDRAM_ARG PTR(OSThread) t, OSPri pri); -OSPri osGetThreadPri(RDRAM_ARG PTR(OSThread) thread); -OSId osGetThreadId(RDRAM_ARG PTR(OSThread) t); - -s32 MQ_GET_COUNT(RDRAM_ARG PTR(OSMesgQueue)); -s32 MQ_IS_EMPTY(RDRAM_ARG PTR(OSMesgQueue)); -s32 MQ_IS_FULL(RDRAM_ARG PTR(OSMesgQueue)); - -void osCreateMesgQueue(RDRAM_ARG PTR(OSMesgQueue), PTR(OSMesg), s32); -s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue), OSMesg, s32); -s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue), OSMesg, s32); -s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue), PTR(OSMesg), s32); -void osSetEventMesg(RDRAM_ARG OSEvent, PTR(OSMesgQueue), OSMesg); -void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue), OSMesg, u32); -void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr); -PTR(void) osViGetNextFramebuffer(); -PTR(void) osViGetCurrentFramebuffer(); -u32 osGetCount(); -OSTime osGetTime(); -int osSetTimer(RDRAM_ARG PTR(OSTimer) timer, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg); -int osStopTimer(RDRAM_ARG PTR(OSTimer) timer); -u32 osVirtualToPhysical(PTR(void) addr); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif diff --git a/test/portultra/ultrainit.cpp b/test/portultra/ultrainit.cpp deleted file mode 100644 index a95c06c..0000000 --- a/test/portultra/ultrainit.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "ultra64.h" -#include "multilibultra.hpp" - -void Multilibultra::preinit(uint8_t* rdram, uint8_t* rom) { - Multilibultra::set_main_thread(); - Multilibultra::init_events(rdram, rom); - Multilibultra::init_timers(rdram); - Multilibultra::init_audio(); - Multilibultra::save_init(); -} - -extern "C" void osInitialize() { - Multilibultra::init_scheduler(); - Multilibultra::native_init(); -} diff --git a/test/rsp/.gitignore b/test/rsp/.gitignore deleted file mode 100644 index 8341830..0000000 --- a/test/rsp/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -njpgdspMain.cpp -aspMain.cpp diff --git a/test/src/ai.cpp b/test/src/ai.cpp deleted file mode 100644 index 835bb01..0000000 --- a/test/src/ai.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "recomp.h" -#include -#include -#include "../portultra/ultra64.h" -#include "../portultra/multilibultra.hpp" - -#define VI_NTSC_CLOCK 48681812 - -extern "C" void osAiSetFrequency_recomp(uint8_t* rdram, recomp_context* ctx) { - uint32_t freq = ctx->r4; - // This makes actual audio frequency more accurate to console, but may not be desirable - //uint32_t dacRate = (uint32_t)(((float)VI_NTSC_CLOCK / freq) + 0.5f); - //freq = VI_NTSC_CLOCK / dacRate; - ctx->r2 = freq; - Multilibultra::set_audio_frequency(freq); -} - -extern "C" void osAiSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) { - Multilibultra::queue_audio_buffer(rdram, ctx->r4, ctx->r5); - ctx->r2 = 0; -} - -extern "C" void osAiGetLength_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = Multilibultra::get_remaining_audio_bytes(); -} - -extern "C" void osAiGetStatus_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = 0x00000000; // Pretend the audio DMAs finish instantly -} diff --git a/test/src/cont.cpp b/test/src/cont.cpp deleted file mode 100644 index 52283a2..0000000 --- a/test/src/cont.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "../portultra/multilibultra.hpp" -#include "recomp.h" - -static int max_controllers = 0; - -extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) { - gpr bitpattern = ctx->r5; - gpr status = ctx->r6; - - // Set bit 0 to indicate that controller 0 is present - MEM_B(0, bitpattern) = 0x01; - - // Mark controller 0 as present - MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus) - MEM_B(2, status) = 0x00; // status: 0 (from joybus) - MEM_B(3, status) = 0x00; // errno: 0 (from libultra) - - max_controllers = 4; - - // Mark controllers 1-3 as not connected - for (size_t controller = 1; controller < max_controllers; controller++) { - // Libultra doesn't write status or type for absent controllers - MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4 - } - - ctx->r2 = 0; -} - -extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) { - Multilibultra::send_si_message(); -} - -struct OSContPad { - u16 button; - s8 stick_x; /* -80 <= stick_x <= 80 */ - s8 stick_y; /* -80 <= stick_y <= 80 */ - u8 errno_; -}; - -int button = 0; -int stick_x = 0; -int stick_y = 0; - -void press_button(int button) { - -} - -void release_button(int button) { - -} - -extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) { - int32_t pad = (int32_t)ctx->r4; - - if (max_controllers > 0) { - // button - MEM_H(0, pad) = button; - // stick_x - MEM_B(2, pad) = stick_x; - // stick_y - MEM_B(3, pad) = stick_y; - // errno - MEM_B(4, pad) = 0; - } - for (int controller = 1; controller < max_controllers; controller++) { - MEM_B(6 * controller + 4, pad) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4 - } -} - -extern "C" void osContStartQuery_recomp(uint8_t * rdram, recomp_context * ctx) { - Multilibultra::send_si_message(); -} - -extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) { - gpr status = ctx->r4; - - // Mark controller 0 as present - MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus) - MEM_B(2, status) = 0x00; // status: 0 (from joybus) - MEM_B(3, status) = 0x00; // errno: 0 (from libultra) - - // Mark controllers 1-3 as not connected - for (size_t controller = 1; controller < max_controllers; controller++) { - // Libultra doesn't write status or type for absent controllers - MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4 - } -} - -extern "C" void osContSetCh_recomp(uint8_t* rdram, recomp_context* ctx) { - max_controllers = std::min((unsigned int)ctx->r4, 4u); - ctx->r2 = 0; -} - -extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) { - -} - -extern "C" void osMotorInit_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} diff --git a/test/src/dp.cpp b/test/src/dp.cpp deleted file mode 100644 index 3cf8e43..0000000 --- a/test/src/dp.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "recomp.h" - -extern "C" void osDpSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} diff --git a/test/src/eep.cpp b/test/src/eep.cpp deleted file mode 100644 index b6b04f9..0000000 --- a/test/src/eep.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "recomp.h" - -extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} diff --git a/test/src/euc-jp.cpp b/test/src/euc-jp.cpp deleted file mode 100644 index dbeb2fe..0000000 --- a/test/src/euc-jp.cpp +++ /dev/null @@ -1,2587 +0,0 @@ -// Adapted from https://github.com/odashi/encoding -// Original license as follows: - -// MIT License -// -// Copyright (c) 2017 Yusuke Oda -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING -#include -#include -#include -#include -#include "euc-jp.h" - -namespace Encoding { - // JIS X 0201 to Unicode - const int jisx0201_2_unicode[] = { - // ASCII Compatible - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, - 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, - // Shift_JIS First Byte - 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, - 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, - 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, - 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, - // Halfwidth Katakana - 0xfffd, 0xff61, 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, - 0xff68, 0xff69, 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, - 0xff70, 0xff71, 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, - 0xff78, 0xff79, 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, - 0xff80, 0xff81, 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, - 0xff88, 0xff89, 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, - 0xff90, 0xff91, 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, - 0xff98, 0xff99, 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, - // Shift_JIS First Byte - 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, - 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, - 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, - 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd - }; - - // JIS X 0213 to Unicode - const int jisx0213_2_unicode[] = { - /* This table is converted by "JIS X 0213:2004 8-bit code vs Unicode mapping table" from x0213.org. - * License: - * Copyright (C) 2001 earthian@tama.or.jp, All Rights Reserved. - * Copyright (C) 2001 I'O, All Rights Reserved. - * Copyright (C) 2009 Project X0213, All Rights Reserved. - * You can use, modify, distribute this table freely. - */ - // plane 1 row 1 - 0x003000, 0x003001, 0x003002, 0x00002c, 0x00002e, 0x0030fb, 0x00003a, 0x00003b, - 0x00003f, 0x000021, 0x00309b, 0x00309c, 0x0000b4, 0x000060, 0x0000a8, 0x00005e, - 0x00203e, 0x00005f, 0x0030fd, 0x0030fe, 0x00309d, 0x00309e, 0x003003, 0x004edd, - 0x003005, 0x003006, 0x003007, 0x0030fc, 0x002014, 0x002010, 0x00002f, 0x00005c, - 0x00301c, 0x002016, 0x00007c, 0x002026, 0x002025, 0x002018, 0x002019, 0x00201c, - 0x00201d, 0x000028, 0x000029, 0x003014, 0x003015, 0x00005b, 0x00005d, 0x00007b, - 0x00007d, 0x003008, 0x003009, 0x00300a, 0x00300b, 0x00300c, 0x00300d, 0x00300e, - 0x00300f, 0x003010, 0x003011, 0x00002b, 0x002212, 0x0000b1, 0x0000d7, 0x0000f7, - 0x00003d, 0x002260, 0x00003c, 0x00003e, 0x002266, 0x002267, 0x00221e, 0x002234, - 0x002642, 0x002640, 0x0000b0, 0x002032, 0x002033, 0x002103, 0x0000a5, 0x000024, - 0x0000a2, 0x0000a3, 0x000025, 0x000023, 0x000026, 0x00002a, 0x000040, 0x0000a7, - 0x002606, 0x002605, 0x0025cb, 0x0025cf, 0x0025ce, 0x0025c7, - // plane 1 row 2 - 0x0025c6, 0x0025a1, 0x0025a0, 0x0025b3, 0x0025b2, 0x0025bd, 0x0025bc, 0x00203b, - 0x003012, 0x002192, 0x002190, 0x002191, 0x002193, 0x003013, 0x000027, 0x000022, - 0x00002d, 0x00007e, 0x003033, 0x003034, 0x003035, 0x00303b, 0x00303c, 0x0030ff, - 0x00309f, 0x002208, 0x00220b, 0x002286, 0x002287, 0x002282, 0x002283, 0x00222a, - 0x002229, 0x002284, 0x002285, 0x00228a, 0x00228b, 0x002209, 0x002205, 0x002305, - 0x002306, 0x002227, 0x002228, 0x0000ac, 0x0021d2, 0x0021d4, 0x002200, 0x002203, - 0x002295, 0x002296, 0x002297, 0x002225, 0x002226, 0x00ff5f, 0x00ff60, 0x003018, - 0x003019, 0x003016, 0x003017, 0x002220, 0x0022a5, 0x002312, 0x002202, 0x002207, - 0x002261, 0x002252, 0x00226a, 0x00226b, 0x00221a, 0x00223d, 0x00221d, 0x002235, - 0x00222b, 0x00222c, 0x002262, 0x002243, 0x002245, 0x002248, 0x002276, 0x002277, - 0x002194, 0x00212b, 0x002030, 0x00266f, 0x00266d, 0x00266a, 0x002020, 0x002021, - 0x0000b6, 0x00266e, 0x00266b, 0x00266c, 0x002669, 0x0025ef, - // plane 1 row 3 - 0x0025b7, 0x0025b6, 0x0025c1, 0x0025c0, 0x002197, 0x002198, 0x002196, 0x002199, - 0x0021c4, 0x0021e8, 0x0021e6, 0x0021e7, 0x0021e9, 0x002934, 0x002935, 0x000030, - 0x000031, 0x000032, 0x000033, 0x000034, 0x000035, 0x000036, 0x000037, 0x000038, - 0x000039, 0x0029bf, 0x0025c9, 0x00303d, 0x00fe46, 0x00fe45, 0x0025e6, 0x002022, - 0x000041, 0x000042, 0x000043, 0x000044, 0x000045, 0x000046, 0x000047, 0x000048, - 0x000049, 0x00004a, 0x00004b, 0x00004c, 0x00004d, 0x00004e, 0x00004f, 0x000050, - 0x000051, 0x000052, 0x000053, 0x000054, 0x000055, 0x000056, 0x000057, 0x000058, - 0x000059, 0x00005a, 0x002213, 0x002135, 0x00210f, 0x0033cb, 0x002113, 0x002127, - 0x000061, 0x000062, 0x000063, 0x000064, 0x000065, 0x000066, 0x000067, 0x000068, - 0x000069, 0x00006a, 0x00006b, 0x00006c, 0x00006d, 0x00006e, 0x00006f, 0x000070, - 0x000071, 0x000072, 0x000073, 0x000074, 0x000075, 0x000076, 0x000077, 0x000078, - 0x000079, 0x00007a, 0x0030a0, 0x002013, 0x0029fa, 0x0029fb, - // plane 1 row 4 - 0x003041, 0x003042, 0x003043, 0x003044, 0x003045, 0x003046, 0x003047, 0x003048, - 0x003049, 0x00304a, 0x00304b, 0x00304c, 0x00304d, 0x00304e, 0x00304f, 0x003050, - 0x003051, 0x003052, 0x003053, 0x003054, 0x003055, 0x003056, 0x003057, 0x003058, - 0x003059, 0x00305a, 0x00305b, 0x00305c, 0x00305d, 0x00305e, 0x00305f, 0x003060, - 0x003061, 0x003062, 0x003063, 0x003064, 0x003065, 0x003066, 0x003067, 0x003068, - 0x003069, 0x00306a, 0x00306b, 0x00306c, 0x00306d, 0x00306e, 0x00306f, 0x003070, - 0x003071, 0x003072, 0x003073, 0x003074, 0x003075, 0x003076, 0x003077, 0x003078, - 0x003079, 0x00307a, 0x00307b, 0x00307c, 0x00307d, 0x00307e, 0x00307f, 0x003080, - 0x003081, 0x003082, 0x003083, 0x003084, 0x003085, 0x003086, 0x003087, 0x003088, - 0x003089, 0x00308a, 0x00308b, 0x00308c, 0x00308d, 0x00308e, 0x00308f, 0x003090, - 0x003091, 0x003092, 0x003093, 0x003094, 0x003095, 0x003096, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 1 row 5 - 0x0030a1, 0x0030a2, 0x0030a3, 0x0030a4, 0x0030a5, 0x0030a6, 0x0030a7, 0x0030a8, - 0x0030a9, 0x0030aa, 0x0030ab, 0x0030ac, 0x0030ad, 0x0030ae, 0x0030af, 0x0030b0, - 0x0030b1, 0x0030b2, 0x0030b3, 0x0030b4, 0x0030b5, 0x0030b6, 0x0030b7, 0x0030b8, - 0x0030b9, 0x0030ba, 0x0030bb, 0x0030bc, 0x0030bd, 0x0030be, 0x0030bf, 0x0030c0, - 0x0030c1, 0x0030c2, 0x0030c3, 0x0030c4, 0x0030c5, 0x0030c6, 0x0030c7, 0x0030c8, - 0x0030c9, 0x0030ca, 0x0030cb, 0x0030cc, 0x0030cd, 0x0030ce, 0x0030cf, 0x0030d0, - 0x0030d1, 0x0030d2, 0x0030d3, 0x0030d4, 0x0030d5, 0x0030d6, 0x0030d7, 0x0030d8, - 0x0030d9, 0x0030da, 0x0030db, 0x0030dc, 0x0030dd, 0x0030de, 0x0030df, 0x0030e0, - 0x0030e1, 0x0030e2, 0x0030e3, 0x0030e4, 0x0030e5, 0x0030e6, 0x0030e7, 0x0030e8, - 0x0030e9, 0x0030ea, 0x0030eb, 0x0030ec, 0x0030ed, 0x0030ee, 0x0030ef, 0x0030f0, - 0x0030f1, 0x0030f2, 0x0030f3, 0x0030f4, 0x0030f5, 0x0030f6, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 1 row 6 - 0x000391, 0x000392, 0x000393, 0x000394, 0x000395, 0x000396, 0x000397, 0x000398, - 0x000399, 0x00039a, 0x00039b, 0x00039c, 0x00039d, 0x00039e, 0x00039f, 0x0003a0, - 0x0003a1, 0x0003a3, 0x0003a4, 0x0003a5, 0x0003a6, 0x0003a7, 0x0003a8, 0x0003a9, - 0x002664, 0x002660, 0x002662, 0x002666, 0x002661, 0x002665, 0x002667, 0x002663, - 0x0003b1, 0x0003b2, 0x0003b3, 0x0003b4, 0x0003b5, 0x0003b6, 0x0003b7, 0x0003b8, - 0x0003b9, 0x0003ba, 0x0003bb, 0x0003bc, 0x0003bd, 0x0003be, 0x0003bf, 0x0003c0, - 0x0003c1, 0x0003c3, 0x0003c4, 0x0003c5, 0x0003c6, 0x0003c7, 0x0003c8, 0x0003c9, - 0x0003c2, 0x0024f5, 0x0024f6, 0x0024f7, 0x0024f8, 0x0024f9, 0x0024fa, 0x0024fb, - 0x0024fc, 0x0024fd, 0x0024fe, 0x002616, 0x002617, 0x003020, 0x00260e, 0x002600, - 0x002601, 0x002602, 0x002603, 0x002668, 0x0025b1, 0x0031f0, 0x0031f1, 0x0031f2, - 0x0031f3, 0x0031f4, 0x0031f5, 0x0031f6, 0x0031f7, 0x0031f8, 0x0031f9, 0x00fffd, - 0x0031fa, 0x0031fb, 0x0031fc, 0x0031fd, 0x0031fe, 0x0031ff, - // plane 1 row 7 - 0x000410, 0x000411, 0x000412, 0x000413, 0x000414, 0x000415, 0x000401, 0x000416, - 0x000417, 0x000418, 0x000419, 0x00041a, 0x00041b, 0x00041c, 0x00041d, 0x00041e, - 0x00041f, 0x000420, 0x000421, 0x000422, 0x000423, 0x000424, 0x000425, 0x000426, - 0x000427, 0x000428, 0x000429, 0x00042a, 0x00042b, 0x00042c, 0x00042d, 0x00042e, - 0x00042f, 0x0023be, 0x0023bf, 0x0023c0, 0x0023c1, 0x0023c2, 0x0023c3, 0x0023c4, - 0x0023c5, 0x0023c6, 0x0023c7, 0x0023c8, 0x0023c9, 0x0023ca, 0x0023cb, 0x0023cc, - 0x000430, 0x000431, 0x000432, 0x000433, 0x000434, 0x000435, 0x000451, 0x000436, - 0x000437, 0x000438, 0x000439, 0x00043a, 0x00043b, 0x00043c, 0x00043d, 0x00043e, - 0x00043f, 0x000440, 0x000441, 0x000442, 0x000443, 0x000444, 0x000445, 0x000446, - 0x000447, 0x000448, 0x000449, 0x00044a, 0x00044b, 0x00044c, 0x00044d, 0x00044e, - 0x00044f, 0x0030f7, 0x0030f8, 0x0030f9, 0x0030fa, 0x0022da, 0x0022db, 0x002153, - 0x002154, 0x002155, 0x002713, 0x002318, 0x002423, 0x0023ce, - // plane 1 row 8 - 0x002500, 0x002502, 0x00250c, 0x002510, 0x002518, 0x002514, 0x00251c, 0x00252c, - 0x002524, 0x002534, 0x00253c, 0x002501, 0x002503, 0x00250f, 0x002513, 0x00251b, - 0x002517, 0x002523, 0x002533, 0x00252b, 0x00253b, 0x00254b, 0x002520, 0x00252f, - 0x002528, 0x002537, 0x00253f, 0x00251d, 0x002530, 0x002525, 0x002538, 0x002542, - 0x003251, 0x003252, 0x003253, 0x003254, 0x003255, 0x003256, 0x003257, 0x003258, - 0x003259, 0x00325a, 0x00325b, 0x00325c, 0x00325d, 0x00325e, 0x00325f, 0x0032b1, - 0x0032b2, 0x0032b3, 0x0032b4, 0x0032b5, 0x0032b6, 0x0032b7, 0x0032b8, 0x0032b9, - 0x0032ba, 0x0032bb, 0x0032bc, 0x0032bd, 0x0032be, 0x0032bf, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x0025d0, 0x0025d1, - 0x0025d2, 0x0025d3, 0x00203c, 0x002047, 0x002048, 0x002049, 0x0001cd, 0x0001ce, - 0x0001d0, 0x001e3e, 0x001e3f, 0x0001f8, 0x0001f9, 0x0001d1, 0x0001d2, 0x0001d4, - 0x0001d6, 0x0001d8, 0x0001da, 0x0001dc, 0x00fffd, 0x00fffd, - // plane 1 row 9 - 0x0020ac, 0x0000a0, 0x0000a1, 0x0000a4, 0x0000a6, 0x0000a9, 0x0000aa, 0x0000ab, - 0x0000ad, 0x0000ae, 0x0000af, 0x0000b2, 0x0000b3, 0x0000b7, 0x0000b8, 0x0000b9, - 0x0000ba, 0x0000bb, 0x0000bc, 0x0000bd, 0x0000be, 0x0000bf, 0x0000c0, 0x0000c1, - 0x0000c2, 0x0000c3, 0x0000c4, 0x0000c5, 0x0000c6, 0x0000c7, 0x0000c8, 0x0000c9, - 0x0000ca, 0x0000cb, 0x0000cc, 0x0000cd, 0x0000ce, 0x0000cf, 0x0000d0, 0x0000d1, - 0x0000d2, 0x0000d3, 0x0000d4, 0x0000d5, 0x0000d6, 0x0000d8, 0x0000d9, 0x0000da, - 0x0000db, 0x0000dc, 0x0000dd, 0x0000de, 0x0000df, 0x0000e0, 0x0000e1, 0x0000e2, - 0x0000e3, 0x0000e4, 0x0000e5, 0x0000e6, 0x0000e7, 0x0000e8, 0x0000e9, 0x0000ea, - 0x0000eb, 0x0000ec, 0x0000ed, 0x0000ee, 0x0000ef, 0x0000f0, 0x0000f1, 0x0000f2, - 0x0000f3, 0x0000f4, 0x0000f5, 0x0000f6, 0x0000f8, 0x0000f9, 0x0000fa, 0x0000fb, - 0x0000fc, 0x0000fd, 0x0000fe, 0x0000ff, 0x000100, 0x00012a, 0x00016a, 0x000112, - 0x00014c, 0x000101, 0x00012b, 0x00016b, 0x000113, 0x00014d, - // plane 1 row 10 - 0x000104, 0x0002d8, 0x000141, 0x00013d, 0x00015a, 0x000160, 0x00015e, 0x000164, - 0x000179, 0x00017d, 0x00017b, 0x000105, 0x0002db, 0x000142, 0x00013e, 0x00015b, - 0x0002c7, 0x000161, 0x00015f, 0x000165, 0x00017a, 0x0002dd, 0x00017e, 0x00017c, - 0x000154, 0x000102, 0x000139, 0x000106, 0x00010c, 0x000118, 0x00011a, 0x00010e, - 0x000143, 0x000147, 0x000150, 0x000158, 0x00016e, 0x000170, 0x000162, 0x000155, - 0x000103, 0x00013a, 0x000107, 0x00010d, 0x000119, 0x00011b, 0x00010f, 0x000111, - 0x000144, 0x000148, 0x000151, 0x000159, 0x00016f, 0x000171, 0x000163, 0x0002d9, - 0x000108, 0x00011c, 0x000124, 0x000134, 0x00015c, 0x00016c, 0x000109, 0x00011d, - 0x000125, 0x000135, 0x00015d, 0x00016d, 0x000271, 0x00028b, 0x00027e, 0x000283, - 0x000292, 0x00026c, 0x00026e, 0x000279, 0x000288, 0x000256, 0x000273, 0x00027d, - 0x000282, 0x000290, 0x00027b, 0x00026d, 0x00025f, 0x000272, 0x00029d, 0x00028e, - 0x000261, 0x00014b, 0x000270, 0x000281, 0x000127, 0x000295, - // plane 1 row 11 - 0x000294, 0x000266, 0x000298, 0x0001c2, 0x000253, 0x000257, 0x000284, 0x000260, - 0x000193, 0x000153, 0x000152, 0x000268, 0x000289, 0x000258, 0x000275, 0x000259, - 0x00025c, 0x00025e, 0x000250, 0x00026f, 0x00028a, 0x000264, 0x00028c, 0x000254, - 0x000251, 0x000252, 0x00028d, 0x000265, 0x0002a2, 0x0002a1, 0x000255, 0x000291, - 0x00027a, 0x000267, 0x00025a, 0x00fffd, 0x0001fd, 0x001f70, 0x001f71, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x001f72, - 0x001f73, 0x000361, 0x0002c8, 0x0002cc, 0x0002d0, 0x0002d1, 0x000306, 0x00203f, - 0x00030b, 0x000301, 0x000304, 0x000300, 0x00030f, 0x00030c, 0x000302, 0x0002e5, - 0x0002e6, 0x0002e7, 0x0002e8, 0x0002e9, 0x00fffd, 0x00fffd, 0x000325, 0x00032c, - 0x000339, 0x00031c, 0x00031f, 0x000320, 0x000308, 0x00033d, 0x000329, 0x00032f, - 0x0002de, 0x000324, 0x000330, 0x00033c, 0x000334, 0x00031d, 0x00031e, 0x000318, - 0x000319, 0x00032a, 0x00033a, 0x00033b, 0x000303, 0x00031a, - // plane 1 row 12 - 0x002776, 0x002777, 0x002778, 0x002779, 0x00277a, 0x00277b, 0x00277c, 0x00277d, - 0x00277e, 0x00277f, 0x0024eb, 0x0024ec, 0x0024ed, 0x0024ee, 0x0024ef, 0x0024f0, - 0x0024f1, 0x0024f2, 0x0024f3, 0x0024f4, 0x002170, 0x002171, 0x002172, 0x002173, - 0x002174, 0x002175, 0x002176, 0x002177, 0x002178, 0x002179, 0x00217a, 0x00217b, - 0x0024d0, 0x0024d1, 0x0024d2, 0x0024d3, 0x0024d4, 0x0024d5, 0x0024d6, 0x0024d7, - 0x0024d8, 0x0024d9, 0x0024da, 0x0024db, 0x0024dc, 0x0024dd, 0x0024de, 0x0024df, - 0x0024e0, 0x0024e1, 0x0024e2, 0x0024e3, 0x0024e4, 0x0024e5, 0x0024e6, 0x0024e7, - 0x0024e8, 0x0024e9, 0x0032d0, 0x0032d1, 0x0032d2, 0x0032d3, 0x0032d4, 0x0032d5, - 0x0032d6, 0x0032d7, 0x0032d8, 0x0032d9, 0x0032da, 0x0032db, 0x0032dc, 0x0032dd, - 0x0032de, 0x0032df, 0x0032e0, 0x0032e1, 0x0032e2, 0x0032e3, 0x0032fa, 0x0032e9, - 0x0032e5, 0x0032ed, 0x0032ec, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x002051, 0x002042, - // plane 1 row 13 - 0x002460, 0x002461, 0x002462, 0x002463, 0x002464, 0x002465, 0x002466, 0x002467, - 0x002468, 0x002469, 0x00246a, 0x00246b, 0x00246c, 0x00246d, 0x00246e, 0x00246f, - 0x002470, 0x002471, 0x002472, 0x002473, 0x002160, 0x002161, 0x002162, 0x002163, - 0x002164, 0x002165, 0x002166, 0x002167, 0x002168, 0x002169, 0x00216a, 0x003349, - 0x003314, 0x003322, 0x00334d, 0x003318, 0x003327, 0x003303, 0x003336, 0x003351, - 0x003357, 0x00330d, 0x003326, 0x003323, 0x00332b, 0x00334a, 0x00333b, 0x00339c, - 0x00339d, 0x00339e, 0x00338e, 0x00338f, 0x0033c4, 0x0033a1, 0x00216b, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00337b, 0x00301d, - 0x00301f, 0x002116, 0x0033cd, 0x002121, 0x0032a4, 0x0032a5, 0x0032a6, 0x0032a7, - 0x0032a8, 0x003231, 0x003232, 0x003239, 0x00337e, 0x00337d, 0x00337c, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00222e, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00221f, - 0x0022bf, 0x00fffd, 0x00fffd, 0x00fffd, 0x002756, 0x00261e, - // plane 1 row 14 - 0x004ff1, 0x02000b, 0x003402, 0x004e28, 0x004e2f, 0x004e30, 0x004e8d, 0x004ee1, - 0x004efd, 0x004eff, 0x004f03, 0x004f0b, 0x004f60, 0x004f48, 0x004f49, 0x004f56, - 0x004f5f, 0x004f6a, 0x004f6c, 0x004f7e, 0x004f8a, 0x004f94, 0x004f97, 0x00fa30, - 0x004fc9, 0x004fe0, 0x005001, 0x005002, 0x00500e, 0x005018, 0x005027, 0x00502e, - 0x005040, 0x00503b, 0x005041, 0x005094, 0x0050cc, 0x0050f2, 0x0050d0, 0x0050e6, - 0x00fa31, 0x005106, 0x005103, 0x00510b, 0x00511e, 0x005135, 0x00514a, 0x00fa32, - 0x005155, 0x005157, 0x0034b5, 0x00519d, 0x0051c3, 0x0051ca, 0x0051de, 0x0051e2, - 0x0051ee, 0x005201, 0x0034db, 0x005213, 0x005215, 0x005249, 0x005257, 0x005261, - 0x005293, 0x0052c8, 0x00fa33, 0x0052cc, 0x0052d0, 0x0052d6, 0x0052db, 0x00fa34, - 0x0052f0, 0x0052fb, 0x005300, 0x005307, 0x00531c, 0x00fa35, 0x005361, 0x005363, - 0x00537d, 0x005393, 0x00539d, 0x0053b2, 0x005412, 0x005427, 0x00544d, 0x00549c, - 0x00546b, 0x005474, 0x00547f, 0x005488, 0x005496, 0x0054a1, - // plane 1 row 15 - 0x0054a9, 0x0054c6, 0x0054ff, 0x00550e, 0x00552b, 0x005535, 0x005550, 0x00555e, - 0x005581, 0x005586, 0x00558e, 0x00fa36, 0x0055ad, 0x0055ce, 0x00fa37, 0x005608, - 0x00560e, 0x00563b, 0x005649, 0x005676, 0x005666, 0x00fa38, 0x00566f, 0x005671, - 0x005672, 0x005699, 0x00569e, 0x0056a9, 0x0056ac, 0x0056b3, 0x0056c9, 0x0056ca, - 0x00570a, 0x02123d, 0x005721, 0x00572f, 0x005733, 0x005734, 0x005770, 0x005777, - 0x00577c, 0x00579c, 0x00fa0f, 0x02131b, 0x0057b8, 0x0057c7, 0x0057c8, 0x0057cf, - 0x0057e4, 0x0057ed, 0x0057f5, 0x0057f6, 0x0057ff, 0x005809, 0x00fa10, 0x005861, - 0x005864, 0x00fa39, 0x00587c, 0x005889, 0x00589e, 0x00fa3a, 0x0058a9, 0x02146e, - 0x0058d2, 0x0058ce, 0x0058d4, 0x0058da, 0x0058e0, 0x0058e9, 0x00590c, 0x008641, - 0x00595d, 0x00596d, 0x00598b, 0x005992, 0x0059a4, 0x0059c3, 0x0059d2, 0x0059dd, - 0x005a13, 0x005a23, 0x005a67, 0x005a6d, 0x005a77, 0x005a7e, 0x005a84, 0x005a9e, - 0x005aa7, 0x005ac4, 0x0218bd, 0x005b19, 0x005b25, 0x00525d, - // plane 1 row 16 - 0x004e9c, 0x005516, 0x005a03, 0x00963f, 0x0054c0, 0x00611b, 0x006328, 0x0059f6, - 0x009022, 0x008475, 0x00831c, 0x007a50, 0x0060aa, 0x0063e1, 0x006e25, 0x0065ed, - 0x008466, 0x0082a6, 0x009bf5, 0x006893, 0x005727, 0x0065a1, 0x006271, 0x005b9b, - 0x0059d0, 0x00867b, 0x0098f4, 0x007d62, 0x007dbe, 0x009b8e, 0x006216, 0x007c9f, - 0x0088b7, 0x005b89, 0x005eb5, 0x006309, 0x006697, 0x006848, 0x0095c7, 0x00978d, - 0x00674f, 0x004ee5, 0x004f0a, 0x004f4d, 0x004f9d, 0x005049, 0x0056f2, 0x005937, - 0x0059d4, 0x005a01, 0x005c09, 0x0060df, 0x00610f, 0x006170, 0x006613, 0x006905, - 0x0070ba, 0x00754f, 0x007570, 0x0079fb, 0x007dad, 0x007def, 0x0080c3, 0x00840e, - 0x008863, 0x008b02, 0x009055, 0x00907a, 0x00533b, 0x004e95, 0x004ea5, 0x0057df, - 0x0080b2, 0x0090c1, 0x0078ef, 0x004e00, 0x0058f1, 0x006ea2, 0x009038, 0x007a32, - 0x008328, 0x00828b, 0x009c2f, 0x005141, 0x005370, 0x0054bd, 0x0054e1, 0x0056e0, - 0x0059fb, 0x005f15, 0x0098f2, 0x006deb, 0x0080e4, 0x00852d, - // plane 1 row 17 - 0x009662, 0x009670, 0x0096a0, 0x0097fb, 0x00540b, 0x0053f3, 0x005b87, 0x0070cf, - 0x007fbd, 0x008fc2, 0x0096e8, 0x00536f, 0x009d5c, 0x007aba, 0x004e11, 0x007893, - 0x0081fc, 0x006e26, 0x005618, 0x005504, 0x006b1d, 0x00851a, 0x009c3b, 0x0059e5, - 0x0053a9, 0x006d66, 0x0074dc, 0x00958f, 0x005642, 0x004e91, 0x00904b, 0x0096f2, - 0x00834f, 0x00990c, 0x0053e1, 0x0055b6, 0x005b30, 0x005f71, 0x006620, 0x0066f3, - 0x006804, 0x006c38, 0x006cf3, 0x006d29, 0x00745b, 0x0076c8, 0x007a4e, 0x009834, - 0x0082f1, 0x00885b, 0x008a60, 0x0092ed, 0x006db2, 0x0075ab, 0x0076ca, 0x0099c5, - 0x0060a6, 0x008b01, 0x008d8a, 0x0095b2, 0x00698e, 0x0053ad, 0x005186, 0x005712, - 0x005830, 0x005944, 0x005bb4, 0x005ef6, 0x006028, 0x0063a9, 0x0063f4, 0x006cbf, - 0x006f14, 0x00708e, 0x007114, 0x007159, 0x0071d5, 0x00733f, 0x007e01, 0x008276, - 0x0082d1, 0x008597, 0x009060, 0x00925b, 0x009d1b, 0x005869, 0x0065bc, 0x006c5a, - 0x007525, 0x0051f9, 0x00592e, 0x005965, 0x005f80, 0x005fdc, - // plane 1 row 18 - 0x0062bc, 0x0065fa, 0x006a2a, 0x006b27, 0x006bb4, 0x00738b, 0x007fc1, 0x008956, - 0x009d2c, 0x009d0e, 0x009ec4, 0x005ca1, 0x006c96, 0x00837b, 0x005104, 0x005c4b, - 0x0061b6, 0x0081c6, 0x006876, 0x007261, 0x004e59, 0x004ffa, 0x005378, 0x006069, - 0x006e29, 0x007a4f, 0x0097f3, 0x004e0b, 0x005316, 0x004eee, 0x004f55, 0x004f3d, - 0x004fa1, 0x004f73, 0x0052a0, 0x0053ef, 0x005609, 0x00590f, 0x005ac1, 0x005bb6, - 0x005be1, 0x0079d1, 0x006687, 0x00679c, 0x0067b6, 0x006b4c, 0x006cb3, 0x00706b, - 0x0073c2, 0x00798d, 0x0079be, 0x007a3c, 0x007b87, 0x0082b1, 0x0082db, 0x008304, - 0x008377, 0x0083ef, 0x0083d3, 0x008766, 0x008ab2, 0x005629, 0x008ca8, 0x008fe6, - 0x00904e, 0x00971e, 0x00868a, 0x004fc4, 0x005ce8, 0x006211, 0x007259, 0x00753b, - 0x0081e5, 0x0082bd, 0x0086fe, 0x008cc0, 0x0096c5, 0x009913, 0x0099d5, 0x004ecb, - 0x004f1a, 0x0089e3, 0x0056de, 0x00584a, 0x0058ca, 0x005efb, 0x005feb, 0x00602a, - 0x006094, 0x006062, 0x0061d0, 0x006212, 0x0062d0, 0x006539, - // plane 1 row 19 - 0x009b41, 0x006666, 0x0068b0, 0x006d77, 0x007070, 0x00754c, 0x007686, 0x007d75, - 0x0082a5, 0x0087f9, 0x00958b, 0x00968e, 0x008c9d, 0x0051f1, 0x0052be, 0x005916, - 0x0054b3, 0x005bb3, 0x005d16, 0x006168, 0x006982, 0x006daf, 0x00788d, 0x0084cb, - 0x008857, 0x008a72, 0x0093a7, 0x009ab8, 0x006d6c, 0x0099a8, 0x0086d9, 0x0057a3, - 0x0067ff, 0x0086ce, 0x00920e, 0x005283, 0x005687, 0x005404, 0x005ed3, 0x0062e1, - 0x0064b9, 0x00683c, 0x006838, 0x006bbb, 0x007372, 0x0078ba, 0x007a6b, 0x00899a, - 0x0089d2, 0x008d6b, 0x008f03, 0x0090ed, 0x0095a3, 0x009694, 0x009769, 0x005b66, - 0x005cb3, 0x00697d, 0x00984d, 0x00984e, 0x00639b, 0x007b20, 0x006a2b, 0x006a7f, - 0x0068b6, 0x009c0d, 0x006f5f, 0x005272, 0x00559d, 0x006070, 0x0062ec, 0x006d3b, - 0x006e07, 0x006ed1, 0x00845b, 0x008910, 0x008f44, 0x004e14, 0x009c39, 0x0053f6, - 0x00691b, 0x006a3a, 0x009784, 0x00682a, 0x00515c, 0x007ac3, 0x0084b2, 0x0091dc, - 0x00938c, 0x00565b, 0x009d28, 0x006822, 0x008305, 0x008431, - // plane 1 row 20 - 0x007ca5, 0x005208, 0x0082c5, 0x0074e6, 0x004e7e, 0x004f83, 0x0051a0, 0x005bd2, - 0x00520a, 0x0052d8, 0x0052e7, 0x005dfb, 0x00559a, 0x00582a, 0x0059e6, 0x005b8c, - 0x005b98, 0x005bdb, 0x005e72, 0x005e79, 0x0060a3, 0x00611f, 0x006163, 0x0061be, - 0x0063db, 0x006562, 0x0067d1, 0x006853, 0x0068fa, 0x006b3e, 0x006b53, 0x006c57, - 0x006f22, 0x006f97, 0x006f45, 0x0074b0, 0x007518, 0x0076e3, 0x00770b, 0x007aff, - 0x007ba1, 0x007c21, 0x007de9, 0x007f36, 0x007ff0, 0x00809d, 0x008266, 0x00839e, - 0x0089b3, 0x008acc, 0x008cab, 0x009084, 0x009451, 0x009593, 0x009591, 0x0095a2, - 0x009665, 0x0097d3, 0x009928, 0x008218, 0x004e38, 0x00542b, 0x005cb8, 0x005dcc, - 0x0073a9, 0x00764c, 0x00773c, 0x005ca9, 0x007feb, 0x008d0b, 0x0096c1, 0x009811, - 0x009854, 0x009858, 0x004f01, 0x004f0e, 0x005371, 0x00559c, 0x005668, 0x0057fa, - 0x005947, 0x005b09, 0x005bc4, 0x005c90, 0x005e0c, 0x005e7e, 0x005fcc, 0x0063ee, - 0x00673a, 0x0065d7, 0x0065e2, 0x00671f, 0x0068cb, 0x0068c4, - // plane 1 row 21 - 0x006a5f, 0x005e30, 0x006bc5, 0x006c17, 0x006c7d, 0x00757f, 0x007948, 0x005b63, - 0x007a00, 0x007d00, 0x005fbd, 0x00898f, 0x008a18, 0x008cb4, 0x008d77, 0x008ecc, - 0x008f1d, 0x0098e2, 0x009a0e, 0x009b3c, 0x004e80, 0x00507d, 0x005100, 0x005993, - 0x005b9c, 0x00622f, 0x006280, 0x0064ec, 0x006b3a, 0x0072a0, 0x007591, 0x007947, - 0x007fa9, 0x0087fb, 0x008abc, 0x008b70, 0x0063ac, 0x0083ca, 0x0097a0, 0x005409, - 0x005403, 0x0055ab, 0x006854, 0x006a58, 0x008a70, 0x007827, 0x006775, 0x009ecd, - 0x005374, 0x005ba2, 0x00811a, 0x008650, 0x009006, 0x004e18, 0x004e45, 0x004ec7, - 0x004f11, 0x0053ca, 0x005438, 0x005bae, 0x005f13, 0x006025, 0x006551, 0x00673d, - 0x006c42, 0x006c72, 0x006ce3, 0x007078, 0x007403, 0x007a76, 0x007aae, 0x007b08, - 0x007d1a, 0x007cfe, 0x007d66, 0x0065e7, 0x00725b, 0x0053bb, 0x005c45, 0x005de8, - 0x0062d2, 0x0062e0, 0x006319, 0x006e20, 0x00865a, 0x008a31, 0x008ddd, 0x0092f8, - 0x006f01, 0x0079a6, 0x009b5a, 0x004ea8, 0x004eab, 0x004eac, - // plane 1 row 22 - 0x004f9b, 0x004fa0, 0x0050d1, 0x005147, 0x007af6, 0x005171, 0x0051f6, 0x005354, - 0x005321, 0x00537f, 0x0053eb, 0x0055ac, 0x005883, 0x005ce1, 0x005f37, 0x005f4a, - 0x00602f, 0x006050, 0x00606d, 0x00631f, 0x006559, 0x006a4b, 0x006cc1, 0x0072c2, - 0x0072ed, 0x0077ef, 0x0080f8, 0x008105, 0x008208, 0x00854e, 0x0090f7, 0x0093e1, - 0x0097ff, 0x009957, 0x009a5a, 0x004ef0, 0x0051dd, 0x005c2d, 0x006681, 0x00696d, - 0x005c40, 0x0066f2, 0x006975, 0x007389, 0x006850, 0x007c81, 0x0050c5, 0x0052e4, - 0x005747, 0x005dfe, 0x009326, 0x0065a4, 0x006b23, 0x006b3d, 0x007434, 0x007981, - 0x0079bd, 0x007b4b, 0x007dca, 0x0082b9, 0x0083cc, 0x00887f, 0x00895f, 0x008b39, - 0x008fd1, 0x0091d1, 0x00541f, 0x009280, 0x004e5d, 0x005036, 0x0053e5, 0x00533a, - 0x0072d7, 0x007396, 0x0077e9, 0x0082e6, 0x008eaf, 0x0099c6, 0x0099c8, 0x0099d2, - 0x005177, 0x00611a, 0x00865e, 0x0055b0, 0x007a7a, 0x005076, 0x005bd3, 0x009047, - 0x009685, 0x004e32, 0x006adb, 0x0091e7, 0x005c51, 0x005c48, - // plane 1 row 23 - 0x006398, 0x007a9f, 0x006c93, 0x009774, 0x008f61, 0x007aaa, 0x00718a, 0x009688, - 0x007c82, 0x006817, 0x007e70, 0x006851, 0x00936c, 0x0052f2, 0x00541b, 0x0085ab, - 0x008a13, 0x007fa4, 0x008ecd, 0x0090e1, 0x005366, 0x008888, 0x007941, 0x004fc2, - 0x0050be, 0x005211, 0x005144, 0x005553, 0x00572d, 0x0073ea, 0x00578b, 0x005951, - 0x005f62, 0x005f84, 0x006075, 0x006176, 0x006167, 0x0061a9, 0x0063b2, 0x00643a, - 0x00656c, 0x00666f, 0x006842, 0x006e13, 0x007566, 0x007a3d, 0x007cfb, 0x007d4c, - 0x007d99, 0x007e4b, 0x007f6b, 0x00830e, 0x00834a, 0x0086cd, 0x008a08, 0x008a63, - 0x008b66, 0x008efd, 0x00981a, 0x009d8f, 0x0082b8, 0x008fce, 0x009be8, 0x005287, - 0x00621f, 0x006483, 0x006fc0, 0x009699, 0x006841, 0x005091, 0x006b20, 0x006c7a, - 0x006f54, 0x007a74, 0x007d50, 0x008840, 0x008a23, 0x006708, 0x004ef6, 0x005039, - 0x005026, 0x005065, 0x00517c, 0x005238, 0x005263, 0x0055a7, 0x00570f, 0x005805, - 0x005acc, 0x005efa, 0x0061b2, 0x0061f8, 0x0062f3, 0x006372, - // plane 1 row 24 - 0x00691c, 0x006a29, 0x00727d, 0x0072ac, 0x00732e, 0x007814, 0x00786f, 0x007d79, - 0x00770c, 0x0080a9, 0x00898b, 0x008b19, 0x008ce2, 0x008ed2, 0x009063, 0x009375, - 0x00967a, 0x009855, 0x009a13, 0x009e78, 0x005143, 0x00539f, 0x0053b3, 0x005e7b, - 0x005f26, 0x006e1b, 0x006e90, 0x007384, 0x0073fe, 0x007d43, 0x008237, 0x008a00, - 0x008afa, 0x009650, 0x004e4e, 0x00500b, 0x0053e4, 0x00547c, 0x0056fa, 0x0059d1, - 0x005b64, 0x005df1, 0x005eab, 0x005f27, 0x006238, 0x006545, 0x0067af, 0x006e56, - 0x0072d0, 0x007cca, 0x0088b4, 0x0080a1, 0x0080e1, 0x0083f0, 0x00864e, 0x008a87, - 0x008de8, 0x009237, 0x0096c7, 0x009867, 0x009f13, 0x004e94, 0x004e92, 0x004f0d, - 0x005348, 0x005449, 0x00543e, 0x005a2f, 0x005f8c, 0x005fa1, 0x00609f, 0x0068a7, - 0x006a8e, 0x00745a, 0x007881, 0x008a9e, 0x008aa4, 0x008b77, 0x009190, 0x004e5e, - 0x009bc9, 0x004ea4, 0x004f7c, 0x004faf, 0x005019, 0x005016, 0x005149, 0x00516c, - 0x00529f, 0x0052b9, 0x0052fe, 0x00539a, 0x0053e3, 0x005411, - // plane 1 row 25 - 0x00540e, 0x005589, 0x005751, 0x0057a2, 0x00597d, 0x005b54, 0x005b5d, 0x005b8f, - 0x005de5, 0x005de7, 0x005df7, 0x005e78, 0x005e83, 0x005e9a, 0x005eb7, 0x005f18, - 0x006052, 0x00614c, 0x006297, 0x0062d8, 0x0063a7, 0x00653b, 0x006602, 0x006643, - 0x0066f4, 0x00676d, 0x006821, 0x006897, 0x0069cb, 0x006c5f, 0x006d2a, 0x006d69, - 0x006e2f, 0x006e9d, 0x007532, 0x007687, 0x00786c, 0x007a3f, 0x007ce0, 0x007d05, - 0x007d18, 0x007d5e, 0x007db1, 0x008015, 0x008003, 0x0080af, 0x0080b1, 0x008154, - 0x00818f, 0x00822a, 0x008352, 0x00884c, 0x008861, 0x008b1b, 0x008ca2, 0x008cfc, - 0x0090ca, 0x009175, 0x009271, 0x00783f, 0x0092fc, 0x0095a4, 0x00964d, 0x009805, - 0x009999, 0x009ad8, 0x009d3b, 0x00525b, 0x0052ab, 0x0053f7, 0x005408, 0x0058d5, - 0x0062f7, 0x006fe0, 0x008c6a, 0x008f5f, 0x009eb9, 0x00514b, 0x00523b, 0x00544a, - 0x0056fd, 0x007a40, 0x009177, 0x009d60, 0x009ed2, 0x007344, 0x006f09, 0x008170, - 0x007511, 0x005ffd, 0x0060da, 0x009aa8, 0x0072db, 0x008fbc, - // plane 1 row 26 - 0x006b64, 0x009803, 0x004eca, 0x0056f0, 0x005764, 0x0058be, 0x005a5a, 0x006068, - 0x0061c7, 0x00660f, 0x006606, 0x006839, 0x0068b1, 0x006df7, 0x0075d5, 0x007d3a, - 0x00826e, 0x009b42, 0x004e9b, 0x004f50, 0x0053c9, 0x005506, 0x005d6f, 0x005de6, - 0x005dee, 0x0067fb, 0x006c99, 0x007473, 0x007802, 0x008a50, 0x009396, 0x0088df, - 0x005750, 0x005ea7, 0x00632b, 0x0050b5, 0x0050ac, 0x00518d, 0x006700, 0x0054c9, - 0x00585e, 0x0059bb, 0x005bb0, 0x005f69, 0x00624d, 0x0063a1, 0x00683d, 0x006b73, - 0x006e08, 0x00707d, 0x0091c7, 0x007280, 0x007815, 0x007826, 0x00796d, 0x00658e, - 0x007d30, 0x0083dc, 0x0088c1, 0x008f09, 0x00969b, 0x005264, 0x005728, 0x006750, - 0x007f6a, 0x008ca1, 0x0051b4, 0x005742, 0x00962a, 0x00583a, 0x00698a, 0x0080b4, - 0x0054b2, 0x005d0e, 0x0057fc, 0x007895, 0x009dfa, 0x004f5c, 0x00524a, 0x00548b, - 0x00643e, 0x006628, 0x006714, 0x0067f5, 0x007a84, 0x007b56, 0x007d22, 0x00932f, - 0x00685c, 0x009bad, 0x007b39, 0x005319, 0x00518a, 0x005237, - // plane 1 row 27 - 0x005bdf, 0x0062f6, 0x0064ae, 0x0064e6, 0x00672d, 0x006bba, 0x0085a9, 0x0096d1, - 0x007690, 0x009bd6, 0x00634c, 0x009306, 0x009bab, 0x0076bf, 0x006652, 0x004e09, - 0x005098, 0x0053c2, 0x005c71, 0x0060e8, 0x006492, 0x006563, 0x00685f, 0x0071e6, - 0x0073ca, 0x007523, 0x007b97, 0x007e82, 0x008695, 0x008b83, 0x008cdb, 0x009178, - 0x009910, 0x0065ac, 0x0066ab, 0x006b8b, 0x004ed5, 0x004ed4, 0x004f3a, 0x004f7f, - 0x00523a, 0x0053f8, 0x0053f2, 0x0055e3, 0x0056db, 0x0058eb, 0x0059cb, 0x0059c9, - 0x0059ff, 0x005b50, 0x005c4d, 0x005e02, 0x005e2b, 0x005fd7, 0x00601d, 0x006307, - 0x00652f, 0x005b5c, 0x0065af, 0x0065bd, 0x0065e8, 0x00679d, 0x006b62, 0x006b7b, - 0x006c0f, 0x007345, 0x007949, 0x0079c1, 0x007cf8, 0x007d19, 0x007d2b, 0x0080a2, - 0x008102, 0x0081f3, 0x008996, 0x008a5e, 0x008a69, 0x008a66, 0x008a8c, 0x008aee, - 0x008cc7, 0x008cdc, 0x0096cc, 0x0098fc, 0x006b6f, 0x004e8b, 0x004f3c, 0x004f8d, - 0x005150, 0x005b57, 0x005bfa, 0x006148, 0x006301, 0x006642, - // plane 1 row 28 - 0x006b21, 0x006ecb, 0x006cbb, 0x00723e, 0x0074bd, 0x0075d4, 0x0078c1, 0x00793a, - 0x00800c, 0x008033, 0x0081ea, 0x008494, 0x008f9e, 0x006c50, 0x009e7f, 0x005f0f, - 0x008b58, 0x009d2b, 0x007afa, 0x008ef8, 0x005b8d, 0x0096eb, 0x004e03, 0x0053f1, - 0x0057f7, 0x005931, 0x005ac9, 0x005ba4, 0x006089, 0x006e7f, 0x006f06, 0x0075be, - 0x008cea, 0x005b9f, 0x008500, 0x007be0, 0x005072, 0x0067f4, 0x00829d, 0x005c61, - 0x00854a, 0x007e1e, 0x00820e, 0x005199, 0x005c04, 0x006368, 0x008d66, 0x00659c, - 0x00716e, 0x00793e, 0x007d17, 0x008005, 0x008b1d, 0x008eca, 0x00906e, 0x0086c7, - 0x0090aa, 0x00501f, 0x0052fa, 0x005c3a, 0x006753, 0x00707c, 0x007235, 0x00914c, - 0x0091c8, 0x00932b, 0x0082e5, 0x005bc2, 0x005f31, 0x0060f9, 0x004e3b, 0x0053d6, - 0x005b88, 0x00624b, 0x006731, 0x006b8a, 0x0072e9, 0x0073e0, 0x007a2e, 0x00816b, - 0x008da3, 0x009152, 0x009996, 0x005112, 0x0053d7, 0x00546a, 0x005bff, 0x006388, - 0x006a39, 0x007dac, 0x009700, 0x0056da, 0x0053ce, 0x005468, - // plane 1 row 29 - 0x005b97, 0x005c31, 0x005dde, 0x004fee, 0x006101, 0x0062fe, 0x006d32, 0x0079c0, - 0x0079cb, 0x007d42, 0x007e4d, 0x007fd2, 0x0081ed, 0x00821f, 0x008490, 0x008846, - 0x008972, 0x008b90, 0x008e74, 0x008f2f, 0x009031, 0x00914b, 0x00916c, 0x0096c6, - 0x00919c, 0x004ec0, 0x004f4f, 0x005145, 0x005341, 0x005f93, 0x00620e, 0x0067d4, - 0x006c41, 0x006e0b, 0x007363, 0x007e26, 0x0091cd, 0x009283, 0x0053d4, 0x005919, - 0x005bbf, 0x006dd1, 0x00795d, 0x007e2e, 0x007c9b, 0x00587e, 0x00719f, 0x0051fa, - 0x008853, 0x008ff0, 0x004fca, 0x005cfb, 0x006625, 0x0077ac, 0x007ae3, 0x00821c, - 0x0099ff, 0x0051c6, 0x005faa, 0x0065ec, 0x00696f, 0x006b89, 0x006df3, 0x006e96, - 0x006f64, 0x0076fe, 0x007d14, 0x005de1, 0x009075, 0x009187, 0x009806, 0x0051e6, - 0x00521d, 0x006240, 0x006691, 0x0066d9, 0x006e1a, 0x005eb6, 0x007dd2, 0x007f72, - 0x0066f8, 0x0085af, 0x0085f7, 0x008af8, 0x0052a9, 0x0053d9, 0x005973, 0x005e8f, - 0x005f90, 0x006055, 0x0092e4, 0x009664, 0x0050b7, 0x00511f, - // plane 1 row 30 - 0x0052dd, 0x005320, 0x005347, 0x0053ec, 0x0054e8, 0x005546, 0x005531, 0x005617, - 0x005968, 0x0059be, 0x005a3c, 0x005bb5, 0x005c06, 0x005c0f, 0x005c11, 0x005c1a, - 0x005e84, 0x005e8a, 0x005ee0, 0x005f70, 0x00627f, 0x006284, 0x0062db, 0x00638c, - 0x006377, 0x006607, 0x00660c, 0x00662d, 0x006676, 0x00677e, 0x0068a2, 0x006a1f, - 0x006a35, 0x006cbc, 0x006d88, 0x006e09, 0x006e58, 0x00713c, 0x007126, 0x007167, - 0x0075c7, 0x007701, 0x00785d, 0x007901, 0x007965, 0x0079f0, 0x007ae0, 0x007b11, - 0x007ca7, 0x007d39, 0x008096, 0x0083d6, 0x00848b, 0x008549, 0x00885d, 0x0088f3, - 0x008a1f, 0x008a3c, 0x008a54, 0x008a73, 0x008c61, 0x008cde, 0x0091a4, 0x009266, - 0x00937e, 0x009418, 0x00969c, 0x009798, 0x004e0a, 0x004e08, 0x004e1e, 0x004e57, - 0x005197, 0x005270, 0x0057ce, 0x005834, 0x0058cc, 0x005b22, 0x005e38, 0x0060c5, - 0x0064fe, 0x006761, 0x006756, 0x006d44, 0x0072b6, 0x007573, 0x007a63, 0x0084b8, - 0x008b72, 0x0091b8, 0x009320, 0x005631, 0x0057f4, 0x0098fe, - // plane 1 row 31 - 0x0062ed, 0x00690d, 0x006b96, 0x0071ed, 0x007e54, 0x008077, 0x008272, 0x0089e6, - 0x0098df, 0x008755, 0x008fb1, 0x005c3b, 0x004f38, 0x004fe1, 0x004fb5, 0x005507, - 0x005a20, 0x005bdd, 0x005be9, 0x005fc3, 0x00614e, 0x00632f, 0x0065b0, 0x00664b, - 0x0068ee, 0x00699b, 0x006d78, 0x006df1, 0x007533, 0x0075b9, 0x00771f, 0x00795e, - 0x0079e6, 0x007d33, 0x0081e3, 0x0082af, 0x0085aa, 0x0089aa, 0x008a3a, 0x008eab, - 0x008f9b, 0x009032, 0x0091dd, 0x009707, 0x004eba, 0x004ec1, 0x005203, 0x005875, - 0x0058ec, 0x005c0b, 0x00751a, 0x005c3d, 0x00814e, 0x008a0a, 0x008fc5, 0x009663, - 0x00976d, 0x007b25, 0x008acf, 0x009808, 0x009162, 0x0056f3, 0x0053a8, 0x009017, - 0x005439, 0x005782, 0x005e25, 0x0063a8, 0x006c34, 0x00708a, 0x007761, 0x007c8b, - 0x007fe0, 0x008870, 0x009042, 0x009154, 0x009310, 0x009318, 0x00968f, 0x00745e, - 0x009ac4, 0x005d07, 0x005d69, 0x006570, 0x0067a2, 0x008da8, 0x0096db, 0x00636e, - 0x006749, 0x006919, 0x0083c5, 0x009817, 0x0096c0, 0x0088fe, - // plane 1 row 32 - 0x006f84, 0x00647a, 0x005bf8, 0x004e16, 0x00702c, 0x00755d, 0x00662f, 0x0051c4, - 0x005236, 0x0052e2, 0x0059d3, 0x005f81, 0x006027, 0x006210, 0x00653f, 0x006574, - 0x00661f, 0x006674, 0x0068f2, 0x006816, 0x006b63, 0x006e05, 0x007272, 0x00751f, - 0x0076db, 0x007cbe, 0x008056, 0x0058f0, 0x0088fd, 0x00897f, 0x008aa0, 0x008a93, - 0x008acb, 0x00901d, 0x009192, 0x009752, 0x009759, 0x006589, 0x007a0e, 0x008106, - 0x0096bb, 0x005e2d, 0x0060dc, 0x00621a, 0x0065a5, 0x006614, 0x006790, 0x0077f3, - 0x007a4d, 0x007c4d, 0x007e3e, 0x00810a, 0x008cac, 0x008d64, 0x008de1, 0x008e5f, - 0x0078a9, 0x005207, 0x0062d9, 0x0063a5, 0x006442, 0x006298, 0x008a2d, 0x007a83, - 0x007bc0, 0x008aac, 0x0096ea, 0x007d76, 0x00820c, 0x008749, 0x004ed9, 0x005148, - 0x005343, 0x005360, 0x005ba3, 0x005c02, 0x005c16, 0x005ddd, 0x006226, 0x006247, - 0x0064b0, 0x006813, 0x006834, 0x006cc9, 0x006d45, 0x006d17, 0x0067d3, 0x006f5c, - 0x00714e, 0x00717d, 0x0065cb, 0x007a7f, 0x007bad, 0x007dda, - // plane 1 row 33 - 0x007e4a, 0x007fa8, 0x00817a, 0x00821b, 0x008239, 0x0085a6, 0x008a6e, 0x008cce, - 0x008df5, 0x009078, 0x009077, 0x0092ad, 0x009291, 0x009583, 0x009bae, 0x00524d, - 0x005584, 0x006f38, 0x007136, 0x005168, 0x007985, 0x007e55, 0x0081b3, 0x007cce, - 0x00564c, 0x005851, 0x005ca8, 0x0063aa, 0x0066fe, 0x0066fd, 0x00695a, 0x0072d9, - 0x00758f, 0x00758e, 0x00790e, 0x007956, 0x0079df, 0x007c97, 0x007d20, 0x007d44, - 0x008607, 0x008a34, 0x00963b, 0x009061, 0x009f20, 0x0050e7, 0x005275, 0x0053cc, - 0x0053e2, 0x005009, 0x0055aa, 0x0058ee, 0x00594f, 0x00723d, 0x005b8b, 0x005c64, - 0x00531d, 0x0060e3, 0x0060f3, 0x00635c, 0x006383, 0x00633f, 0x0063bb, 0x0064cd, - 0x0065e9, 0x0066f9, 0x005de3, 0x0069cd, 0x0069fd, 0x006f15, 0x0071e5, 0x004e89, - 0x0075e9, 0x0076f8, 0x007a93, 0x007cdf, 0x007dcf, 0x007d9c, 0x008061, 0x008349, - 0x008358, 0x00846c, 0x0084bc, 0x0085fb, 0x0088c5, 0x008d70, 0x009001, 0x00906d, - 0x009397, 0x00971c, 0x009a12, 0x0050cf, 0x005897, 0x00618e, - // plane 1 row 34 - 0x0081d3, 0x008535, 0x008d08, 0x009020, 0x004fc3, 0x005074, 0x005247, 0x005373, - 0x00606f, 0x006349, 0x00675f, 0x006e2c, 0x008db3, 0x00901f, 0x004fd7, 0x005c5e, - 0x008cca, 0x0065cf, 0x007d9a, 0x005352, 0x008896, 0x005176, 0x0063c3, 0x005b58, - 0x005b6b, 0x005c0a, 0x00640d, 0x006751, 0x00905c, 0x004ed6, 0x00591a, 0x00592a, - 0x006c70, 0x008a51, 0x00553e, 0x005815, 0x0059a5, 0x0060f0, 0x006253, 0x0067c1, - 0x008235, 0x006955, 0x009640, 0x0099c4, 0x009a28, 0x004f53, 0x005806, 0x005bfe, - 0x008010, 0x005cb1, 0x005e2f, 0x005f85, 0x006020, 0x00614b, 0x006234, 0x0066ff, - 0x006cf0, 0x006ede, 0x0080ce, 0x00817f, 0x0082d4, 0x00888b, 0x008cb8, 0x009000, - 0x00902e, 0x00968a, 0x009edb, 0x009bdb, 0x004ee3, 0x0053f0, 0x005927, 0x007b2c, - 0x00918d, 0x00984c, 0x009df9, 0x006edd, 0x007027, 0x005353, 0x005544, 0x005b85, - 0x006258, 0x00629e, 0x0062d3, 0x006ca2, 0x006fef, 0x007422, 0x008a17, 0x009438, - 0x006fc1, 0x008afe, 0x008338, 0x0051e7, 0x0086f8, 0x0053ea, - // plane 1 row 35 - 0x0053e9, 0x004f46, 0x009054, 0x008fb0, 0x00596a, 0x008131, 0x005dfd, 0x007aea, - 0x008fbf, 0x0068da, 0x008c37, 0x0072f8, 0x009c48, 0x006a3d, 0x008ab0, 0x004e39, - 0x005358, 0x005606, 0x005766, 0x0062c5, 0x0063a2, 0x0065e6, 0x006b4e, 0x006de1, - 0x006e5b, 0x0070ad, 0x0077ed, 0x007aef, 0x007baa, 0x007dbb, 0x00803d, 0x0080c6, - 0x0086cb, 0x008a95, 0x00935b, 0x0056e3, 0x0058c7, 0x005f3e, 0x0065ad, 0x006696, - 0x006a80, 0x006bb5, 0x007537, 0x008ac7, 0x005024, 0x0077e5, 0x005730, 0x005f1b, - 0x006065, 0x00667a, 0x006c60, 0x0075f4, 0x007a1a, 0x007f6e, 0x0081f4, 0x008718, - 0x009045, 0x0099b3, 0x007bc9, 0x00755c, 0x007af9, 0x007b51, 0x0084c4, 0x009010, - 0x0079e9, 0x007a92, 0x008336, 0x005ae1, 0x007740, 0x004e2d, 0x004ef2, 0x005b99, - 0x005fe0, 0x0062bd, 0x00663c, 0x0067f1, 0x006ce8, 0x00866b, 0x008877, 0x008a3b, - 0x00914e, 0x0092f3, 0x0099d0, 0x006a17, 0x007026, 0x00732a, 0x0082e7, 0x008457, - 0x008caf, 0x004e01, 0x005146, 0x0051cb, 0x00558b, 0x005bf5, - // plane 1 row 36 - 0x005e16, 0x005e33, 0x005e81, 0x005f14, 0x005f35, 0x005f6b, 0x005fb4, 0x0061f2, - 0x006311, 0x0066a2, 0x00671d, 0x006f6e, 0x007252, 0x00753a, 0x00773a, 0x008074, - 0x008139, 0x008178, 0x008776, 0x008abf, 0x008adc, 0x008d85, 0x008df3, 0x00929a, - 0x009577, 0x009802, 0x009ce5, 0x0052c5, 0x006357, 0x0076f4, 0x006715, 0x006c88, - 0x0073cd, 0x008cc3, 0x0093ae, 0x009673, 0x006d25, 0x00589c, 0x00690e, 0x0069cc, - 0x008ffd, 0x00939a, 0x0075db, 0x00901a, 0x00585a, 0x006802, 0x0063b4, 0x0069fb, - 0x004f43, 0x006f2c, 0x0067d8, 0x008fbb, 0x008526, 0x007db4, 0x009354, 0x00693f, - 0x006f70, 0x00576a, 0x0058f7, 0x005b2c, 0x007d2c, 0x00722a, 0x00540a, 0x0091e3, - 0x009db4, 0x004ead, 0x004f4e, 0x00505c, 0x005075, 0x005243, 0x008c9e, 0x005448, - 0x005824, 0x005b9a, 0x005e1d, 0x005e95, 0x005ead, 0x005ef7, 0x005f1f, 0x00608c, - 0x0062b5, 0x00633a, 0x0063d0, 0x0068af, 0x006c40, 0x007887, 0x00798e, 0x007a0b, - 0x007de0, 0x008247, 0x008a02, 0x008ae6, 0x008e44, 0x009013, - // plane 1 row 37 - 0x0090b8, 0x00912d, 0x0091d8, 0x009f0e, 0x006ce5, 0x006458, 0x0064e2, 0x006575, - 0x006ef4, 0x007684, 0x007b1b, 0x009069, 0x0093d1, 0x006eba, 0x0054f2, 0x005fb9, - 0x0064a4, 0x008f4d, 0x008fed, 0x009244, 0x005178, 0x00586b, 0x005929, 0x005c55, - 0x005e97, 0x006dfb, 0x007e8f, 0x00751c, 0x008cbc, 0x008ee2, 0x00985b, 0x0070b9, - 0x004f1d, 0x006bbf, 0x006fb1, 0x007530, 0x0096fb, 0x00514e, 0x005410, 0x005835, - 0x005857, 0x0059ac, 0x005c60, 0x005f92, 0x006597, 0x00675c, 0x006e21, 0x00767b, - 0x0083df, 0x008ced, 0x009014, 0x0090fd, 0x00934d, 0x007825, 0x00783a, 0x0052aa, - 0x005ea6, 0x00571f, 0x005974, 0x006012, 0x005012, 0x00515a, 0x0051ac, 0x0051cd, - 0x005200, 0x005510, 0x005854, 0x005858, 0x005957, 0x005b95, 0x005cf6, 0x005d8b, - 0x0060bc, 0x006295, 0x00642d, 0x006771, 0x006843, 0x0068bc, 0x0068df, 0x0076d7, - 0x006dd8, 0x006e6f, 0x006d9b, 0x00706f, 0x0071c8, 0x005f53, 0x0075d8, 0x007977, - 0x007b49, 0x007b54, 0x007b52, 0x007cd6, 0x007d71, 0x005230, - // plane 1 row 38 - 0x008463, 0x008569, 0x0085e4, 0x008a0e, 0x008b04, 0x008c46, 0x008e0f, 0x009003, - 0x00900f, 0x009419, 0x009676, 0x00982d, 0x009a30, 0x0095d8, 0x0050cd, 0x0052d5, - 0x00540c, 0x005802, 0x005c0e, 0x0061a7, 0x00649e, 0x006d1e, 0x0077b3, 0x007ae5, - 0x0080f4, 0x008404, 0x009053, 0x009285, 0x005ce0, 0x009d07, 0x00533f, 0x005f97, - 0x005fb3, 0x006d9c, 0x007279, 0x007763, 0x0079bf, 0x007be4, 0x006bd2, 0x0072ec, - 0x008aad, 0x006803, 0x006a61, 0x0051f8, 0x007a81, 0x006934, 0x005c4a, 0x009cf6, - 0x0082eb, 0x005bc5, 0x009149, 0x00701e, 0x005678, 0x005c6f, 0x0060c7, 0x006566, - 0x006c8c, 0x008c5a, 0x009041, 0x009813, 0x005451, 0x0066c7, 0x00920d, 0x005948, - 0x0090a3, 0x005185, 0x004e4d, 0x0051ea, 0x008599, 0x008b0e, 0x007058, 0x00637a, - 0x00934b, 0x006962, 0x0099b4, 0x007e04, 0x007577, 0x005357, 0x006960, 0x008edf, - 0x0096e3, 0x006c5d, 0x004e8c, 0x005c3c, 0x005f10, 0x008fe9, 0x005302, 0x008cd1, - 0x008089, 0x008679, 0x005eff, 0x0065e5, 0x004e73, 0x005165, - // plane 1 row 39 - 0x005982, 0x005c3f, 0x0097ee, 0x004efb, 0x00598a, 0x005fcd, 0x008a8d, 0x006fe1, - 0x0079b0, 0x007962, 0x005be7, 0x008471, 0x00732b, 0x0071b1, 0x005e74, 0x005ff5, - 0x00637b, 0x00649a, 0x0071c3, 0x007c98, 0x004e43, 0x005efc, 0x004e4b, 0x0057dc, - 0x0056a2, 0x0060a9, 0x006fc3, 0x007d0d, 0x0080fd, 0x008133, 0x0081bf, 0x008fb2, - 0x008997, 0x0086a4, 0x005df4, 0x00628a, 0x0064ad, 0x008987, 0x006777, 0x006ce2, - 0x006d3e, 0x007436, 0x007834, 0x005a46, 0x007f75, 0x0082ad, 0x0099ac, 0x004ff3, - 0x005ec3, 0x0062dd, 0x006392, 0x006557, 0x00676f, 0x0076c3, 0x00724c, 0x0080cc, - 0x0080ba, 0x008f29, 0x00914d, 0x00500d, 0x0057f9, 0x005a92, 0x006885, 0x006973, - 0x007164, 0x0072fd, 0x008cb7, 0x0058f2, 0x008ce0, 0x00966a, 0x009019, 0x00877f, - 0x0079e4, 0x0077e7, 0x008429, 0x004f2f, 0x005265, 0x00535a, 0x0062cd, 0x0067cf, - 0x006cca, 0x00767d, 0x007b94, 0x007c95, 0x008236, 0x008584, 0x008feb, 0x0066dd, - 0x006f20, 0x007206, 0x007e1b, 0x0083ab, 0x0099c1, 0x009ea6, - // plane 1 row 40 - 0x0051fd, 0x007bb1, 0x007872, 0x007bb8, 0x008087, 0x007b48, 0x006ae8, 0x005e61, - 0x00808c, 0x007551, 0x007560, 0x00516b, 0x009262, 0x006e8c, 0x00767a, 0x009197, - 0x009aea, 0x004f10, 0x007f70, 0x00629c, 0x007b4f, 0x0095a5, 0x009ce9, 0x00567a, - 0x005859, 0x0086e4, 0x0096bc, 0x004f34, 0x005224, 0x00534a, 0x0053cd, 0x0053db, - 0x005e06, 0x00642c, 0x006591, 0x00677f, 0x006c3e, 0x006c4e, 0x007248, 0x0072af, - 0x0073ed, 0x007554, 0x007e41, 0x00822c, 0x0085e9, 0x008ca9, 0x007bc4, 0x0091c6, - 0x007169, 0x009812, 0x0098ef, 0x00633d, 0x006669, 0x00756a, 0x0076e4, 0x0078d0, - 0x008543, 0x0086ee, 0x00532a, 0x005351, 0x005426, 0x005983, 0x005e87, 0x005f7c, - 0x0060b2, 0x006249, 0x006279, 0x0062ab, 0x006590, 0x006bd4, 0x006ccc, 0x0075b2, - 0x0076ae, 0x007891, 0x0079d8, 0x007dcb, 0x007f77, 0x0080a5, 0x0088ab, 0x008ab9, - 0x008cbb, 0x00907f, 0x00975e, 0x0098db, 0x006a0b, 0x007c38, 0x005099, 0x005c3e, - 0x005fae, 0x006787, 0x006bd8, 0x007435, 0x007709, 0x007f8e, - // plane 1 row 41 - 0x009f3b, 0x0067ca, 0x007a17, 0x005339, 0x00758b, 0x009aed, 0x005f66, 0x00819d, - 0x0083f1, 0x008098, 0x005f3c, 0x005fc5, 0x007562, 0x007b46, 0x00903c, 0x006867, - 0x0059eb, 0x005a9b, 0x007d10, 0x00767e, 0x008b2c, 0x004ff5, 0x005f6a, 0x006a19, - 0x006c37, 0x006f02, 0x0074e2, 0x007968, 0x008868, 0x008a55, 0x008c79, 0x005edf, - 0x0063cf, 0x0075c5, 0x0079d2, 0x0082d7, 0x009328, 0x0092f2, 0x00849c, 0x0086ed, - 0x009c2d, 0x0054c1, 0x005f6c, 0x00658c, 0x006d5c, 0x007015, 0x008ca7, 0x008cd3, - 0x00983b, 0x00654f, 0x0074f6, 0x004e0d, 0x004ed8, 0x0057e0, 0x00592b, 0x005a66, - 0x005bcc, 0x0051a8, 0x005e03, 0x005e9c, 0x006016, 0x006276, 0x006577, 0x0065a7, - 0x00666e, 0x006d6e, 0x007236, 0x007b26, 0x008150, 0x00819a, 0x008299, 0x008b5c, - 0x008ca0, 0x008ce6, 0x008d74, 0x00961c, 0x009644, 0x004fae, 0x0064ab, 0x006b66, - 0x00821e, 0x008461, 0x00856a, 0x0090e8, 0x005c01, 0x006953, 0x0098a8, 0x00847a, - 0x008557, 0x004f0f, 0x00526f, 0x005fa9, 0x005e45, 0x00670d, - // plane 1 row 42 - 0x00798f, 0x008179, 0x008907, 0x008986, 0x006df5, 0x005f17, 0x006255, 0x006cb8, - 0x004ecf, 0x007269, 0x009b92, 0x005206, 0x00543b, 0x005674, 0x0058b3, 0x0061a4, - 0x00626e, 0x00711a, 0x00596e, 0x007c89, 0x007cde, 0x007d1b, 0x0096f0, 0x006587, - 0x00805e, 0x004e19, 0x004f75, 0x005175, 0x005840, 0x005e63, 0x005e73, 0x005f0a, - 0x0067c4, 0x004e26, 0x00853d, 0x009589, 0x00965b, 0x007c73, 0x009801, 0x0050fb, - 0x0058c1, 0x007656, 0x0078a7, 0x005225, 0x0077a5, 0x008511, 0x007b86, 0x00504f, - 0x005909, 0x007247, 0x007bc7, 0x007de8, 0x008fba, 0x008fd4, 0x00904d, 0x004fbf, - 0x0052c9, 0x005a29, 0x005f01, 0x0097ad, 0x004fdd, 0x008217, 0x0092ea, 0x005703, - 0x006355, 0x006b69, 0x00752b, 0x0088dc, 0x008f14, 0x007a42, 0x0052df, 0x005893, - 0x006155, 0x00620a, 0x0066ae, 0x006bcd, 0x007c3f, 0x0083e9, 0x005023, 0x004ff8, - 0x005305, 0x005446, 0x005831, 0x005949, 0x005b9d, 0x005cf0, 0x005cef, 0x005d29, - 0x005e96, 0x0062b1, 0x006367, 0x00653e, 0x0065b9, 0x00670b, - // plane 1 row 43 - 0x006cd5, 0x006ce1, 0x0070f9, 0x007832, 0x007e2b, 0x0080de, 0x0082b3, 0x00840c, - 0x0084ec, 0x008702, 0x008912, 0x008a2a, 0x008c4a, 0x0090a6, 0x0092d2, 0x0098fd, - 0x009cf3, 0x009d6c, 0x004e4f, 0x004ea1, 0x00508d, 0x005256, 0x00574a, 0x0059a8, - 0x005e3d, 0x005fd8, 0x005fd9, 0x00623f, 0x0066b4, 0x00671b, 0x0067d0, 0x0068d2, - 0x005192, 0x007d21, 0x0080aa, 0x0081a8, 0x008b00, 0x008c8c, 0x008cbf, 0x00927e, - 0x009632, 0x005420, 0x00982c, 0x005317, 0x0050d5, 0x00535c, 0x0058a8, 0x0064b2, - 0x006734, 0x007267, 0x007766, 0x007a46, 0x0091e6, 0x0052c3, 0x006ca1, 0x006b86, - 0x005800, 0x005e4c, 0x005954, 0x00672c, 0x007ffb, 0x0051e1, 0x0076c6, 0x006469, - 0x0078e8, 0x009b54, 0x009ebb, 0x0057cb, 0x0059b9, 0x006627, 0x00679a, 0x006bce, - 0x0054e9, 0x0069d9, 0x005e55, 0x00819c, 0x006795, 0x009baa, 0x0067fe, 0x009c52, - 0x00685d, 0x004ea6, 0x004fe3, 0x0053c8, 0x0062b9, 0x00672b, 0x006cab, 0x008fc4, - 0x004fad, 0x007e6d, 0x009ebf, 0x004e07, 0x006162, 0x006e80, - // plane 1 row 44 - 0x006f2b, 0x008513, 0x005473, 0x00672a, 0x009b45, 0x005df3, 0x007b95, 0x005cac, - 0x005bc6, 0x00871c, 0x006e4a, 0x0084d1, 0x007a14, 0x008108, 0x005999, 0x007c8d, - 0x006c11, 0x007720, 0x0052d9, 0x005922, 0x007121, 0x00725f, 0x0077db, 0x009727, - 0x009d61, 0x00690b, 0x005a7f, 0x005a18, 0x0051a5, 0x00540d, 0x00547d, 0x00660e, - 0x0076df, 0x008ff7, 0x009298, 0x009cf4, 0x0059ea, 0x00725d, 0x006ec5, 0x00514d, - 0x0068c9, 0x007dbf, 0x007dec, 0x009762, 0x009eba, 0x006478, 0x006a21, 0x008302, - 0x005984, 0x005b5f, 0x006bdb, 0x00731b, 0x0076f2, 0x007db2, 0x008017, 0x008499, - 0x005132, 0x006728, 0x009ed9, 0x0076ee, 0x006762, 0x0052ff, 0x009905, 0x005c24, - 0x00623b, 0x007c7e, 0x008cb0, 0x00554f, 0x0060b6, 0x007d0b, 0x009580, 0x005301, - 0x004e5f, 0x0051b6, 0x00591c, 0x00723a, 0x008036, 0x0091ce, 0x005f25, 0x0077e2, - 0x005384, 0x005f79, 0x007d04, 0x0085ac, 0x008a33, 0x008e8d, 0x009756, 0x0067f3, - 0x0085ae, 0x009453, 0x006109, 0x006108, 0x006cb9, 0x007652, - // plane 1 row 45 - 0x008aed, 0x008f38, 0x00552f, 0x004f51, 0x00512a, 0x0052c7, 0x0053cb, 0x005ba5, - 0x005e7d, 0x0060a0, 0x006182, 0x0063d6, 0x006709, 0x0067da, 0x006e67, 0x006d8c, - 0x007336, 0x007337, 0x007531, 0x007950, 0x0088d5, 0x008a98, 0x00904a, 0x009091, - 0x0090f5, 0x0096c4, 0x00878d, 0x005915, 0x004e88, 0x004f59, 0x004e0e, 0x008a89, - 0x008f3f, 0x009810, 0x0050ad, 0x005e7c, 0x005996, 0x005bb9, 0x005eb8, 0x0063da, - 0x0063fa, 0x0064c1, 0x0066dc, 0x00694a, 0x0069d8, 0x006d0b, 0x006eb6, 0x007194, - 0x007528, 0x007aaf, 0x007f8a, 0x008000, 0x008449, 0x0084c9, 0x008981, 0x008b21, - 0x008e0a, 0x009065, 0x00967d, 0x00990a, 0x00617e, 0x006291, 0x006b32, 0x006c83, - 0x006d74, 0x007fcc, 0x007ffc, 0x006dc0, 0x007f85, 0x0087ba, 0x0088f8, 0x006765, - 0x0083b1, 0x00983c, 0x0096f7, 0x006d1b, 0x007d61, 0x00843d, 0x00916a, 0x004e71, - 0x005375, 0x005d50, 0x006b04, 0x006feb, 0x0085cd, 0x00862d, 0x0089a7, 0x005229, - 0x00540f, 0x005c65, 0x00674e, 0x0068a8, 0x007406, 0x007483, - // plane 1 row 46 - 0x0075e2, 0x0088cf, 0x0088e1, 0x0091cc, 0x0096e2, 0x009678, 0x005f8b, 0x007387, - 0x007acb, 0x00844e, 0x0063a0, 0x007565, 0x005289, 0x006d41, 0x006e9c, 0x007409, - 0x007559, 0x00786b, 0x007c92, 0x009686, 0x007adc, 0x009f8d, 0x004fb6, 0x00616e, - 0x0065c5, 0x00865c, 0x004e86, 0x004eae, 0x0050da, 0x004e21, 0x0051cc, 0x005bee, - 0x006599, 0x006881, 0x006dbc, 0x00731f, 0x007642, 0x0077ad, 0x007a1c, 0x007ce7, - 0x00826f, 0x008ad2, 0x00907c, 0x0091cf, 0x009675, 0x009818, 0x00529b, 0x007dd1, - 0x00502b, 0x005398, 0x006797, 0x006dcb, 0x0071d0, 0x007433, 0x0081e8, 0x008f2a, - 0x0096a3, 0x009c57, 0x009e9f, 0x007460, 0x005841, 0x006d99, 0x007d2f, 0x00985e, - 0x004ee4, 0x004f36, 0x004f8b, 0x0051b7, 0x0052b1, 0x005dba, 0x00601c, 0x0073b2, - 0x00793c, 0x0082d3, 0x009234, 0x0096b7, 0x0096f6, 0x00970a, 0x009e97, 0x009f62, - 0x0066a6, 0x006b74, 0x005217, 0x0052a3, 0x0070c8, 0x0088c2, 0x005ec9, 0x00604b, - 0x006190, 0x006f23, 0x007149, 0x007c3e, 0x007df4, 0x00806f, - // plane 1 row 47 - 0x0084ee, 0x009023, 0x00932c, 0x005442, 0x009b6f, 0x006ad3, 0x007089, 0x008cc2, - 0x008def, 0x009732, 0x0052b4, 0x005a41, 0x005eca, 0x005f04, 0x006717, 0x00697c, - 0x006994, 0x006d6a, 0x006f0f, 0x007262, 0x0072fc, 0x007bed, 0x008001, 0x00807e, - 0x00874b, 0x0090ce, 0x00516d, 0x009e93, 0x007984, 0x00808b, 0x009332, 0x008ad6, - 0x00502d, 0x00548c, 0x008a71, 0x006b6a, 0x008cc4, 0x008107, 0x0060d1, 0x0067a0, - 0x009df2, 0x004e99, 0x004e98, 0x009c10, 0x008a6b, 0x0085c1, 0x008568, 0x006900, - 0x006e7e, 0x007897, 0x008155, 0x020b9f, 0x005b41, 0x005b56, 0x005b7d, 0x005b93, - 0x005bd8, 0x005bec, 0x005c12, 0x005c1e, 0x005c23, 0x005c2b, 0x00378d, 0x005c62, - 0x00fa3b, 0x00fa3c, 0x0216b4, 0x005c7a, 0x005c8f, 0x005c9f, 0x005ca3, 0x005caa, - 0x005cba, 0x005ccb, 0x005cd0, 0x005cd2, 0x005cf4, 0x021e34, 0x0037e2, 0x005d0d, - 0x005d27, 0x00fa11, 0x005d46, 0x005d47, 0x005d53, 0x005d4a, 0x005d6d, 0x005d81, - 0x005da0, 0x005da4, 0x005da7, 0x005db8, 0x005dcb, 0x00541e, - // plane 1 row 48 - 0x005f0c, 0x004e10, 0x004e15, 0x004e2a, 0x004e31, 0x004e36, 0x004e3c, 0x004e3f, - 0x004e42, 0x004e56, 0x004e58, 0x004e82, 0x004e85, 0x008c6b, 0x004e8a, 0x008212, - 0x005f0d, 0x004e8e, 0x004e9e, 0x004e9f, 0x004ea0, 0x004ea2, 0x004eb0, 0x004eb3, - 0x004eb6, 0x004ece, 0x004ecd, 0x004ec4, 0x004ec6, 0x004ec2, 0x004ed7, 0x004ede, - 0x004eed, 0x004edf, 0x004ef7, 0x004f09, 0x004f5a, 0x004f30, 0x004f5b, 0x004f5d, - 0x004f57, 0x004f47, 0x004f76, 0x004f88, 0x004f8f, 0x004f98, 0x004f7b, 0x004f69, - 0x004f70, 0x004f91, 0x004f6f, 0x004f86, 0x004f96, 0x005118, 0x004fd4, 0x004fdf, - 0x004fce, 0x004fd8, 0x004fdb, 0x004fd1, 0x004fda, 0x004fd0, 0x004fe4, 0x004fe5, - 0x00501a, 0x005028, 0x005014, 0x00502a, 0x005025, 0x005005, 0x004f1c, 0x004ff6, - 0x005021, 0x005029, 0x00502c, 0x004ffe, 0x004fef, 0x005011, 0x005006, 0x005043, - 0x005047, 0x006703, 0x005055, 0x005050, 0x005048, 0x00505a, 0x005056, 0x00506c, - 0x005078, 0x005080, 0x00509a, 0x005085, 0x0050b4, 0x0050b2, - // plane 1 row 49 - 0x0050c9, 0x0050ca, 0x0050b3, 0x0050c2, 0x0050d6, 0x0050de, 0x0050e5, 0x0050ed, - 0x0050e3, 0x0050ee, 0x0050f9, 0x0050f5, 0x005109, 0x005101, 0x005102, 0x005116, - 0x005115, 0x005114, 0x00511a, 0x005121, 0x00513a, 0x005137, 0x00513c, 0x00513b, - 0x00513f, 0x005140, 0x005152, 0x00514c, 0x005154, 0x005162, 0x007af8, 0x005169, - 0x00516a, 0x00516e, 0x005180, 0x005182, 0x0056d8, 0x00518c, 0x005189, 0x00518f, - 0x005191, 0x005193, 0x005195, 0x005196, 0x0051a4, 0x0051a6, 0x0051a2, 0x0051a9, - 0x0051aa, 0x0051ab, 0x0051b3, 0x0051b1, 0x0051b2, 0x0051b0, 0x0051b5, 0x0051bd, - 0x0051c5, 0x0051c9, 0x0051db, 0x0051e0, 0x008655, 0x0051e9, 0x0051ed, 0x0051f0, - 0x0051f5, 0x0051fe, 0x005204, 0x00520b, 0x005214, 0x00520e, 0x005227, 0x00522a, - 0x00522e, 0x005233, 0x005239, 0x00524f, 0x005244, 0x00524b, 0x00524c, 0x00525e, - 0x005254, 0x00526a, 0x005274, 0x005269, 0x005273, 0x00527f, 0x00527d, 0x00528d, - 0x005294, 0x005292, 0x005271, 0x005288, 0x005291, 0x008fa8, - // plane 1 row 50 - 0x008fa7, 0x0052ac, 0x0052ad, 0x0052bc, 0x0052b5, 0x0052c1, 0x0052cd, 0x0052d7, - 0x0052de, 0x0052e3, 0x0052e6, 0x0098ed, 0x0052e0, 0x0052f3, 0x0052f5, 0x0052f8, - 0x0052f9, 0x005306, 0x005308, 0x007538, 0x00530d, 0x005310, 0x00530f, 0x005315, - 0x00531a, 0x005323, 0x00532f, 0x005331, 0x005333, 0x005338, 0x005340, 0x005346, - 0x005345, 0x004e17, 0x005349, 0x00534d, 0x0051d6, 0x00535e, 0x005369, 0x00536e, - 0x005918, 0x00537b, 0x005377, 0x005382, 0x005396, 0x0053a0, 0x0053a6, 0x0053a5, - 0x0053ae, 0x0053b0, 0x0053b6, 0x0053c3, 0x007c12, 0x0096d9, 0x0053df, 0x0066fc, - 0x0071ee, 0x0053ee, 0x0053e8, 0x0053ed, 0x0053fa, 0x005401, 0x00543d, 0x005440, - 0x00542c, 0x00542d, 0x00543c, 0x00542e, 0x005436, 0x005429, 0x00541d, 0x00544e, - 0x00548f, 0x005475, 0x00548e, 0x00545f, 0x005471, 0x005477, 0x005470, 0x005492, - 0x00547b, 0x005480, 0x005476, 0x005484, 0x005490, 0x005486, 0x0054c7, 0x0054a2, - 0x0054b8, 0x0054a5, 0x0054ac, 0x0054c4, 0x0054c8, 0x0054a8, - // plane 1 row 51 - 0x0054ab, 0x0054c2, 0x0054a4, 0x0054be, 0x0054bc, 0x0054d8, 0x0054e5, 0x0054e6, - 0x00550f, 0x005514, 0x0054fd, 0x0054ee, 0x0054ed, 0x0054fa, 0x0054e2, 0x005539, - 0x005540, 0x005563, 0x00554c, 0x00552e, 0x00555c, 0x005545, 0x005556, 0x005557, - 0x005538, 0x005533, 0x00555d, 0x005599, 0x005580, 0x0054af, 0x00558a, 0x00559f, - 0x00557b, 0x00557e, 0x005598, 0x00559e, 0x0055ae, 0x00557c, 0x005583, 0x0055a9, - 0x005587, 0x0055a8, 0x0055da, 0x0055c5, 0x0055df, 0x0055c4, 0x0055dc, 0x0055e4, - 0x0055d4, 0x005614, 0x0055f7, 0x005616, 0x0055fe, 0x0055fd, 0x00561b, 0x0055f9, - 0x00564e, 0x005650, 0x0071df, 0x005634, 0x005636, 0x005632, 0x005638, 0x00566b, - 0x005664, 0x00562f, 0x00566c, 0x00566a, 0x005686, 0x005680, 0x00568a, 0x0056a0, - 0x005694, 0x00568f, 0x0056a5, 0x0056ae, 0x0056b6, 0x0056b4, 0x0056c2, 0x0056bc, - 0x0056c1, 0x0056c3, 0x0056c0, 0x0056c8, 0x0056ce, 0x0056d1, 0x0056d3, 0x0056d7, - 0x0056ee, 0x0056f9, 0x005700, 0x0056ff, 0x005704, 0x005709, - // plane 1 row 52 - 0x005708, 0x00570b, 0x00570d, 0x005713, 0x005718, 0x005716, 0x0055c7, 0x00571c, - 0x005726, 0x005737, 0x005738, 0x00574e, 0x00573b, 0x005740, 0x00574f, 0x005769, - 0x0057c0, 0x005788, 0x005761, 0x00577f, 0x005789, 0x005793, 0x0057a0, 0x0057b3, - 0x0057a4, 0x0057aa, 0x0057b0, 0x0057c3, 0x0057c6, 0x0057d4, 0x0057d2, 0x0057d3, - 0x00580a, 0x0057d6, 0x0057e3, 0x00580b, 0x005819, 0x00581d, 0x005872, 0x005821, - 0x005862, 0x00584b, 0x005870, 0x006bc0, 0x005852, 0x00583d, 0x005879, 0x005885, - 0x0058b9, 0x00589f, 0x0058ab, 0x0058ba, 0x0058de, 0x0058bb, 0x0058b8, 0x0058ae, - 0x0058c5, 0x0058d3, 0x0058d1, 0x0058d7, 0x0058d9, 0x0058d8, 0x0058e5, 0x0058dc, - 0x0058e4, 0x0058df, 0x0058ef, 0x0058fa, 0x0058f9, 0x0058fb, 0x0058fc, 0x0058fd, - 0x005902, 0x00590a, 0x005910, 0x00591b, 0x0068a6, 0x005925, 0x00592c, 0x00592d, - 0x005932, 0x005938, 0x00593e, 0x007ad2, 0x005955, 0x005950, 0x00594e, 0x00595a, - 0x005958, 0x005962, 0x005960, 0x005967, 0x00596c, 0x005969, - // plane 1 row 53 - 0x005978, 0x005981, 0x00599d, 0x004f5e, 0x004fab, 0x0059a3, 0x0059b2, 0x0059c6, - 0x0059e8, 0x0059dc, 0x00598d, 0x0059d9, 0x0059da, 0x005a25, 0x005a1f, 0x005a11, - 0x005a1c, 0x005a09, 0x005a1a, 0x005a40, 0x005a6c, 0x005a49, 0x005a35, 0x005a36, - 0x005a62, 0x005a6a, 0x005a9a, 0x005abc, 0x005abe, 0x005acb, 0x005ac2, 0x005abd, - 0x005ae3, 0x005ad7, 0x005ae6, 0x005ae9, 0x005ad6, 0x005afa, 0x005afb, 0x005b0c, - 0x005b0b, 0x005b16, 0x005b32, 0x005ad0, 0x005b2a, 0x005b36, 0x005b3e, 0x005b43, - 0x005b45, 0x005b40, 0x005b51, 0x005b55, 0x005b5a, 0x005b5b, 0x005b65, 0x005b69, - 0x005b70, 0x005b73, 0x005b75, 0x005b78, 0x006588, 0x005b7a, 0x005b80, 0x005b83, - 0x005ba6, 0x005bb8, 0x005bc3, 0x005bc7, 0x005bc9, 0x005bd4, 0x005bd0, 0x005be4, - 0x005be6, 0x005be2, 0x005bde, 0x005be5, 0x005beb, 0x005bf0, 0x005bf6, 0x005bf3, - 0x005c05, 0x005c07, 0x005c08, 0x005c0d, 0x005c13, 0x005c20, 0x005c22, 0x005c28, - 0x005c38, 0x005c39, 0x005c41, 0x005c46, 0x005c4e, 0x005c53, - // plane 1 row 54 - 0x005c50, 0x005c4f, 0x005b71, 0x005c6c, 0x005c6e, 0x004e62, 0x005c76, 0x005c79, - 0x005c8c, 0x005c91, 0x005c94, 0x00599b, 0x005cab, 0x005cbb, 0x005cb6, 0x005cbc, - 0x005cb7, 0x005cc5, 0x005cbe, 0x005cc7, 0x005cd9, 0x005ce9, 0x005cfd, 0x005cfa, - 0x005ced, 0x005d8c, 0x005cea, 0x005d0b, 0x005d15, 0x005d17, 0x005d5c, 0x005d1f, - 0x005d1b, 0x005d11, 0x005d14, 0x005d22, 0x005d1a, 0x005d19, 0x005d18, 0x005d4c, - 0x005d52, 0x005d4e, 0x005d4b, 0x005d6c, 0x005d73, 0x005d76, 0x005d87, 0x005d84, - 0x005d82, 0x005da2, 0x005d9d, 0x005dac, 0x005dae, 0x005dbd, 0x005d90, 0x005db7, - 0x005dbc, 0x005dc9, 0x005dcd, 0x005dd3, 0x005dd2, 0x005dd6, 0x005ddb, 0x005deb, - 0x005df2, 0x005df5, 0x005e0b, 0x005e1a, 0x005e19, 0x005e11, 0x005e1b, 0x005e36, - 0x005e37, 0x005e44, 0x005e43, 0x005e40, 0x005e4e, 0x005e57, 0x005e54, 0x005e5f, - 0x005e62, 0x005e64, 0x005e47, 0x005e75, 0x005e76, 0x005e7a, 0x009ebc, 0x005e7f, - 0x005ea0, 0x005ec1, 0x005ec2, 0x005ec8, 0x005ed0, 0x005ecf, - // plane 1 row 55 - 0x005ed6, 0x005ee3, 0x005edd, 0x005eda, 0x005edb, 0x005ee2, 0x005ee1, 0x005ee8, - 0x005ee9, 0x005eec, 0x005ef1, 0x005ef3, 0x005ef0, 0x005ef4, 0x005ef8, 0x005efe, - 0x005f03, 0x005f09, 0x005f5d, 0x005f5c, 0x005f0b, 0x005f11, 0x005f16, 0x005f29, - 0x005f2d, 0x005f38, 0x005f41, 0x005f48, 0x005f4c, 0x005f4e, 0x005f2f, 0x005f51, - 0x005f56, 0x005f57, 0x005f59, 0x005f61, 0x005f6d, 0x005f73, 0x005f77, 0x005f83, - 0x005f82, 0x005f7f, 0x005f8a, 0x005f88, 0x005f91, 0x005f87, 0x005f9e, 0x005f99, - 0x005f98, 0x005fa0, 0x005fa8, 0x005fad, 0x005fbc, 0x005fd6, 0x005ffb, 0x005fe4, - 0x005ff8, 0x005ff1, 0x005fdd, 0x0060b3, 0x005fff, 0x006021, 0x006060, 0x006019, - 0x006010, 0x006029, 0x00600e, 0x006031, 0x00601b, 0x006015, 0x00602b, 0x006026, - 0x00600f, 0x00603a, 0x00605a, 0x006041, 0x00606a, 0x006077, 0x00605f, 0x00604a, - 0x006046, 0x00604d, 0x006063, 0x006043, 0x006064, 0x006042, 0x00606c, 0x00606b, - 0x006059, 0x006081, 0x00608d, 0x0060e7, 0x006083, 0x00609a, - // plane 1 row 56 - 0x006084, 0x00609b, 0x006096, 0x006097, 0x006092, 0x0060a7, 0x00608b, 0x0060e1, - 0x0060b8, 0x0060e0, 0x0060d3, 0x0060b4, 0x005ff0, 0x0060bd, 0x0060c6, 0x0060b5, - 0x0060d8, 0x00614d, 0x006115, 0x006106, 0x0060f6, 0x0060f7, 0x006100, 0x0060f4, - 0x0060fa, 0x006103, 0x006121, 0x0060fb, 0x0060f1, 0x00610d, 0x00610e, 0x006147, - 0x00613e, 0x006128, 0x006127, 0x00614a, 0x00613f, 0x00613c, 0x00612c, 0x006134, - 0x00613d, 0x006142, 0x006144, 0x006173, 0x006177, 0x006158, 0x006159, 0x00615a, - 0x00616b, 0x006174, 0x00616f, 0x006165, 0x006171, 0x00615f, 0x00615d, 0x006153, - 0x006175, 0x006199, 0x006196, 0x006187, 0x0061ac, 0x006194, 0x00619a, 0x00618a, - 0x006191, 0x0061ab, 0x0061ae, 0x0061cc, 0x0061ca, 0x0061c9, 0x0061f7, 0x0061c8, - 0x0061c3, 0x0061c6, 0x0061ba, 0x0061cb, 0x007f79, 0x0061cd, 0x0061e6, 0x0061e3, - 0x0061f6, 0x0061fa, 0x0061f4, 0x0061ff, 0x0061fd, 0x0061fc, 0x0061fe, 0x006200, - 0x006208, 0x006209, 0x00620d, 0x00620c, 0x006214, 0x00621b, - // plane 1 row 57 - 0x00621e, 0x006221, 0x00622a, 0x00622e, 0x006230, 0x006232, 0x006233, 0x006241, - 0x00624e, 0x00625e, 0x006263, 0x00625b, 0x006260, 0x006268, 0x00627c, 0x006282, - 0x006289, 0x00627e, 0x006292, 0x006293, 0x006296, 0x0062d4, 0x006283, 0x006294, - 0x0062d7, 0x0062d1, 0x0062bb, 0x0062cf, 0x0062ff, 0x0062c6, 0x0064d4, 0x0062c8, - 0x0062dc, 0x0062cc, 0x0062ca, 0x0062c2, 0x0062c7, 0x00629b, 0x0062c9, 0x00630c, - 0x0062ee, 0x0062f1, 0x006327, 0x006302, 0x006308, 0x0062ef, 0x0062f5, 0x006350, - 0x00633e, 0x00634d, 0x00641c, 0x00634f, 0x006396, 0x00638e, 0x006380, 0x0063ab, - 0x006376, 0x0063a3, 0x00638f, 0x006389, 0x00639f, 0x0063b5, 0x00636b, 0x006369, - 0x0063be, 0x0063e9, 0x0063c0, 0x0063c6, 0x0063e3, 0x0063c9, 0x0063d2, 0x0063f6, - 0x0063c4, 0x006416, 0x006434, 0x006406, 0x006413, 0x006426, 0x006436, 0x00651d, - 0x006417, 0x006428, 0x00640f, 0x006467, 0x00646f, 0x006476, 0x00644e, 0x00652a, - 0x006495, 0x006493, 0x0064a5, 0x0064a9, 0x006488, 0x0064bc, - // plane 1 row 58 - 0x0064da, 0x0064d2, 0x0064c5, 0x0064c7, 0x0064bb, 0x0064d8, 0x0064c2, 0x0064f1, - 0x0064e7, 0x008209, 0x0064e0, 0x0064e1, 0x0062ac, 0x0064e3, 0x0064ef, 0x00652c, - 0x0064f6, 0x0064f4, 0x0064f2, 0x0064fa, 0x006500, 0x0064fd, 0x006518, 0x00651c, - 0x006505, 0x006524, 0x006523, 0x00652b, 0x006534, 0x006535, 0x006537, 0x006536, - 0x006538, 0x00754b, 0x006548, 0x006556, 0x006555, 0x00654d, 0x006558, 0x00655e, - 0x00655d, 0x006572, 0x006578, 0x006582, 0x006583, 0x008b8a, 0x00659b, 0x00659f, - 0x0065ab, 0x0065b7, 0x0065c3, 0x0065c6, 0x0065c1, 0x0065c4, 0x0065cc, 0x0065d2, - 0x0065db, 0x0065d9, 0x0065e0, 0x0065e1, 0x0065f1, 0x006772, 0x00660a, 0x006603, - 0x0065fb, 0x006773, 0x006635, 0x006636, 0x006634, 0x00661c, 0x00664f, 0x006644, - 0x006649, 0x006641, 0x00665e, 0x00665d, 0x006664, 0x006667, 0x006668, 0x00665f, - 0x006662, 0x006670, 0x006683, 0x006688, 0x00668e, 0x006689, 0x006684, 0x006698, - 0x00669d, 0x0066c1, 0x0066b9, 0x0066c9, 0x0066be, 0x0066bc, - // plane 1 row 59 - 0x0066c4, 0x0066b8, 0x0066d6, 0x0066da, 0x0066e0, 0x00663f, 0x0066e6, 0x0066e9, - 0x0066f0, 0x0066f5, 0x0066f7, 0x00670f, 0x006716, 0x00671e, 0x006726, 0x006727, - 0x009738, 0x00672e, 0x00673f, 0x006736, 0x006741, 0x006738, 0x006737, 0x006746, - 0x00675e, 0x006760, 0x006759, 0x006763, 0x006764, 0x006789, 0x006770, 0x0067a9, - 0x00677c, 0x00676a, 0x00678c, 0x00678b, 0x0067a6, 0x0067a1, 0x006785, 0x0067b7, - 0x0067ef, 0x0067b4, 0x0067ec, 0x0067b3, 0x0067e9, 0x0067b8, 0x0067e4, 0x0067de, - 0x0067dd, 0x0067e2, 0x0067ee, 0x0067b9, 0x0067ce, 0x0067c6, 0x0067e7, 0x006a9c, - 0x00681e, 0x006846, 0x006829, 0x006840, 0x00684d, 0x006832, 0x00684e, 0x0068b3, - 0x00682b, 0x006859, 0x006863, 0x006877, 0x00687f, 0x00689f, 0x00688f, 0x0068ad, - 0x006894, 0x00689d, 0x00689b, 0x006883, 0x006aae, 0x0068b9, 0x006874, 0x0068b5, - 0x0068a0, 0x0068ba, 0x00690f, 0x00688d, 0x00687e, 0x006901, 0x0068ca, 0x006908, - 0x0068d8, 0x006922, 0x006926, 0x0068e1, 0x00690c, 0x0068cd, - // plane 1 row 60 - 0x0068d4, 0x0068e7, 0x0068d5, 0x006936, 0x006912, 0x006904, 0x0068d7, 0x0068e3, - 0x006925, 0x0068f9, 0x0068e0, 0x0068ef, 0x006928, 0x00692a, 0x00691a, 0x006923, - 0x006921, 0x0068c6, 0x006979, 0x006977, 0x00695c, 0x006978, 0x00696b, 0x006954, - 0x00697e, 0x00696e, 0x006939, 0x006974, 0x00693d, 0x006959, 0x006930, 0x006961, - 0x00695e, 0x00695d, 0x006981, 0x00696a, 0x0069b2, 0x0069ae, 0x0069d0, 0x0069bf, - 0x0069c1, 0x0069d3, 0x0069be, 0x0069ce, 0x005be8, 0x0069ca, 0x0069dd, 0x0069bb, - 0x0069c3, 0x0069a7, 0x006a2e, 0x006991, 0x0069a0, 0x00699c, 0x006995, 0x0069b4, - 0x0069de, 0x0069e8, 0x006a02, 0x006a1b, 0x0069ff, 0x006b0a, 0x0069f9, 0x0069f2, - 0x0069e7, 0x006a05, 0x0069b1, 0x006a1e, 0x0069ed, 0x006a14, 0x0069eb, 0x006a0a, - 0x006a12, 0x006ac1, 0x006a23, 0x006a13, 0x006a44, 0x006a0c, 0x006a72, 0x006a36, - 0x006a78, 0x006a47, 0x006a62, 0x006a59, 0x006a66, 0x006a48, 0x006a38, 0x006a22, - 0x006a90, 0x006a8d, 0x006aa0, 0x006a84, 0x006aa2, 0x006aa3, - // plane 1 row 61 - 0x006a97, 0x008617, 0x006abb, 0x006ac3, 0x006ac2, 0x006ab8, 0x006ab3, 0x006aac, - 0x006ade, 0x006ad1, 0x006adf, 0x006aaa, 0x006ada, 0x006aea, 0x006afb, 0x006b05, - 0x008616, 0x006afa, 0x006b12, 0x006b16, 0x009b31, 0x006b1f, 0x006b38, 0x006b37, - 0x0076dc, 0x006b39, 0x0098ee, 0x006b47, 0x006b43, 0x006b49, 0x006b50, 0x006b59, - 0x006b54, 0x006b5b, 0x006b5f, 0x006b61, 0x006b78, 0x006b79, 0x006b7f, 0x006b80, - 0x006b84, 0x006b83, 0x006b8d, 0x006b98, 0x006b95, 0x006b9e, 0x006ba4, 0x006baa, - 0x006bab, 0x006baf, 0x006bb2, 0x006bb1, 0x006bb3, 0x006bb7, 0x006bbc, 0x006bc6, - 0x006bcb, 0x006bd3, 0x006bdf, 0x006bec, 0x006beb, 0x006bf3, 0x006bef, 0x009ebe, - 0x006c08, 0x006c13, 0x006c14, 0x006c1b, 0x006c24, 0x006c23, 0x006c5e, 0x006c55, - 0x006c62, 0x006c6a, 0x006c82, 0x006c8d, 0x006c9a, 0x006c81, 0x006c9b, 0x006c7e, - 0x006c68, 0x006c73, 0x006c92, 0x006c90, 0x006cc4, 0x006cf1, 0x006cd3, 0x006cbd, - 0x006cd7, 0x006cc5, 0x006cdd, 0x006cae, 0x006cb1, 0x006cbe, - // plane 1 row 62 - 0x006cba, 0x006cdb, 0x006cef, 0x006cd9, 0x006cea, 0x006d1f, 0x00884d, 0x006d36, - 0x006d2b, 0x006d3d, 0x006d38, 0x006d19, 0x006d35, 0x006d33, 0x006d12, 0x006d0c, - 0x006d63, 0x006d93, 0x006d64, 0x006d5a, 0x006d79, 0x006d59, 0x006d8e, 0x006d95, - 0x006fe4, 0x006d85, 0x006df9, 0x006e15, 0x006e0a, 0x006db5, 0x006dc7, 0x006de6, - 0x006db8, 0x006dc6, 0x006dec, 0x006dde, 0x006dcc, 0x006de8, 0x006dd2, 0x006dc5, - 0x006dfa, 0x006dd9, 0x006de4, 0x006dd5, 0x006dea, 0x006dee, 0x006e2d, 0x006e6e, - 0x006e2e, 0x006e19, 0x006e72, 0x006e5f, 0x006e3e, 0x006e23, 0x006e6b, 0x006e2b, - 0x006e76, 0x006e4d, 0x006e1f, 0x006e43, 0x006e3a, 0x006e4e, 0x006e24, 0x006eff, - 0x006e1d, 0x006e38, 0x006e82, 0x006eaa, 0x006e98, 0x006ec9, 0x006eb7, 0x006ed3, - 0x006ebd, 0x006eaf, 0x006ec4, 0x006eb2, 0x006ed4, 0x006ed5, 0x006e8f, 0x006ea5, - 0x006ec2, 0x006e9f, 0x006f41, 0x006f11, 0x00704c, 0x006eec, 0x006ef8, 0x006efe, - 0x006f3f, 0x006ef2, 0x006f31, 0x006eef, 0x006f32, 0x006ecc, - // plane 1 row 63 - 0x006f3e, 0x006f13, 0x006ef7, 0x006f86, 0x006f7a, 0x006f78, 0x006f81, 0x006f80, - 0x006f6f, 0x006f5b, 0x006ff3, 0x006f6d, 0x006f82, 0x006f7c, 0x006f58, 0x006f8e, - 0x006f91, 0x006fc2, 0x006f66, 0x006fb3, 0x006fa3, 0x006fa1, 0x006fa4, 0x006fb9, - 0x006fc6, 0x006faa, 0x006fdf, 0x006fd5, 0x006fec, 0x006fd4, 0x006fd8, 0x006ff1, - 0x006fee, 0x006fdb, 0x007009, 0x00700b, 0x006ffa, 0x007011, 0x007001, 0x00700f, - 0x006ffe, 0x00701b, 0x00701a, 0x006f74, 0x00701d, 0x007018, 0x00701f, 0x007030, - 0x00703e, 0x007032, 0x007051, 0x007063, 0x007099, 0x007092, 0x0070af, 0x0070f1, - 0x0070ac, 0x0070b8, 0x0070b3, 0x0070ae, 0x0070df, 0x0070cb, 0x0070dd, 0x0070d9, - 0x007109, 0x0070fd, 0x00711c, 0x007119, 0x007165, 0x007155, 0x007188, 0x007166, - 0x007162, 0x00714c, 0x007156, 0x00716c, 0x00718f, 0x0071fb, 0x007184, 0x007195, - 0x0071a8, 0x0071ac, 0x0071d7, 0x0071b9, 0x0071be, 0x0071d2, 0x0071c9, 0x0071d4, - 0x0071ce, 0x0071e0, 0x0071ec, 0x0071e7, 0x0071f5, 0x0071fc, - // plane 1 row 64 - 0x0071f9, 0x0071ff, 0x00720d, 0x007210, 0x00721b, 0x007228, 0x00722d, 0x00722c, - 0x007230, 0x007232, 0x00723b, 0x00723c, 0x00723f, 0x007240, 0x007246, 0x00724b, - 0x007258, 0x007274, 0x00727e, 0x007282, 0x007281, 0x007287, 0x007292, 0x007296, - 0x0072a2, 0x0072a7, 0x0072b9, 0x0072b2, 0x0072c3, 0x0072c6, 0x0072c4, 0x0072ce, - 0x0072d2, 0x0072e2, 0x0072e0, 0x0072e1, 0x0072f9, 0x0072f7, 0x00500f, 0x007317, - 0x00730a, 0x00731c, 0x007316, 0x00731d, 0x007334, 0x00732f, 0x007329, 0x007325, - 0x00733e, 0x00734e, 0x00734f, 0x009ed8, 0x007357, 0x00736a, 0x007368, 0x007370, - 0x007378, 0x007375, 0x00737b, 0x00737a, 0x0073c8, 0x0073b3, 0x0073ce, 0x0073bb, - 0x0073c0, 0x0073e5, 0x0073ee, 0x0073de, 0x0074a2, 0x007405, 0x00746f, 0x007425, - 0x0073f8, 0x007432, 0x00743a, 0x007455, 0x00743f, 0x00745f, 0x007459, 0x007441, - 0x00745c, 0x007469, 0x007470, 0x007463, 0x00746a, 0x007476, 0x00747e, 0x00748b, - 0x00749e, 0x0074a7, 0x0074ca, 0x0074cf, 0x0074d4, 0x0073f1, - // plane 1 row 65 - 0x0074e0, 0x0074e3, 0x0074e7, 0x0074e9, 0x0074ee, 0x0074f2, 0x0074f0, 0x0074f1, - 0x0074f8, 0x0074f7, 0x007504, 0x007503, 0x007505, 0x00750c, 0x00750e, 0x00750d, - 0x007515, 0x007513, 0x00751e, 0x007526, 0x00752c, 0x00753c, 0x007544, 0x00754d, - 0x00754a, 0x007549, 0x00755b, 0x007546, 0x00755a, 0x007569, 0x007564, 0x007567, - 0x00756b, 0x00756d, 0x007578, 0x007576, 0x007586, 0x007587, 0x007574, 0x00758a, - 0x007589, 0x007582, 0x007594, 0x00759a, 0x00759d, 0x0075a5, 0x0075a3, 0x0075c2, - 0x0075b3, 0x0075c3, 0x0075b5, 0x0075bd, 0x0075b8, 0x0075bc, 0x0075b1, 0x0075cd, - 0x0075ca, 0x0075d2, 0x0075d9, 0x0075e3, 0x0075de, 0x0075fe, 0x0075ff, 0x0075fc, - 0x007601, 0x0075f0, 0x0075fa, 0x0075f2, 0x0075f3, 0x00760b, 0x00760d, 0x007609, - 0x00761f, 0x007627, 0x007620, 0x007621, 0x007622, 0x007624, 0x007634, 0x007630, - 0x00763b, 0x007647, 0x007648, 0x007646, 0x00765c, 0x007658, 0x007661, 0x007662, - 0x007668, 0x007669, 0x00766a, 0x007667, 0x00766c, 0x007670, - // plane 1 row 66 - 0x007672, 0x007676, 0x007678, 0x00767c, 0x007680, 0x007683, 0x007688, 0x00768b, - 0x00768e, 0x007696, 0x007693, 0x007699, 0x00769a, 0x0076b0, 0x0076b4, 0x0076b8, - 0x0076b9, 0x0076ba, 0x0076c2, 0x0076cd, 0x0076d6, 0x0076d2, 0x0076de, 0x0076e1, - 0x0076e5, 0x0076e7, 0x0076ea, 0x00862f, 0x0076fb, 0x007708, 0x007707, 0x007704, - 0x007729, 0x007724, 0x00771e, 0x007725, 0x007726, 0x00771b, 0x007737, 0x007738, - 0x007747, 0x00775a, 0x007768, 0x00776b, 0x00775b, 0x007765, 0x00777f, 0x00777e, - 0x007779, 0x00778e, 0x00778b, 0x007791, 0x0077a0, 0x00779e, 0x0077b0, 0x0077b6, - 0x0077b9, 0x0077bf, 0x0077bc, 0x0077bd, 0x0077bb, 0x0077c7, 0x0077cd, 0x0077d7, - 0x0077da, 0x0077dc, 0x0077e3, 0x0077ee, 0x0077fc, 0x00780c, 0x007812, 0x007926, - 0x007820, 0x00792a, 0x007845, 0x00788e, 0x007874, 0x007886, 0x00787c, 0x00789a, - 0x00788c, 0x0078a3, 0x0078b5, 0x0078aa, 0x0078af, 0x0078d1, 0x0078c6, 0x0078cb, - 0x0078d4, 0x0078be, 0x0078bc, 0x0078c5, 0x0078ca, 0x0078ec, - // plane 1 row 67 - 0x0078e7, 0x0078da, 0x0078fd, 0x0078f4, 0x007907, 0x007912, 0x007911, 0x007919, - 0x00792c, 0x00792b, 0x007940, 0x007960, 0x007957, 0x00795f, 0x00795a, 0x007955, - 0x007953, 0x00797a, 0x00797f, 0x00798a, 0x00799d, 0x0079a7, 0x009f4b, 0x0079aa, - 0x0079ae, 0x0079b3, 0x0079b9, 0x0079ba, 0x0079c9, 0x0079d5, 0x0079e7, 0x0079ec, - 0x0079e1, 0x0079e3, 0x007a08, 0x007a0d, 0x007a18, 0x007a19, 0x007a20, 0x007a1f, - 0x007980, 0x007a31, 0x007a3b, 0x007a3e, 0x007a37, 0x007a43, 0x007a57, 0x007a49, - 0x007a61, 0x007a62, 0x007a69, 0x009f9d, 0x007a70, 0x007a79, 0x007a7d, 0x007a88, - 0x007a97, 0x007a95, 0x007a98, 0x007a96, 0x007aa9, 0x007ac8, 0x007ab0, 0x007ab6, - 0x007ac5, 0x007ac4, 0x007abf, 0x009083, 0x007ac7, 0x007aca, 0x007acd, 0x007acf, - 0x007ad5, 0x007ad3, 0x007ad9, 0x007ada, 0x007add, 0x007ae1, 0x007ae2, 0x007ae6, - 0x007aed, 0x007af0, 0x007b02, 0x007b0f, 0x007b0a, 0x007b06, 0x007b33, 0x007b18, - 0x007b19, 0x007b1e, 0x007b35, 0x007b28, 0x007b36, 0x007b50, - // plane 1 row 68 - 0x007b7a, 0x007b04, 0x007b4d, 0x007b0b, 0x007b4c, 0x007b45, 0x007b75, 0x007b65, - 0x007b74, 0x007b67, 0x007b70, 0x007b71, 0x007b6c, 0x007b6e, 0x007b9d, 0x007b98, - 0x007b9f, 0x007b8d, 0x007b9c, 0x007b9a, 0x007b8b, 0x007b92, 0x007b8f, 0x007b5d, - 0x007b99, 0x007bcb, 0x007bc1, 0x007bcc, 0x007bcf, 0x007bb4, 0x007bc6, 0x007bdd, - 0x007be9, 0x007c11, 0x007c14, 0x007be6, 0x007be5, 0x007c60, 0x007c00, 0x007c07, - 0x007c13, 0x007bf3, 0x007bf7, 0x007c17, 0x007c0d, 0x007bf6, 0x007c23, 0x007c27, - 0x007c2a, 0x007c1f, 0x007c37, 0x007c2b, 0x007c3d, 0x007c4c, 0x007c43, 0x007c54, - 0x007c4f, 0x007c40, 0x007c50, 0x007c58, 0x007c5f, 0x007c64, 0x007c56, 0x007c65, - 0x007c6c, 0x007c75, 0x007c83, 0x007c90, 0x007ca4, 0x007cad, 0x007ca2, 0x007cab, - 0x007ca1, 0x007ca8, 0x007cb3, 0x007cb2, 0x007cb1, 0x007cae, 0x007cb9, 0x007cbd, - 0x007cc0, 0x007cc5, 0x007cc2, 0x007cd8, 0x007cd2, 0x007cdc, 0x007ce2, 0x009b3b, - 0x007cef, 0x007cf2, 0x007cf4, 0x007cf6, 0x007cfa, 0x007d06, - // plane 1 row 69 - 0x007d02, 0x007d1c, 0x007d15, 0x007d0a, 0x007d45, 0x007d4b, 0x007d2e, 0x007d32, - 0x007d3f, 0x007d35, 0x007d46, 0x007d73, 0x007d56, 0x007d4e, 0x007d72, 0x007d68, - 0x007d6e, 0x007d4f, 0x007d63, 0x007d93, 0x007d89, 0x007d5b, 0x007d8f, 0x007d7d, - 0x007d9b, 0x007dba, 0x007dae, 0x007da3, 0x007db5, 0x007dc7, 0x007dbd, 0x007dab, - 0x007e3d, 0x007da2, 0x007daf, 0x007ddc, 0x007db8, 0x007d9f, 0x007db0, 0x007dd8, - 0x007ddd, 0x007de4, 0x007dde, 0x007dfb, 0x007df2, 0x007de1, 0x007e05, 0x007e0a, - 0x007e23, 0x007e21, 0x007e12, 0x007e31, 0x007e1f, 0x007e09, 0x007e0b, 0x007e22, - 0x007e46, 0x007e66, 0x007e3b, 0x007e35, 0x007e39, 0x007e43, 0x007e37, 0x007e32, - 0x007e3a, 0x007e67, 0x007e5d, 0x007e56, 0x007e5e, 0x007e59, 0x007e5a, 0x007e79, - 0x007e6a, 0x007e69, 0x007e7c, 0x007e7b, 0x007e83, 0x007dd5, 0x007e7d, 0x008fae, - 0x007e7f, 0x007e88, 0x007e89, 0x007e8c, 0x007e92, 0x007e90, 0x007e93, 0x007e94, - 0x007e96, 0x007e8e, 0x007e9b, 0x007e9c, 0x007f38, 0x007f3a, - // plane 1 row 70 - 0x007f45, 0x007f4c, 0x007f4d, 0x007f4e, 0x007f50, 0x007f51, 0x007f55, 0x007f54, - 0x007f58, 0x007f5f, 0x007f60, 0x007f68, 0x007f69, 0x007f67, 0x007f78, 0x007f82, - 0x007f86, 0x007f83, 0x007f88, 0x007f87, 0x007f8c, 0x007f94, 0x007f9e, 0x007f9d, - 0x007f9a, 0x007fa3, 0x007faf, 0x007fb2, 0x007fb9, 0x007fae, 0x007fb6, 0x007fb8, - 0x008b71, 0x007fc5, 0x007fc6, 0x007fca, 0x007fd5, 0x007fd4, 0x007fe1, 0x007fe6, - 0x007fe9, 0x007ff3, 0x007ff9, 0x0098dc, 0x008006, 0x008004, 0x00800b, 0x008012, - 0x008018, 0x008019, 0x00801c, 0x008021, 0x008028, 0x00803f, 0x00803b, 0x00804a, - 0x008046, 0x008052, 0x008058, 0x00805a, 0x00805f, 0x008062, 0x008068, 0x008073, - 0x008072, 0x008070, 0x008076, 0x008079, 0x00807d, 0x00807f, 0x008084, 0x008086, - 0x008085, 0x00809b, 0x008093, 0x00809a, 0x0080ad, 0x005190, 0x0080ac, 0x0080db, - 0x0080e5, 0x0080d9, 0x0080dd, 0x0080c4, 0x0080da, 0x0080d6, 0x008109, 0x0080ef, - 0x0080f1, 0x00811b, 0x008129, 0x008123, 0x00812f, 0x00814b, - // plane 1 row 71 - 0x00968b, 0x008146, 0x00813e, 0x008153, 0x008151, 0x0080fc, 0x008171, 0x00816e, - 0x008165, 0x008166, 0x008174, 0x008183, 0x008188, 0x00818a, 0x008180, 0x008182, - 0x0081a0, 0x008195, 0x0081a4, 0x0081a3, 0x00815f, 0x008193, 0x0081a9, 0x0081b0, - 0x0081b5, 0x0081be, 0x0081b8, 0x0081bd, 0x0081c0, 0x0081c2, 0x0081ba, 0x0081c9, - 0x0081cd, 0x0081d1, 0x0081d9, 0x0081d8, 0x0081c8, 0x0081da, 0x0081df, 0x0081e0, - 0x0081e7, 0x0081fa, 0x0081fb, 0x0081fe, 0x008201, 0x008202, 0x008205, 0x008207, - 0x00820a, 0x00820d, 0x008210, 0x008216, 0x008229, 0x00822b, 0x008238, 0x008233, - 0x008240, 0x008259, 0x008258, 0x00825d, 0x00825a, 0x00825f, 0x008264, 0x008262, - 0x008268, 0x00826a, 0x00826b, 0x00822e, 0x008271, 0x008277, 0x008278, 0x00827e, - 0x00828d, 0x008292, 0x0082ab, 0x00829f, 0x0082bb, 0x0082ac, 0x0082e1, 0x0082e3, - 0x0082df, 0x0082d2, 0x0082f4, 0x0082f3, 0x0082fa, 0x008393, 0x008303, 0x0082fb, - 0x0082f9, 0x0082de, 0x008306, 0x0082dc, 0x008309, 0x0082d9, - // plane 1 row 72 - 0x008335, 0x008334, 0x008316, 0x008332, 0x008331, 0x008340, 0x008339, 0x008350, - 0x008345, 0x00832f, 0x00832b, 0x008317, 0x008318, 0x008385, 0x00839a, 0x0083aa, - 0x00839f, 0x0083a2, 0x008396, 0x008323, 0x00838e, 0x008387, 0x00838a, 0x00837c, - 0x0083b5, 0x008373, 0x008375, 0x0083a0, 0x008389, 0x0083a8, 0x0083f4, 0x008413, - 0x0083eb, 0x0083ce, 0x0083fd, 0x008403, 0x0083d8, 0x00840b, 0x0083c1, 0x0083f7, - 0x008407, 0x0083e0, 0x0083f2, 0x00840d, 0x008422, 0x008420, 0x0083bd, 0x008438, - 0x008506, 0x0083fb, 0x00846d, 0x00842a, 0x00843c, 0x00855a, 0x008484, 0x008477, - 0x00846b, 0x0084ad, 0x00846e, 0x008482, 0x008469, 0x008446, 0x00842c, 0x00846f, - 0x008479, 0x008435, 0x0084ca, 0x008462, 0x0084b9, 0x0084bf, 0x00849f, 0x0084d9, - 0x0084cd, 0x0084bb, 0x0084da, 0x0084d0, 0x0084c1, 0x0084c6, 0x0084d6, 0x0084a1, - 0x008521, 0x0084ff, 0x0084f4, 0x008517, 0x008518, 0x00852c, 0x00851f, 0x008515, - 0x008514, 0x0084fc, 0x008540, 0x008563, 0x008558, 0x008548, - // plane 1 row 73 - 0x008541, 0x008602, 0x00854b, 0x008555, 0x008580, 0x0085a4, 0x008588, 0x008591, - 0x00858a, 0x0085a8, 0x00856d, 0x008594, 0x00859b, 0x0085ea, 0x008587, 0x00859c, - 0x008577, 0x00857e, 0x008590, 0x0085c9, 0x0085ba, 0x0085cf, 0x0085b9, 0x0085d0, - 0x0085d5, 0x0085dd, 0x0085e5, 0x0085dc, 0x0085f9, 0x00860a, 0x008613, 0x00860b, - 0x0085fe, 0x0085fa, 0x008606, 0x008622, 0x00861a, 0x008630, 0x00863f, 0x00864d, - 0x004e55, 0x008654, 0x00865f, 0x008667, 0x008671, 0x008693, 0x0086a3, 0x0086a9, - 0x0086aa, 0x00868b, 0x00868c, 0x0086b6, 0x0086af, 0x0086c4, 0x0086c6, 0x0086b0, - 0x0086c9, 0x008823, 0x0086ab, 0x0086d4, 0x0086de, 0x0086e9, 0x0086ec, 0x0086df, - 0x0086db, 0x0086ef, 0x008712, 0x008706, 0x008708, 0x008700, 0x008703, 0x0086fb, - 0x008711, 0x008709, 0x00870d, 0x0086f9, 0x00870a, 0x008734, 0x00873f, 0x008737, - 0x00873b, 0x008725, 0x008729, 0x00871a, 0x008760, 0x00875f, 0x008778, 0x00874c, - 0x00874e, 0x008774, 0x008757, 0x008768, 0x00876e, 0x008759, - // plane 1 row 74 - 0x008753, 0x008763, 0x00876a, 0x008805, 0x0087a2, 0x00879f, 0x008782, 0x0087af, - 0x0087cb, 0x0087bd, 0x0087c0, 0x0087d0, 0x0096d6, 0x0087ab, 0x0087c4, 0x0087b3, - 0x0087c7, 0x0087c6, 0x0087bb, 0x0087ef, 0x0087f2, 0x0087e0, 0x00880f, 0x00880d, - 0x0087fe, 0x0087f6, 0x0087f7, 0x00880e, 0x0087d2, 0x008811, 0x008816, 0x008815, - 0x008822, 0x008821, 0x008831, 0x008836, 0x008839, 0x008827, 0x00883b, 0x008844, - 0x008842, 0x008852, 0x008859, 0x00885e, 0x008862, 0x00886b, 0x008881, 0x00887e, - 0x00889e, 0x008875, 0x00887d, 0x0088b5, 0x008872, 0x008882, 0x008897, 0x008892, - 0x0088ae, 0x008899, 0x0088a2, 0x00888d, 0x0088a4, 0x0088b0, 0x0088bf, 0x0088b1, - 0x0088c3, 0x0088c4, 0x0088d4, 0x0088d8, 0x0088d9, 0x0088dd, 0x0088f9, 0x008902, - 0x0088fc, 0x0088f4, 0x0088e8, 0x0088f2, 0x008904, 0x00890c, 0x00890a, 0x008913, - 0x008943, 0x00891e, 0x008925, 0x00892a, 0x00892b, 0x008941, 0x008944, 0x00893b, - 0x008936, 0x008938, 0x00894c, 0x00891d, 0x008960, 0x00895e, - // plane 1 row 75 - 0x008966, 0x008964, 0x00896d, 0x00896a, 0x00896f, 0x008974, 0x008977, 0x00897e, - 0x008983, 0x008988, 0x00898a, 0x008993, 0x008998, 0x0089a1, 0x0089a9, 0x0089a6, - 0x0089ac, 0x0089af, 0x0089b2, 0x0089ba, 0x0089bd, 0x0089bf, 0x0089c0, 0x0089da, - 0x0089dc, 0x0089dd, 0x0089e7, 0x0089f4, 0x0089f8, 0x008a03, 0x008a16, 0x008a10, - 0x008a0c, 0x008a1b, 0x008a1d, 0x008a25, 0x008a36, 0x008a41, 0x008a5b, 0x008a52, - 0x008a46, 0x008a48, 0x008a7c, 0x008a6d, 0x008a6c, 0x008a62, 0x008a85, 0x008a82, - 0x008a84, 0x008aa8, 0x008aa1, 0x008a91, 0x008aa5, 0x008aa6, 0x008a9a, 0x008aa3, - 0x008ac4, 0x008acd, 0x008ac2, 0x008ada, 0x008aeb, 0x008af3, 0x008ae7, 0x008ae4, - 0x008af1, 0x008b14, 0x008ae0, 0x008ae2, 0x008af7, 0x008ade, 0x008adb, 0x008b0c, - 0x008b07, 0x008b1a, 0x008ae1, 0x008b16, 0x008b10, 0x008b17, 0x008b20, 0x008b33, - 0x0097ab, 0x008b26, 0x008b2b, 0x008b3e, 0x008b28, 0x008b41, 0x008b4c, 0x008b4f, - 0x008b4e, 0x008b49, 0x008b56, 0x008b5b, 0x008b5a, 0x008b6b, - // plane 1 row 76 - 0x008b5f, 0x008b6c, 0x008b6f, 0x008b74, 0x008b7d, 0x008b80, 0x008b8c, 0x008b8e, - 0x008b92, 0x008b93, 0x008b96, 0x008b99, 0x008b9a, 0x008c3a, 0x008c41, 0x008c3f, - 0x008c48, 0x008c4c, 0x008c4e, 0x008c50, 0x008c55, 0x008c62, 0x008c6c, 0x008c78, - 0x008c7a, 0x008c82, 0x008c89, 0x008c85, 0x008c8a, 0x008c8d, 0x008c8e, 0x008c94, - 0x008c7c, 0x008c98, 0x00621d, 0x008cad, 0x008caa, 0x008cbd, 0x008cb2, 0x008cb3, - 0x008cae, 0x008cb6, 0x008cc8, 0x008cc1, 0x008ce4, 0x008ce3, 0x008cda, 0x008cfd, - 0x008cfa, 0x008cfb, 0x008d04, 0x008d05, 0x008d0a, 0x008d07, 0x008d0f, 0x008d0d, - 0x008d10, 0x009f4e, 0x008d13, 0x008ccd, 0x008d14, 0x008d16, 0x008d67, 0x008d6d, - 0x008d71, 0x008d73, 0x008d81, 0x008d99, 0x008dc2, 0x008dbe, 0x008dba, 0x008dcf, - 0x008dda, 0x008dd6, 0x008dcc, 0x008ddb, 0x008dcb, 0x008dea, 0x008deb, 0x008ddf, - 0x008de3, 0x008dfc, 0x008e08, 0x008e09, 0x008dff, 0x008e1d, 0x008e1e, 0x008e10, - 0x008e1f, 0x008e42, 0x008e35, 0x008e30, 0x008e34, 0x008e4a, - // plane 1 row 77 - 0x008e47, 0x008e49, 0x008e4c, 0x008e50, 0x008e48, 0x008e59, 0x008e64, 0x008e60, - 0x008e2a, 0x008e63, 0x008e55, 0x008e76, 0x008e72, 0x008e7c, 0x008e81, 0x008e87, - 0x008e85, 0x008e84, 0x008e8b, 0x008e8a, 0x008e93, 0x008e91, 0x008e94, 0x008e99, - 0x008eaa, 0x008ea1, 0x008eac, 0x008eb0, 0x008ec6, 0x008eb1, 0x008ebe, 0x008ec5, - 0x008ec8, 0x008ecb, 0x008edb, 0x008ee3, 0x008efc, 0x008efb, 0x008eeb, 0x008efe, - 0x008f0a, 0x008f05, 0x008f15, 0x008f12, 0x008f19, 0x008f13, 0x008f1c, 0x008f1f, - 0x008f1b, 0x008f0c, 0x008f26, 0x008f33, 0x008f3b, 0x008f39, 0x008f45, 0x008f42, - 0x008f3e, 0x008f4c, 0x008f49, 0x008f46, 0x008f4e, 0x008f57, 0x008f5c, 0x008f62, - 0x008f63, 0x008f64, 0x008f9c, 0x008f9f, 0x008fa3, 0x008fad, 0x008faf, 0x008fb7, - 0x008fda, 0x008fe5, 0x008fe2, 0x008fea, 0x008fef, 0x009087, 0x008ff4, 0x009005, - 0x008ff9, 0x008ffa, 0x009011, 0x009015, 0x009021, 0x00900d, 0x00901e, 0x009016, - 0x00900b, 0x009027, 0x009036, 0x009035, 0x009039, 0x008ff8, - // plane 1 row 78 - 0x00904f, 0x009050, 0x009051, 0x009052, 0x00900e, 0x009049, 0x00903e, 0x009056, - 0x009058, 0x00905e, 0x009068, 0x00906f, 0x009076, 0x0096a8, 0x009072, 0x009082, - 0x00907d, 0x009081, 0x009080, 0x00908a, 0x009089, 0x00908f, 0x0090a8, 0x0090af, - 0x0090b1, 0x0090b5, 0x0090e2, 0x0090e4, 0x006248, 0x0090db, 0x009102, 0x009112, - 0x009119, 0x009132, 0x009130, 0x00914a, 0x009156, 0x009158, 0x009163, 0x009165, - 0x009169, 0x009173, 0x009172, 0x00918b, 0x009189, 0x009182, 0x0091a2, 0x0091ab, - 0x0091af, 0x0091aa, 0x0091b5, 0x0091b4, 0x0091ba, 0x0091c0, 0x0091c1, 0x0091c9, - 0x0091cb, 0x0091d0, 0x0091d6, 0x0091df, 0x0091e1, 0x0091db, 0x0091fc, 0x0091f5, - 0x0091f6, 0x00921e, 0x0091ff, 0x009214, 0x00922c, 0x009215, 0x009211, 0x00925e, - 0x009257, 0x009245, 0x009249, 0x009264, 0x009248, 0x009295, 0x00923f, 0x00924b, - 0x009250, 0x00929c, 0x009296, 0x009293, 0x00929b, 0x00925a, 0x0092cf, 0x0092b9, - 0x0092b7, 0x0092e9, 0x00930f, 0x0092fa, 0x009344, 0x00932e, - // plane 1 row 79 - 0x009319, 0x009322, 0x00931a, 0x009323, 0x00933a, 0x009335, 0x00933b, 0x00935c, - 0x009360, 0x00937c, 0x00936e, 0x009356, 0x0093b0, 0x0093ac, 0x0093ad, 0x009394, - 0x0093b9, 0x0093d6, 0x0093d7, 0x0093e8, 0x0093e5, 0x0093d8, 0x0093c3, 0x0093dd, - 0x0093d0, 0x0093c8, 0x0093e4, 0x00941a, 0x009414, 0x009413, 0x009403, 0x009407, - 0x009410, 0x009436, 0x00942b, 0x009435, 0x009421, 0x00943a, 0x009441, 0x009452, - 0x009444, 0x00945b, 0x009460, 0x009462, 0x00945e, 0x00946a, 0x009229, 0x009470, - 0x009475, 0x009477, 0x00947d, 0x00945a, 0x00947c, 0x00947e, 0x009481, 0x00947f, - 0x009582, 0x009587, 0x00958a, 0x009594, 0x009596, 0x009598, 0x009599, 0x0095a0, - 0x0095a8, 0x0095a7, 0x0095ad, 0x0095bc, 0x0095bb, 0x0095b9, 0x0095be, 0x0095ca, - 0x006ff6, 0x0095c3, 0x0095cd, 0x0095cc, 0x0095d5, 0x0095d4, 0x0095d6, 0x0095dc, - 0x0095e1, 0x0095e5, 0x0095e2, 0x009621, 0x009628, 0x00962e, 0x00962f, 0x009642, - 0x00964c, 0x00964f, 0x00964b, 0x009677, 0x00965c, 0x00965e, - // plane 1 row 80 - 0x00965d, 0x00965f, 0x009666, 0x009672, 0x00966c, 0x00968d, 0x009698, 0x009695, - 0x009697, 0x0096aa, 0x0096a7, 0x0096b1, 0x0096b2, 0x0096b0, 0x0096b4, 0x0096b6, - 0x0096b8, 0x0096b9, 0x0096ce, 0x0096cb, 0x0096c9, 0x0096cd, 0x00894d, 0x0096dc, - 0x00970d, 0x0096d5, 0x0096f9, 0x009704, 0x009706, 0x009708, 0x009713, 0x00970e, - 0x009711, 0x00970f, 0x009716, 0x009719, 0x009724, 0x00972a, 0x009730, 0x009739, - 0x00973d, 0x00973e, 0x009744, 0x009746, 0x009748, 0x009742, 0x009749, 0x00975c, - 0x009760, 0x009764, 0x009766, 0x009768, 0x0052d2, 0x00976b, 0x009771, 0x009779, - 0x009785, 0x00977c, 0x009781, 0x00977a, 0x009786, 0x00978b, 0x00978f, 0x009790, - 0x00979c, 0x0097a8, 0x0097a6, 0x0097a3, 0x0097b3, 0x0097b4, 0x0097c3, 0x0097c6, - 0x0097c8, 0x0097cb, 0x0097dc, 0x0097ed, 0x009f4f, 0x0097f2, 0x007adf, 0x0097f6, - 0x0097f5, 0x00980f, 0x00980c, 0x009838, 0x009824, 0x009821, 0x009837, 0x00983d, - 0x009846, 0x00984f, 0x00984b, 0x00986b, 0x00986f, 0x009870, - // plane 1 row 81 - 0x009871, 0x009874, 0x009873, 0x0098aa, 0x0098af, 0x0098b1, 0x0098b6, 0x0098c4, - 0x0098c3, 0x0098c6, 0x0098e9, 0x0098eb, 0x009903, 0x009909, 0x009912, 0x009914, - 0x009918, 0x009921, 0x00991d, 0x00991e, 0x009924, 0x009920, 0x00992c, 0x00992e, - 0x00993d, 0x00993e, 0x009942, 0x009949, 0x009945, 0x009950, 0x00994b, 0x009951, - 0x009952, 0x00994c, 0x009955, 0x009997, 0x009998, 0x0099a5, 0x0099ad, 0x0099ae, - 0x0099bc, 0x0099df, 0x0099db, 0x0099dd, 0x0099d8, 0x0099d1, 0x0099ed, 0x0099ee, - 0x0099f1, 0x0099f2, 0x0099fb, 0x0099f8, 0x009a01, 0x009a0f, 0x009a05, 0x0099e2, - 0x009a19, 0x009a2b, 0x009a37, 0x009a45, 0x009a42, 0x009a40, 0x009a43, 0x009a3e, - 0x009a55, 0x009a4d, 0x009a5b, 0x009a57, 0x009a5f, 0x009a62, 0x009a65, 0x009a64, - 0x009a69, 0x009a6b, 0x009a6a, 0x009aad, 0x009ab0, 0x009abc, 0x009ac0, 0x009acf, - 0x009ad1, 0x009ad3, 0x009ad4, 0x009ade, 0x009adf, 0x009ae2, 0x009ae3, 0x009ae6, - 0x009aef, 0x009aeb, 0x009aee, 0x009af4, 0x009af1, 0x009af7, - // plane 1 row 82 - 0x009afb, 0x009b06, 0x009b18, 0x009b1a, 0x009b1f, 0x009b22, 0x009b23, 0x009b25, - 0x009b27, 0x009b28, 0x009b29, 0x009b2a, 0x009b2e, 0x009b2f, 0x009b32, 0x009b44, - 0x009b43, 0x009b4f, 0x009b4d, 0x009b4e, 0x009b51, 0x009b58, 0x009b74, 0x009b93, - 0x009b83, 0x009b91, 0x009b96, 0x009b97, 0x009b9f, 0x009ba0, 0x009ba8, 0x009bb4, - 0x009bc0, 0x009bca, 0x009bb9, 0x009bc6, 0x009bcf, 0x009bd1, 0x009bd2, 0x009be3, - 0x009be2, 0x009be4, 0x009bd4, 0x009be1, 0x009c3a, 0x009bf2, 0x009bf1, 0x009bf0, - 0x009c15, 0x009c14, 0x009c09, 0x009c13, 0x009c0c, 0x009c06, 0x009c08, 0x009c12, - 0x009c0a, 0x009c04, 0x009c2e, 0x009c1b, 0x009c25, 0x009c24, 0x009c21, 0x009c30, - 0x009c47, 0x009c32, 0x009c46, 0x009c3e, 0x009c5a, 0x009c60, 0x009c67, 0x009c76, - 0x009c78, 0x009ce7, 0x009cec, 0x009cf0, 0x009d09, 0x009d08, 0x009ceb, 0x009d03, - 0x009d06, 0x009d2a, 0x009d26, 0x009daf, 0x009d23, 0x009d1f, 0x009d44, 0x009d15, - 0x009d12, 0x009d41, 0x009d3f, 0x009d3e, 0x009d46, 0x009d48, - // plane 1 row 83 - 0x009d5d, 0x009d5e, 0x009d64, 0x009d51, 0x009d50, 0x009d59, 0x009d72, 0x009d89, - 0x009d87, 0x009dab, 0x009d6f, 0x009d7a, 0x009d9a, 0x009da4, 0x009da9, 0x009db2, - 0x009dc4, 0x009dc1, 0x009dbb, 0x009db8, 0x009dba, 0x009dc6, 0x009dcf, 0x009dc2, - 0x009dd9, 0x009dd3, 0x009df8, 0x009de6, 0x009ded, 0x009def, 0x009dfd, 0x009e1a, - 0x009e1b, 0x009e1e, 0x009e75, 0x009e79, 0x009e7d, 0x009e81, 0x009e88, 0x009e8b, - 0x009e8c, 0x009e92, 0x009e95, 0x009e91, 0x009e9d, 0x009ea5, 0x009ea9, 0x009eb8, - 0x009eaa, 0x009ead, 0x009761, 0x009ecc, 0x009ece, 0x009ecf, 0x009ed0, 0x009ed4, - 0x009edc, 0x009ede, 0x009edd, 0x009ee0, 0x009ee5, 0x009ee8, 0x009eef, 0x009ef4, - 0x009ef6, 0x009ef7, 0x009ef9, 0x009efb, 0x009efc, 0x009efd, 0x009f07, 0x009f08, - 0x0076b7, 0x009f15, 0x009f21, 0x009f2c, 0x009f3e, 0x009f4a, 0x009f52, 0x009f54, - 0x009f63, 0x009f5f, 0x009f60, 0x009f61, 0x009f66, 0x009f67, 0x009f6c, 0x009f6a, - 0x009f77, 0x009f72, 0x009f76, 0x009f95, 0x009f9c, 0x009fa0, - // plane 1 row 84 - 0x00582f, 0x0069c7, 0x009059, 0x007464, 0x0051dc, 0x007199, 0x005653, 0x005de2, - 0x005e14, 0x005e18, 0x005e58, 0x005e5e, 0x005ebe, 0x00f928, 0x005ecb, 0x005ef9, - 0x005f00, 0x005f02, 0x005f07, 0x005f1d, 0x005f23, 0x005f34, 0x005f36, 0x005f3d, - 0x005f40, 0x005f45, 0x005f54, 0x005f58, 0x005f64, 0x005f67, 0x005f7d, 0x005f89, - 0x005f9c, 0x005fa7, 0x005faf, 0x005fb5, 0x005fb7, 0x005fc9, 0x005fde, 0x005fe1, - 0x005fe9, 0x00600d, 0x006014, 0x006018, 0x006033, 0x006035, 0x006047, 0x00fa3d, - 0x00609d, 0x00609e, 0x0060cb, 0x0060d4, 0x0060d5, 0x0060dd, 0x0060f8, 0x00611c, - 0x00612b, 0x006130, 0x006137, 0x00fa3e, 0x00618d, 0x00fa3f, 0x0061bc, 0x0061b9, - 0x00fa40, 0x006222, 0x00623e, 0x006243, 0x006256, 0x00625a, 0x00626f, 0x006285, - 0x0062c4, 0x0062d6, 0x0062fc, 0x00630a, 0x006318, 0x006339, 0x006343, 0x006365, - 0x00637c, 0x0063e5, 0x0063ed, 0x0063f5, 0x006410, 0x006414, 0x006422, 0x006479, - 0x006451, 0x006460, 0x00646d, 0x0064ce, 0x0064be, 0x0064bf, - // plane 1 row 85 - 0x0064c4, 0x0064ca, 0x0064d0, 0x0064f7, 0x0064fb, 0x006522, 0x006529, 0x00fa41, - 0x006567, 0x00659d, 0x00fa42, 0x006600, 0x006609, 0x006615, 0x00661e, 0x00663a, - 0x006622, 0x006624, 0x00662b, 0x006630, 0x006631, 0x006633, 0x0066fb, 0x006648, - 0x00664c, 0x0231c4, 0x006659, 0x00665a, 0x006661, 0x006665, 0x006673, 0x006677, - 0x006678, 0x00668d, 0x00fa43, 0x0066a0, 0x0066b2, 0x0066bb, 0x0066c6, 0x0066c8, - 0x003b22, 0x0066db, 0x0066e8, 0x0066fa, 0x006713, 0x00f929, 0x006733, 0x006766, - 0x006747, 0x006748, 0x00677b, 0x006781, 0x006793, 0x006798, 0x00679b, 0x0067bb, - 0x0067f9, 0x0067c0, 0x0067d7, 0x0067fc, 0x006801, 0x006852, 0x00681d, 0x00682c, - 0x006831, 0x00685b, 0x006872, 0x006875, 0x00fa44, 0x0068a3, 0x0068a5, 0x0068b2, - 0x0068c8, 0x0068d0, 0x0068e8, 0x0068ed, 0x0068f0, 0x0068f1, 0x0068fc, 0x00690a, - 0x006949, 0x0235c4, 0x006935, 0x006942, 0x006957, 0x006963, 0x006964, 0x006968, - 0x006980, 0x00fa14, 0x0069a5, 0x0069ad, 0x0069cf, 0x003bb6, - // plane 1 row 86 - 0x003bc3, 0x0069e2, 0x0069e9, 0x0069ea, 0x0069f5, 0x0069f6, 0x006a0f, 0x006a15, - 0x02373f, 0x006a3b, 0x006a3e, 0x006a45, 0x006a50, 0x006a56, 0x006a5b, 0x006a6b, - 0x006a73, 0x023763, 0x006a89, 0x006a94, 0x006a9d, 0x006a9e, 0x006aa5, 0x006ae4, - 0x006ae7, 0x003c0f, 0x00f91d, 0x006b1b, 0x006b1e, 0x006b2c, 0x006b35, 0x006b46, - 0x006b56, 0x006b60, 0x006b65, 0x006b67, 0x006b77, 0x006b82, 0x006ba9, 0x006bad, - 0x00f970, 0x006bcf, 0x006bd6, 0x006bd7, 0x006bff, 0x006c05, 0x006c10, 0x006c33, - 0x006c59, 0x006c5c, 0x006caa, 0x006c74, 0x006c76, 0x006c85, 0x006c86, 0x006c98, - 0x006c9c, 0x006cfb, 0x006cc6, 0x006cd4, 0x006ce0, 0x006ceb, 0x006cee, 0x023cfe, - 0x006d04, 0x006d0e, 0x006d2e, 0x006d31, 0x006d39, 0x006d3f, 0x006d58, 0x006d65, - 0x00fa45, 0x006d82, 0x006d87, 0x006d89, 0x006d94, 0x006daa, 0x006dac, 0x006dbf, - 0x006dc4, 0x006dd6, 0x006dda, 0x006ddb, 0x006ddd, 0x006dfc, 0x00fa46, 0x006e34, - 0x006e44, 0x006e5c, 0x006e5e, 0x006eab, 0x006eb1, 0x006ec1, - // plane 1 row 87 - 0x006ec7, 0x006ece, 0x006f10, 0x006f1a, 0x00fa47, 0x006f2a, 0x006f2f, 0x006f33, - 0x006f51, 0x006f59, 0x006f5e, 0x006f61, 0x006f62, 0x006f7e, 0x006f88, 0x006f8c, - 0x006f8d, 0x006f94, 0x006fa0, 0x006fa7, 0x006fb6, 0x006fbc, 0x006fc7, 0x006fca, - 0x006ff9, 0x006ff0, 0x006ff5, 0x007005, 0x007006, 0x007028, 0x00704a, 0x00705d, - 0x00705e, 0x00704e, 0x007064, 0x007075, 0x007085, 0x0070a4, 0x0070ab, 0x0070b7, - 0x0070d4, 0x0070d8, 0x0070e4, 0x00710f, 0x00712b, 0x00711e, 0x007120, 0x00712e, - 0x007130, 0x007146, 0x007147, 0x007151, 0x00fa48, 0x007152, 0x00715c, 0x007160, - 0x007168, 0x00fa15, 0x007185, 0x007187, 0x007192, 0x0071c1, 0x0071ba, 0x0071c4, - 0x0071fe, 0x007200, 0x007215, 0x007255, 0x007256, 0x003e3f, 0x00728d, 0x00729b, - 0x0072be, 0x0072c0, 0x0072fb, 0x0247f1, 0x007327, 0x007328, 0x00fa16, 0x007350, - 0x007366, 0x00737c, 0x007395, 0x00739f, 0x0073a0, 0x0073a2, 0x0073a6, 0x0073ab, - 0x0073c9, 0x0073cf, 0x0073d6, 0x0073d9, 0x0073e3, 0x0073e9, - // plane 1 row 88 - 0x007407, 0x00740a, 0x00741a, 0x00741b, 0x00fa4a, 0x007426, 0x007428, 0x00742a, - 0x00742b, 0x00742c, 0x00742e, 0x00742f, 0x007430, 0x007444, 0x007446, 0x007447, - 0x00744b, 0x007457, 0x007462, 0x00746b, 0x00746d, 0x007486, 0x007487, 0x007489, - 0x007498, 0x00749c, 0x00749f, 0x0074a3, 0x007490, 0x0074a6, 0x0074a8, 0x0074a9, - 0x0074b5, 0x0074bf, 0x0074c8, 0x0074c9, 0x0074da, 0x0074ff, 0x007501, 0x007517, - 0x00752f, 0x00756f, 0x007579, 0x007592, 0x003f72, 0x0075ce, 0x0075e4, 0x007600, - 0x007602, 0x007608, 0x007615, 0x007616, 0x007619, 0x00761e, 0x00762d, 0x007635, - 0x007643, 0x00764b, 0x007664, 0x007665, 0x00766d, 0x00766f, 0x007671, 0x007681, - 0x00769b, 0x00769d, 0x00769e, 0x0076a6, 0x0076aa, 0x0076b6, 0x0076c5, 0x0076cc, - 0x0076ce, 0x0076d4, 0x0076e6, 0x0076f1, 0x0076fc, 0x00770a, 0x007719, 0x007734, - 0x007736, 0x007746, 0x00774d, 0x00774e, 0x00775c, 0x00775f, 0x007762, 0x00777a, - 0x007780, 0x007794, 0x0077aa, 0x0077e0, 0x00782d, 0x02548e, - // plane 1 row 89 - 0x007843, 0x00784e, 0x00784f, 0x007851, 0x007868, 0x00786e, 0x00fa4b, 0x0078b0, - 0x02550e, 0x0078ad, 0x0078e4, 0x0078f2, 0x007900, 0x0078f7, 0x00791c, 0x00792e, - 0x007931, 0x007934, 0x00fa4c, 0x00fa4d, 0x007945, 0x007946, 0x00fa4e, 0x00fa4f, - 0x00fa50, 0x00795c, 0x00fa51, 0x00fa19, 0x00fa1a, 0x007979, 0x00fa52, 0x00fa53, - 0x00fa1b, 0x007998, 0x0079b1, 0x0079b8, 0x0079c8, 0x0079ca, 0x025771, 0x0079d4, - 0x0079de, 0x0079eb, 0x0079ed, 0x007a03, 0x00fa54, 0x007a39, 0x007a5d, 0x007a6d, - 0x00fa55, 0x007a85, 0x007aa0, 0x0259c4, 0x007ab3, 0x007abb, 0x007ace, 0x007aeb, - 0x007afd, 0x007b12, 0x007b2d, 0x007b3b, 0x007b47, 0x007b4e, 0x007b60, 0x007b6d, - 0x007b6f, 0x007b72, 0x007b9e, 0x00fa56, 0x007bd7, 0x007bd9, 0x007c01, 0x007c31, - 0x007c1e, 0x007c20, 0x007c33, 0x007c36, 0x004264, 0x025da1, 0x007c59, 0x007c6d, - 0x007c79, 0x007c8f, 0x007c94, 0x007ca0, 0x007cbc, 0x007cd5, 0x007cd9, 0x007cdd, - 0x007d07, 0x007d08, 0x007d13, 0x007d1d, 0x007d23, 0x007d31, - // plane 1 row 90 - 0x007d41, 0x007d48, 0x007d53, 0x007d5c, 0x007d7a, 0x007d83, 0x007d8b, 0x007da0, - 0x007da6, 0x007dc2, 0x007dcc, 0x007dd6, 0x007de3, 0x00fa57, 0x007e28, 0x007e08, - 0x007e11, 0x007e15, 0x00fa59, 0x007e47, 0x007e52, 0x007e61, 0x007e8a, 0x007e8d, - 0x007f47, 0x00fa5a, 0x007f91, 0x007f97, 0x007fbf, 0x007fce, 0x007fdb, 0x007fdf, - 0x007fec, 0x007fee, 0x007ffa, 0x00fa5b, 0x008014, 0x008026, 0x008035, 0x008037, - 0x00803c, 0x0080ca, 0x0080d7, 0x0080e0, 0x0080f3, 0x008118, 0x00814a, 0x008160, - 0x008167, 0x008168, 0x00816d, 0x0081bb, 0x0081ca, 0x0081cf, 0x0081d7, 0x00fa5c, - 0x004453, 0x00445b, 0x008260, 0x008274, 0x026aff, 0x00828e, 0x0082a1, 0x0082a3, - 0x0082a4, 0x0082a9, 0x0082ae, 0x0082b7, 0x0082be, 0x0082bf, 0x0082c6, 0x0082d5, - 0x0082fd, 0x0082fe, 0x008300, 0x008301, 0x008362, 0x008322, 0x00832d, 0x00833a, - 0x008343, 0x008347, 0x008351, 0x008355, 0x00837d, 0x008386, 0x008392, 0x008398, - 0x0083a7, 0x0083a9, 0x0083bf, 0x0083c0, 0x0083c7, 0x0083cf, - // plane 1 row 91 - 0x0083d1, 0x0083e1, 0x0083ea, 0x008401, 0x008406, 0x00840a, 0x00fa5f, 0x008448, - 0x00845f, 0x008470, 0x008473, 0x008485, 0x00849e, 0x0084af, 0x0084b4, 0x0084ba, - 0x0084c0, 0x0084c2, 0x026e40, 0x008532, 0x00851e, 0x008523, 0x00852f, 0x008559, - 0x008564, 0x00fa1f, 0x0085ad, 0x00857a, 0x00858c, 0x00858f, 0x0085a2, 0x0085b0, - 0x0085cb, 0x0085ce, 0x0085ed, 0x008612, 0x0085ff, 0x008604, 0x008605, 0x008610, - 0x0270f4, 0x008618, 0x008629, 0x008638, 0x008657, 0x00865b, 0x00f936, 0x008662, - 0x00459d, 0x00866c, 0x008675, 0x008698, 0x0086b8, 0x0086fa, 0x0086fc, 0x0086fd, - 0x00870b, 0x008771, 0x008787, 0x008788, 0x0087ac, 0x0087ad, 0x0087b5, 0x0045ea, - 0x0087d6, 0x0087ec, 0x008806, 0x00880a, 0x008810, 0x008814, 0x00881f, 0x008898, - 0x0088aa, 0x0088ca, 0x0088ce, 0x027684, 0x0088f5, 0x00891c, 0x00fa60, 0x008918, - 0x008919, 0x00891a, 0x008927, 0x008930, 0x008932, 0x008939, 0x008940, 0x008994, - 0x00fa61, 0x0089d4, 0x0089e5, 0x0089f6, 0x008a12, 0x008a15, - // plane 1 row 92 - 0x008a22, 0x008a37, 0x008a47, 0x008a4e, 0x008a5d, 0x008a61, 0x008a75, 0x008a79, - 0x008aa7, 0x008ad0, 0x008adf, 0x008af4, 0x008af6, 0x00fa22, 0x00fa62, 0x00fa63, - 0x008b46, 0x008b54, 0x008b59, 0x008b69, 0x008b9d, 0x008c49, 0x008c68, 0x00fa64, - 0x008ce1, 0x008cf4, 0x008cf8, 0x008cfe, 0x00fa65, 0x008d12, 0x008d1b, 0x008daf, - 0x008dce, 0x008dd1, 0x008dd7, 0x008e20, 0x008e23, 0x008e3d, 0x008e70, 0x008e7b, - 0x028277, 0x008ec0, 0x004844, 0x008efa, 0x008f1e, 0x008f2d, 0x008f36, 0x008f54, - 0x0283cd, 0x008fa6, 0x008fb5, 0x008fe4, 0x008fe8, 0x008fee, 0x009008, 0x00902d, - 0x00fa67, 0x009088, 0x009095, 0x009097, 0x009099, 0x00909b, 0x0090a2, 0x0090b3, - 0x0090be, 0x0090c4, 0x0090c5, 0x0090c7, 0x0090d7, 0x0090dd, 0x0090de, 0x0090ef, - 0x0090f4, 0x00fa26, 0x009114, 0x009115, 0x009116, 0x009122, 0x009123, 0x009127, - 0x00912f, 0x009131, 0x009134, 0x00913d, 0x009148, 0x00915b, 0x009183, 0x00919e, - 0x0091ac, 0x0091b1, 0x0091bc, 0x0091d7, 0x0091fb, 0x0091e4, - // plane 1 row 93 - 0x0091e5, 0x0091ed, 0x0091f1, 0x009207, 0x009210, 0x009238, 0x009239, 0x00923a, - 0x00923c, 0x009240, 0x009243, 0x00924f, 0x009278, 0x009288, 0x0092c2, 0x0092cb, - 0x0092cc, 0x0092d3, 0x0092e0, 0x0092ff, 0x009304, 0x00931f, 0x009321, 0x009325, - 0x009348, 0x009349, 0x00934a, 0x009364, 0x009365, 0x00936a, 0x009370, 0x00939b, - 0x0093a3, 0x0093ba, 0x0093c6, 0x0093de, 0x0093df, 0x009404, 0x0093fd, 0x009433, - 0x00944a, 0x009463, 0x00946b, 0x009471, 0x009472, 0x00958e, 0x00959f, 0x0095a6, - 0x0095a9, 0x0095ac, 0x0095b6, 0x0095bd, 0x0095cb, 0x0095d0, 0x0095d3, 0x0049b0, - 0x0095da, 0x0095de, 0x009658, 0x009684, 0x00f9dc, 0x00969d, 0x0096a4, 0x0096a5, - 0x0096d2, 0x0096de, 0x00fa68, 0x0096e9, 0x0096ef, 0x009733, 0x00973b, 0x00974d, - 0x00974e, 0x00974f, 0x00975a, 0x00976e, 0x009773, 0x009795, 0x0097ae, 0x0097ba, - 0x0097c1, 0x0097c9, 0x0097de, 0x0097db, 0x0097f4, 0x00fa69, 0x00980a, 0x00981e, - 0x00982b, 0x009830, 0x00fa6a, 0x009852, 0x009853, 0x009856, - // plane 1 row 94 - 0x009857, 0x009859, 0x00985a, 0x00f9d0, 0x009865, 0x00986c, 0x0098ba, 0x0098c8, - 0x0098e7, 0x009958, 0x00999e, 0x009a02, 0x009a03, 0x009a24, 0x009a2d, 0x009a2e, - 0x009a38, 0x009a4a, 0x009a4e, 0x009a52, 0x009ab6, 0x009ac1, 0x009ac3, 0x009ace, - 0x009ad6, 0x009af9, 0x009b02, 0x009b08, 0x009b20, 0x004c17, 0x009b2d, 0x009b5e, - 0x009b79, 0x009b66, 0x009b72, 0x009b75, 0x009b84, 0x009b8a, 0x009b8f, 0x009b9e, - 0x009ba7, 0x009bc1, 0x009bce, 0x009be5, 0x009bf8, 0x009bfd, 0x009c00, 0x009c23, - 0x009c41, 0x009c4f, 0x009c50, 0x009c53, 0x009c63, 0x009c65, 0x009c77, 0x009d1d, - 0x009d1e, 0x009d43, 0x009d47, 0x009d52, 0x009d63, 0x009d70, 0x009d7c, 0x009d8a, - 0x009d96, 0x009dc0, 0x009dac, 0x009dbc, 0x009dd7, 0x02a190, 0x009de7, 0x009e07, - 0x009e15, 0x009e7c, 0x009e9e, 0x009ea4, 0x009eac, 0x009eaf, 0x009eb4, 0x009eb5, - 0x009ec3, 0x009ed1, 0x009f10, 0x009f39, 0x009f57, 0x009f90, 0x009f94, 0x009f97, - 0x009fa2, 0x0059f8, 0x005c5b, 0x005e77, 0x007626, 0x007e6b, - // plane 2 row 1 - 0x020089, 0x004e02, 0x004e0f, 0x004e12, 0x004e29, 0x004e2b, 0x004e2e, 0x004e40, - 0x004e47, 0x004e48, 0x0200a2, 0x004e51, 0x003406, 0x0200a4, 0x004e5a, 0x004e69, - 0x004e9d, 0x00342c, 0x00342e, 0x004eb9, 0x004ebb, 0x0201a2, 0x004ebc, 0x004ec3, - 0x004ec8, 0x004ed0, 0x004eeb, 0x004eda, 0x004ef1, 0x004ef5, 0x004f00, 0x004f16, - 0x004f64, 0x004f37, 0x004f3e, 0x004f54, 0x004f58, 0x020213, 0x004f77, 0x004f78, - 0x004f7a, 0x004f7d, 0x004f82, 0x004f85, 0x004f92, 0x004f9a, 0x004fe6, 0x004fb2, - 0x004fbe, 0x004fc5, 0x004fcb, 0x004fcf, 0x004fd2, 0x00346a, 0x004ff2, 0x005000, - 0x005010, 0x005013, 0x00501c, 0x00501e, 0x005022, 0x003468, 0x005042, 0x005046, - 0x00504e, 0x005053, 0x005057, 0x005063, 0x005066, 0x00506a, 0x005070, 0x0050a3, - 0x005088, 0x005092, 0x005093, 0x005095, 0x005096, 0x00509c, 0x0050aa, 0x02032b, - 0x0050b1, 0x0050ba, 0x0050bb, 0x0050c4, 0x0050c7, 0x0050f3, 0x020381, 0x0050ce, - 0x020371, 0x0050d4, 0x0050d9, 0x0050e1, 0x0050e9, 0x003492, - // plane 2 row 2 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 3 - 0x005108, 0x0203f9, 0x005117, 0x00511b, 0x02044a, 0x005160, 0x020509, 0x005173, - 0x005183, 0x00518b, 0x0034bc, 0x005198, 0x0051a3, 0x0051ad, 0x0034c7, 0x0051bc, - 0x0205d6, 0x020628, 0x0051f3, 0x0051f4, 0x005202, 0x005212, 0x005216, 0x02074f, - 0x005255, 0x00525c, 0x00526c, 0x005277, 0x005284, 0x005282, 0x020807, 0x005298, - 0x02083a, 0x0052a4, 0x0052a6, 0x0052af, 0x0052ba, 0x0052bb, 0x0052ca, 0x00351f, - 0x0052d1, 0x0208b9, 0x0052f7, 0x00530a, 0x00530b, 0x005324, 0x005335, 0x00533e, - 0x005342, 0x02097c, 0x02099d, 0x005367, 0x00536c, 0x00537a, 0x0053a4, 0x0053b4, - 0x020ad3, 0x0053b7, 0x0053c0, 0x020b1d, 0x00355d, 0x00355e, 0x0053d5, 0x0053da, - 0x003563, 0x0053f4, 0x0053f5, 0x005455, 0x005424, 0x005428, 0x00356e, 0x005443, - 0x005462, 0x005466, 0x00546c, 0x00548a, 0x00548d, 0x005495, 0x0054a0, 0x0054a6, - 0x0054ad, 0x0054ae, 0x0054b7, 0x0054ba, 0x0054bf, 0x0054c3, 0x020d45, 0x0054ec, - 0x0054ef, 0x0054f1, 0x0054f3, 0x005500, 0x005501, 0x005509, - // plane 2 row 4 - 0x00553c, 0x005541, 0x0035a6, 0x005547, 0x00554a, 0x0035a8, 0x005560, 0x005561, - 0x005564, 0x020de1, 0x00557d, 0x005582, 0x005588, 0x005591, 0x0035c5, 0x0055d2, - 0x020e95, 0x020e6d, 0x0055bf, 0x0055c9, 0x0055cc, 0x0055d1, 0x0055dd, 0x0035da, - 0x0055e2, 0x020e64, 0x0055e9, 0x005628, 0x020f5f, 0x005607, 0x005610, 0x005630, - 0x005637, 0x0035f4, 0x00563d, 0x00563f, 0x005640, 0x005647, 0x00565e, 0x005660, - 0x00566d, 0x003605, 0x005688, 0x00568c, 0x005695, 0x00569a, 0x00569d, 0x0056a8, - 0x0056ad, 0x0056b2, 0x0056c5, 0x0056cd, 0x0056df, 0x0056e8, 0x0056f6, 0x0056f7, - 0x021201, 0x005715, 0x005723, 0x021255, 0x005729, 0x02127b, 0x005745, 0x005746, - 0x00574c, 0x00574d, 0x021274, 0x005768, 0x00576f, 0x005773, 0x005774, 0x005775, - 0x00577b, 0x0212e4, 0x0212d7, 0x0057ac, 0x00579a, 0x00579d, 0x00579e, 0x0057a8, - 0x0057d7, 0x0212fd, 0x0057cc, 0x021336, 0x021344, 0x0057de, 0x0057e6, 0x0057f0, - 0x00364a, 0x0057f8, 0x0057fb, 0x0057fd, 0x005804, 0x00581e, - // plane 2 row 5 - 0x005820, 0x005827, 0x005832, 0x005839, 0x0213c4, 0x005849, 0x00584c, 0x005867, - 0x00588a, 0x00588b, 0x00588d, 0x00588f, 0x005890, 0x005894, 0x00589d, 0x0058aa, - 0x0058b1, 0x02146d, 0x0058c3, 0x0058cd, 0x0058e2, 0x0058f3, 0x0058f4, 0x005905, - 0x005906, 0x00590b, 0x00590d, 0x005914, 0x005924, 0x0215d7, 0x003691, 0x00593d, - 0x003699, 0x005946, 0x003696, 0x026c29, 0x00595b, 0x00595f, 0x021647, 0x005975, - 0x005976, 0x00597c, 0x00599f, 0x0059ae, 0x0059bc, 0x0059c8, 0x0059cd, 0x0059de, - 0x0059e3, 0x0059e4, 0x0059e7, 0x0059ee, 0x021706, 0x021742, 0x0036cf, 0x005a0c, - 0x005a0d, 0x005a17, 0x005a27, 0x005a2d, 0x005a55, 0x005a65, 0x005a7a, 0x005a8b, - 0x005a9c, 0x005a9f, 0x005aa0, 0x005aa2, 0x005ab1, 0x005ab3, 0x005ab5, 0x005aba, - 0x005abf, 0x005ada, 0x005adc, 0x005ae0, 0x005ae5, 0x005af0, 0x005aee, 0x005af5, - 0x005b00, 0x005b08, 0x005b17, 0x005b34, 0x005b2d, 0x005b4c, 0x005b52, 0x005b68, - 0x005b6f, 0x005b7c, 0x005b7f, 0x005b81, 0x005b84, 0x0219c3, - // plane 2 row 6 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 7 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 8 - 0x005b96, 0x005bac, 0x003761, 0x005bc0, 0x003762, 0x005bce, 0x005bd6, 0x00376c, - 0x00376b, 0x005bf1, 0x005bfd, 0x003775, 0x005c03, 0x005c29, 0x005c30, 0x021c56, - 0x005c5f, 0x005c63, 0x005c67, 0x005c68, 0x005c69, 0x005c70, 0x021d2d, 0x021d45, - 0x005c7c, 0x021d78, 0x021d62, 0x005c88, 0x005c8a, 0x0037c1, 0x021da1, 0x021d9c, - 0x005ca0, 0x005ca2, 0x005ca6, 0x005ca7, 0x021d92, 0x005cad, 0x005cb5, 0x021db7, - 0x005cc9, 0x021de0, 0x021e33, 0x005d06, 0x005d10, 0x005d2b, 0x005d1d, 0x005d20, - 0x005d24, 0x005d26, 0x005d31, 0x005d39, 0x005d42, 0x0037e8, 0x005d61, 0x005d6a, - 0x0037f4, 0x005d70, 0x021f1e, 0x0037fd, 0x005d88, 0x003800, 0x005d92, 0x005d94, - 0x005d97, 0x005d99, 0x005db0, 0x005db2, 0x005db4, 0x021f76, 0x005db9, 0x005dd1, - 0x005dd7, 0x005dd8, 0x005de0, 0x021ffa, 0x005de4, 0x005de9, 0x00382f, 0x005e00, - 0x003836, 0x005e12, 0x005e15, 0x003840, 0x005e1f, 0x005e2e, 0x005e3e, 0x005e49, - 0x00385c, 0x005e56, 0x003861, 0x005e6b, 0x005e6c, 0x005e6d, - // plane 2 row 9 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 10 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 11 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 12 - 0x005e6e, 0x02217b, 0x005ea5, 0x005eaa, 0x005eac, 0x005eb9, 0x005ebf, 0x005ec6, - 0x005ed2, 0x005ed9, 0x02231e, 0x005efd, 0x005f08, 0x005f0e, 0x005f1c, 0x0223ad, - 0x005f1e, 0x005f47, 0x005f63, 0x005f72, 0x005f7e, 0x005f8f, 0x005fa2, 0x005fa4, - 0x005fb8, 0x005fc4, 0x0038fa, 0x005fc7, 0x005fcb, 0x005fd2, 0x005fd3, 0x005fd4, - 0x005fe2, 0x005fee, 0x005fef, 0x005ff3, 0x005ffc, 0x003917, 0x006017, 0x006022, - 0x006024, 0x00391a, 0x00604c, 0x00607f, 0x00608a, 0x006095, 0x0060a8, 0x0226f3, - 0x0060b0, 0x0060b1, 0x0060be, 0x0060c8, 0x0060d9, 0x0060db, 0x0060ee, 0x0060f2, - 0x0060f5, 0x006110, 0x006112, 0x006113, 0x006119, 0x00611e, 0x00613a, 0x00396f, - 0x006141, 0x006146, 0x006160, 0x00617c, 0x02285b, 0x006192, 0x006193, 0x006197, - 0x006198, 0x0061a5, 0x0061a8, 0x0061ad, 0x0228ab, 0x0061d5, 0x0061dd, 0x0061df, - 0x0061f5, 0x02298f, 0x006215, 0x006223, 0x006229, 0x006246, 0x00624c, 0x006251, - 0x006252, 0x006261, 0x006264, 0x00627b, 0x00626d, 0x006273, - // plane 2 row 13 - 0x006299, 0x0062a6, 0x0062d5, 0x022ab8, 0x0062fd, 0x006303, 0x00630d, 0x006310, - 0x022b4f, 0x022b50, 0x006332, 0x006335, 0x00633b, 0x00633c, 0x006341, 0x006344, - 0x00634e, 0x022b46, 0x006359, 0x022c1d, 0x022ba6, 0x00636c, 0x006384, 0x006399, - 0x022c24, 0x006394, 0x0063bd, 0x0063f7, 0x0063d4, 0x0063d5, 0x0063dc, 0x0063e0, - 0x0063eb, 0x0063ec, 0x0063f2, 0x006409, 0x00641e, 0x006425, 0x006429, 0x00642f, - 0x00645a, 0x00645b, 0x00645d, 0x006473, 0x00647d, 0x006487, 0x006491, 0x00649d, - 0x00649f, 0x0064cb, 0x0064cc, 0x0064d5, 0x0064d7, 0x022de1, 0x0064e4, 0x0064e5, - 0x0064ff, 0x006504, 0x003a6e, 0x00650f, 0x006514, 0x006516, 0x003a73, 0x00651e, - 0x006532, 0x006544, 0x006554, 0x00656b, 0x00657a, 0x006581, 0x006584, 0x006585, - 0x00658a, 0x0065b2, 0x0065b5, 0x0065b8, 0x0065bf, 0x0065c2, 0x0065c9, 0x0065d4, - 0x003ad6, 0x0065f2, 0x0065f9, 0x0065fc, 0x006604, 0x006608, 0x006621, 0x00662a, - 0x006645, 0x006651, 0x00664e, 0x003aea, 0x0231c3, 0x006657, - // plane 2 row 14 - 0x00665b, 0x006663, 0x0231f5, 0x0231b6, 0x00666a, 0x00666b, 0x00666c, 0x00666d, - 0x00667b, 0x006680, 0x006690, 0x006692, 0x006699, 0x003b0e, 0x0066ad, 0x0066b1, - 0x0066b5, 0x003b1a, 0x0066bf, 0x003b1c, 0x0066ec, 0x003ad7, 0x006701, 0x006705, - 0x006712, 0x023372, 0x006719, 0x0233d3, 0x0233d2, 0x00674c, 0x00674d, 0x006754, - 0x00675d, 0x0233d0, 0x0233e4, 0x0233d5, 0x006774, 0x006776, 0x0233da, 0x006792, - 0x0233df, 0x008363, 0x006810, 0x0067b0, 0x0067b2, 0x0067c3, 0x0067c8, 0x0067d2, - 0x0067d9, 0x0067db, 0x0067f0, 0x0067f7, 0x02344a, 0x023451, 0x02344b, 0x006818, - 0x00681f, 0x00682d, 0x023465, 0x006833, 0x00683b, 0x00683e, 0x006844, 0x006845, - 0x006849, 0x00684c, 0x006855, 0x006857, 0x003b77, 0x00686b, 0x00686e, 0x00687a, - 0x00687c, 0x006882, 0x006890, 0x006896, 0x003b6d, 0x006898, 0x006899, 0x00689a, - 0x00689c, 0x0068aa, 0x0068ab, 0x0068b4, 0x0068bb, 0x0068fb, 0x0234e4, 0x02355a, - 0x00fa13, 0x0068c3, 0x0068c5, 0x0068cc, 0x0068cf, 0x0068d6, - // plane 2 row 15 - 0x0068d9, 0x0068e4, 0x0068e5, 0x0068ec, 0x0068f7, 0x006903, 0x006907, 0x003b87, - 0x003b88, 0x023594, 0x00693b, 0x003b8d, 0x006946, 0x006969, 0x00696c, 0x006972, - 0x00697a, 0x00697f, 0x006992, 0x003ba4, 0x006996, 0x006998, 0x0069a6, 0x0069b0, - 0x0069b7, 0x0069ba, 0x0069bc, 0x0069c0, 0x0069d1, 0x0069d6, 0x023639, 0x023647, - 0x006a30, 0x023638, 0x02363a, 0x0069e3, 0x0069ee, 0x0069ef, 0x0069f3, 0x003bcd, - 0x0069f4, 0x0069fe, 0x006a11, 0x006a1a, 0x006a1d, 0x02371c, 0x006a32, 0x006a33, - 0x006a34, 0x006a3f, 0x006a46, 0x006a49, 0x006a7a, 0x006a4e, 0x006a52, 0x006a64, - 0x02370c, 0x006a7e, 0x006a83, 0x006a8b, 0x003bf0, 0x006a91, 0x006a9f, 0x006aa1, - 0x023764, 0x006aab, 0x006abd, 0x006ac6, 0x006ad4, 0x006ad0, 0x006adc, 0x006add, - 0x0237ff, 0x0237e7, 0x006aec, 0x006af1, 0x006af2, 0x006af3, 0x006afd, 0x023824, - 0x006b0b, 0x006b0f, 0x006b10, 0x006b11, 0x02383d, 0x006b17, 0x003c26, 0x006b2f, - 0x006b4a, 0x006b58, 0x006b6c, 0x006b75, 0x006b7a, 0x006b81, - // plane 2 row 16 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 17 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 18 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 19 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 20 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 21 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 22 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 23 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 24 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 25 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 26 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 27 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 28 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 29 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 30 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 31 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 32 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 33 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 34 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 35 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 36 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 37 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 38 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 39 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 40 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 41 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 42 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 43 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 44 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 45 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 46 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 47 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 48 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 49 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 50 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 51 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 52 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 53 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 54 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 55 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 56 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 57 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 58 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 59 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 60 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 61 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 62 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 63 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 64 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 65 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 66 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 67 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 68 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 69 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 70 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 71 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 72 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 73 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 74 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 75 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 76 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 77 - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, - // plane 2 row 78 - 0x006b9b, 0x006bae, 0x023a98, 0x006bbd, 0x006bbe, 0x006bc7, 0x006bc8, 0x006bc9, - 0x006bda, 0x006be6, 0x006be7, 0x006bee, 0x006bf1, 0x006c02, 0x006c0a, 0x006c0e, - 0x006c35, 0x006c36, 0x006c3a, 0x023c7f, 0x006c3f, 0x006c4d, 0x006c5b, 0x006c6d, - 0x006c84, 0x006c89, 0x003cc3, 0x006c94, 0x006c95, 0x006c97, 0x006cad, 0x006cc2, - 0x006cd0, 0x003cd2, 0x006cd6, 0x006cda, 0x006cdc, 0x006ce9, 0x006cec, 0x006ced, - 0x023d00, 0x006d00, 0x006d0a, 0x006d24, 0x006d26, 0x006d27, 0x006c67, 0x006d2f, - 0x006d3c, 0x006d5b, 0x006d5e, 0x006d60, 0x006d70, 0x006d80, 0x006d81, 0x006d8a, - 0x006d8d, 0x006d91, 0x006d98, 0x023d40, 0x006e17, 0x023dfa, 0x023df9, 0x023dd3, - 0x006dab, 0x006dae, 0x006db4, 0x006dc2, 0x006d34, 0x006dc8, 0x006dce, 0x006dcf, - 0x006dd0, 0x006ddf, 0x006de9, 0x006df6, 0x006e36, 0x006e1e, 0x006e22, 0x006e27, - 0x003d11, 0x006e32, 0x006e3c, 0x006e48, 0x006e49, 0x006e4b, 0x006e4c, 0x006e4f, - 0x006e51, 0x006e53, 0x006e54, 0x006e57, 0x006e63, 0x003d1e, - // plane 2 row 79 - 0x006e93, 0x006ea7, 0x006eb4, 0x006ebf, 0x006ec3, 0x006eca, 0x006ed9, 0x006f35, - 0x006eeb, 0x006ef9, 0x006efb, 0x006f0a, 0x006f0c, 0x006f18, 0x006f25, 0x006f36, - 0x006f3c, 0x023f7e, 0x006f52, 0x006f57, 0x006f5a, 0x006f60, 0x006f68, 0x006f98, - 0x006f7d, 0x006f90, 0x006f96, 0x006fbe, 0x006f9f, 0x006fa5, 0x006faf, 0x003d64, - 0x006fb5, 0x006fc8, 0x006fc9, 0x006fda, 0x006fde, 0x006fe9, 0x024096, 0x006ffc, - 0x007000, 0x007007, 0x00700a, 0x007023, 0x024103, 0x007039, 0x00703a, 0x00703c, - 0x007043, 0x007047, 0x00704b, 0x003d9a, 0x007054, 0x007065, 0x007069, 0x00706c, - 0x00706e, 0x007076, 0x00707e, 0x007081, 0x007086, 0x007095, 0x007097, 0x0070bb, - 0x0241c6, 0x00709f, 0x0070b1, 0x0241fe, 0x0070ec, 0x0070ca, 0x0070d1, 0x0070d3, - 0x0070dc, 0x007103, 0x007104, 0x007106, 0x007107, 0x007108, 0x00710c, 0x003dc0, - 0x00712f, 0x007131, 0x007150, 0x00714a, 0x007153, 0x00715e, 0x003dd4, 0x007196, - 0x007180, 0x00719b, 0x0071a0, 0x0071a2, 0x0071ae, 0x0071af, - // plane 2 row 80 - 0x0071b3, 0x0243bc, 0x0071cb, 0x0071d3, 0x0071d9, 0x0071dc, 0x007207, 0x003e05, - 0x00fa49, 0x00722b, 0x007234, 0x007238, 0x007239, 0x004e2c, 0x007242, 0x007253, - 0x007257, 0x007263, 0x024629, 0x00726e, 0x00726f, 0x007278, 0x00727f, 0x00728e, - 0x0246a5, 0x0072ad, 0x0072ae, 0x0072b0, 0x0072b1, 0x0072c1, 0x003e60, 0x0072cc, - 0x003e66, 0x003e68, 0x0072f3, 0x0072fa, 0x007307, 0x007312, 0x007318, 0x007319, - 0x003e83, 0x007339, 0x00732c, 0x007331, 0x007333, 0x00733d, 0x007352, 0x003e94, - 0x00736b, 0x00736c, 0x024896, 0x00736e, 0x00736f, 0x007371, 0x007377, 0x007381, - 0x007385, 0x00738a, 0x007394, 0x007398, 0x00739c, 0x00739e, 0x0073a5, 0x0073a8, - 0x0073b5, 0x0073b7, 0x0073b9, 0x0073bc, 0x0073bf, 0x0073c5, 0x0073cb, 0x0073e1, - 0x0073e7, 0x0073f9, 0x007413, 0x0073fa, 0x007401, 0x007424, 0x007431, 0x007439, - 0x007453, 0x007440, 0x007443, 0x00744d, 0x007452, 0x00745d, 0x007471, 0x007481, - 0x007485, 0x007488, 0x024a4d, 0x007492, 0x007497, 0x007499, - // plane 2 row 81 - 0x0074a0, 0x0074a1, 0x0074a5, 0x0074aa, 0x0074ab, 0x0074b9, 0x0074bb, 0x0074ba, - 0x0074d6, 0x0074d8, 0x0074de, 0x0074ef, 0x0074eb, 0x024b56, 0x0074fa, 0x024b6f, - 0x007520, 0x007524, 0x00752a, 0x003f57, 0x024c16, 0x00753d, 0x00753e, 0x007540, - 0x007548, 0x00754e, 0x007550, 0x007552, 0x00756c, 0x007572, 0x007571, 0x00757a, - 0x00757d, 0x00757e, 0x007581, 0x024d14, 0x00758c, 0x003f75, 0x0075a2, 0x003f77, - 0x0075b0, 0x0075b7, 0x0075bf, 0x0075c0, 0x0075c6, 0x0075cf, 0x0075d3, 0x0075dd, - 0x0075df, 0x0075e0, 0x0075e7, 0x0075ec, 0x0075ee, 0x0075f1, 0x0075f9, 0x007603, - 0x007618, 0x007607, 0x00760f, 0x003fae, 0x024e0e, 0x007613, 0x00761b, 0x00761c, - 0x024e37, 0x007625, 0x007628, 0x00763c, 0x007633, 0x024e6a, 0x003fc9, 0x007641, - 0x024e8b, 0x007649, 0x007655, 0x003fd7, 0x00766e, 0x007695, 0x00769c, 0x0076a1, - 0x0076a0, 0x0076a7, 0x0076a8, 0x0076af, 0x02504a, 0x0076c9, 0x025055, 0x0076e8, - 0x0076ec, 0x025122, 0x007717, 0x00771a, 0x00772d, 0x007735, - // plane 2 row 82 - 0x0251a9, 0x004039, 0x0251e5, 0x0251cd, 0x007758, 0x007760, 0x00776a, 0x02521e, - 0x007772, 0x00777c, 0x00777d, 0x02524c, 0x004058, 0x00779a, 0x00779f, 0x0077a2, - 0x0077a4, 0x0077a9, 0x0077de, 0x0077df, 0x0077e4, 0x0077e6, 0x0077ea, 0x0077ec, - 0x004093, 0x0077f0, 0x0077f4, 0x0077fb, 0x02542e, 0x007805, 0x007806, 0x007809, - 0x00780d, 0x007819, 0x007821, 0x00782c, 0x007847, 0x007864, 0x00786a, 0x0254d9, - 0x00788a, 0x007894, 0x0078a4, 0x00789d, 0x00789e, 0x00789f, 0x0078bb, 0x0078c8, - 0x0078cc, 0x0078ce, 0x0078d5, 0x0078e0, 0x0078e1, 0x0078e6, 0x0078f9, 0x0078fa, - 0x0078fb, 0x0078fe, 0x0255a7, 0x007910, 0x00791b, 0x007930, 0x007925, 0x00793b, - 0x00794a, 0x007958, 0x00795b, 0x004105, 0x007967, 0x007972, 0x007994, 0x007995, - 0x007996, 0x00799b, 0x0079a1, 0x0079a9, 0x0079b4, 0x0079bb, 0x0079c2, 0x0079c7, - 0x0079cc, 0x0079cd, 0x0079d6, 0x004148, 0x0257a9, 0x0257b4, 0x00414f, 0x007a0a, - 0x007a11, 0x007a15, 0x007a1b, 0x007a1e, 0x004163, 0x007a2d, - // plane 2 row 83 - 0x007a38, 0x007a47, 0x007a4c, 0x007a56, 0x007a59, 0x007a5c, 0x007a5f, 0x007a60, - 0x007a67, 0x007a6a, 0x007a75, 0x007a78, 0x007a82, 0x007a8a, 0x007a90, 0x007aa3, - 0x007aac, 0x0259d4, 0x0041b4, 0x007ab9, 0x007abc, 0x007abe, 0x0041bf, 0x007acc, - 0x007ad1, 0x007ae7, 0x007ae8, 0x007af4, 0x025ae4, 0x025ae3, 0x007b07, 0x025af1, - 0x007b3d, 0x007b27, 0x007b2a, 0x007b2e, 0x007b2f, 0x007b31, 0x0041e6, 0x0041f3, - 0x007b7f, 0x007b41, 0x0041ee, 0x007b55, 0x007b79, 0x007b64, 0x007b66, 0x007b69, - 0x007b73, 0x025bb2, 0x004207, 0x007b90, 0x007b91, 0x007b9b, 0x00420e, 0x007baf, - 0x007bb5, 0x007bbc, 0x007bc5, 0x007bca, 0x025c4b, 0x025c64, 0x007bd4, 0x007bd6, - 0x007bda, 0x007bea, 0x007bf0, 0x007c03, 0x007c0b, 0x007c0e, 0x007c0f, 0x007c26, - 0x007c45, 0x007c4a, 0x007c51, 0x007c57, 0x007c5e, 0x007c61, 0x007c69, 0x007c6e, - 0x007c6f, 0x007c70, 0x025e2e, 0x025e56, 0x025e65, 0x007ca6, 0x025e62, 0x007cb6, - 0x007cb7, 0x007cbf, 0x025ed8, 0x007cc4, 0x025ec2, 0x007cc8, - // plane 2 row 84 - 0x007ccd, 0x025ee8, 0x007cd7, 0x025f23, 0x007ce6, 0x007ceb, 0x025f5c, 0x007cf5, - 0x007d03, 0x007d09, 0x0042c6, 0x007d12, 0x007d1e, 0x025fe0, 0x025fd4, 0x007d3d, - 0x007d3e, 0x007d40, 0x007d47, 0x02600c, 0x025ffb, 0x0042d6, 0x007d59, 0x007d5a, - 0x007d6a, 0x007d70, 0x0042dd, 0x007d7f, 0x026017, 0x007d86, 0x007d88, 0x007d8c, - 0x007d97, 0x026060, 0x007d9d, 0x007da7, 0x007daa, 0x007db6, 0x007db7, 0x007dc0, - 0x007dd7, 0x007dd9, 0x007de6, 0x007df1, 0x007df9, 0x004302, 0x0260ed, 0x00fa58, - 0x007e10, 0x007e17, 0x007e1d, 0x007e20, 0x007e27, 0x007e2c, 0x007e45, 0x007e73, - 0x007e75, 0x007e7e, 0x007e86, 0x007e87, 0x00432b, 0x007e91, 0x007e98, 0x007e9a, - 0x004343, 0x007f3c, 0x007f3b, 0x007f3e, 0x007f43, 0x007f44, 0x007f4f, 0x0034c1, - 0x026270, 0x007f52, 0x026286, 0x007f61, 0x007f63, 0x007f64, 0x007f6d, 0x007f7d, - 0x007f7e, 0x02634c, 0x007f90, 0x00517b, 0x023d0e, 0x007f96, 0x007f9c, 0x007fad, - 0x026402, 0x007fc3, 0x007fcf, 0x007fe3, 0x007fe5, 0x007fef, - // plane 2 row 85 - 0x007ff2, 0x008002, 0x00800a, 0x008008, 0x00800e, 0x008011, 0x008016, 0x008024, - 0x00802c, 0x008030, 0x008043, 0x008066, 0x008071, 0x008075, 0x00807b, 0x008099, - 0x00809c, 0x0080a4, 0x0080a7, 0x0080b8, 0x02667e, 0x0080c5, 0x0080d5, 0x0080d8, - 0x0080e6, 0x0266b0, 0x00810d, 0x0080f5, 0x0080fb, 0x0043ee, 0x008135, 0x008116, - 0x00811e, 0x0043f0, 0x008124, 0x008127, 0x00812c, 0x02671d, 0x00813d, 0x004408, - 0x008169, 0x004417, 0x008181, 0x00441c, 0x008184, 0x008185, 0x004422, 0x008198, - 0x0081b2, 0x0081c1, 0x0081c3, 0x0081d6, 0x0081db, 0x0268dd, 0x0081e4, 0x0268ea, - 0x0081ec, 0x026951, 0x0081fd, 0x0081ff, 0x02696f, 0x008204, 0x0269dd, 0x008219, - 0x008221, 0x008222, 0x026a1e, 0x008232, 0x008234, 0x00823c, 0x008246, 0x008249, - 0x008245, 0x026a58, 0x00824b, 0x004476, 0x00824f, 0x00447a, 0x008257, 0x026a8c, - 0x00825c, 0x008263, 0x026ab7, 0x00fa5d, 0x00fa5e, 0x008279, 0x004491, 0x00827d, - 0x00827f, 0x008283, 0x00828a, 0x008293, 0x0082a7, 0x0082a8, - // plane 2 row 86 - 0x0082b2, 0x0082b4, 0x0082ba, 0x0082bc, 0x0082e2, 0x0082e8, 0x0082f7, 0x008307, - 0x008308, 0x00830c, 0x008354, 0x00831b, 0x00831d, 0x008330, 0x00833c, 0x008344, - 0x008357, 0x0044be, 0x00837f, 0x0044d4, 0x0044b3, 0x00838d, 0x008394, 0x008395, - 0x00839b, 0x00839d, 0x0083c9, 0x0083d0, 0x0083d4, 0x0083dd, 0x0083e5, 0x0083f9, - 0x00840f, 0x008411, 0x008415, 0x026c73, 0x008417, 0x008439, 0x00844a, 0x00844f, - 0x008451, 0x008452, 0x008459, 0x00845a, 0x00845c, 0x026cdd, 0x008465, 0x008476, - 0x008478, 0x00847c, 0x008481, 0x00450d, 0x0084dc, 0x008497, 0x0084a6, 0x0084be, - 0x004508, 0x0084ce, 0x0084cf, 0x0084d3, 0x026e65, 0x0084e7, 0x0084ea, 0x0084ef, - 0x0084f0, 0x0084f1, 0x0084fa, 0x0084fd, 0x00850c, 0x00851b, 0x008524, 0x008525, - 0x00852b, 0x008534, 0x00854f, 0x00856f, 0x004525, 0x004543, 0x00853e, 0x008551, - 0x008553, 0x00855e, 0x008561, 0x008562, 0x026f94, 0x00857b, 0x00857d, 0x00857f, - 0x008581, 0x008586, 0x008593, 0x00859d, 0x00859f, 0x026ff8, - // plane 2 row 87 - 0x026ff6, 0x026ff7, 0x0085b7, 0x0085bc, 0x0085c7, 0x0085ca, 0x0085d8, 0x0085d9, - 0x0085df, 0x0085e1, 0x0085e6, 0x0085f6, 0x008600, 0x008611, 0x00861e, 0x008621, - 0x008624, 0x008627, 0x02710d, 0x008639, 0x00863c, 0x027139, 0x008640, 0x00fa20, - 0x008653, 0x008656, 0x00866f, 0x008677, 0x00867a, 0x008687, 0x008689, 0x00868d, - 0x008691, 0x00869c, 0x00869d, 0x0086a8, 0x00fa21, 0x0086b1, 0x0086b3, 0x0086c1, - 0x0086c3, 0x0086d1, 0x0086d5, 0x0086d7, 0x0086e3, 0x0086e6, 0x0045b8, 0x008705, - 0x008707, 0x00870e, 0x008710, 0x008713, 0x008719, 0x00871f, 0x008721, 0x008723, - 0x008731, 0x00873a, 0x00873e, 0x008740, 0x008743, 0x008751, 0x008758, 0x008764, - 0x008765, 0x008772, 0x00877c, 0x0273db, 0x0273da, 0x0087a7, 0x008789, 0x00878b, - 0x008793, 0x0087a0, 0x0273fe, 0x0045e5, 0x0087be, 0x027410, 0x0087c1, 0x0087ce, - 0x0087f5, 0x0087df, 0x027449, 0x0087e3, 0x0087e5, 0x0087e6, 0x0087ea, 0x0087eb, - 0x0087ed, 0x008801, 0x008803, 0x00880b, 0x008813, 0x008828, - // plane 2 row 88 - 0x00882e, 0x008832, 0x00883c, 0x00460f, 0x00884a, 0x008858, 0x00885f, 0x008864, - 0x027615, 0x027614, 0x008869, 0x027631, 0x00886f, 0x0088a0, 0x0088bc, 0x0088bd, - 0x0088be, 0x0088c0, 0x0088d2, 0x027693, 0x0088d1, 0x0088d3, 0x0088db, 0x0088f0, - 0x0088f1, 0x004641, 0x008901, 0x02770e, 0x008937, 0x027723, 0x008942, 0x008945, - 0x008949, 0x027752, 0x004665, 0x008962, 0x008980, 0x008989, 0x008990, 0x00899f, - 0x0089b0, 0x0089b7, 0x0089d6, 0x0089d8, 0x0089eb, 0x0046a1, 0x0089f1, 0x0089f3, - 0x0089fd, 0x0089ff, 0x0046af, 0x008a11, 0x008a14, 0x027985, 0x008a21, 0x008a35, - 0x008a3e, 0x008a45, 0x008a4d, 0x008a58, 0x008aae, 0x008a90, 0x008ab7, 0x008abe, - 0x008ad7, 0x008afc, 0x027a84, 0x008b0a, 0x008b05, 0x008b0d, 0x008b1c, 0x008b1f, - 0x008b2d, 0x008b43, 0x00470c, 0x008b51, 0x008b5e, 0x008b76, 0x008b7f, 0x008b81, - 0x008b8b, 0x008b94, 0x008b95, 0x008b9c, 0x008b9e, 0x008c39, 0x027bb3, 0x008c3d, - 0x027bbe, 0x027bc7, 0x008c45, 0x008c47, 0x008c4f, 0x008c54, - // plane 2 row 89 - 0x008c57, 0x008c69, 0x008c6d, 0x008c73, 0x027cb8, 0x008c93, 0x008c92, 0x008c99, - 0x004764, 0x008c9b, 0x008ca4, 0x008cd6, 0x008cd5, 0x008cd9, 0x027da0, 0x008cf0, - 0x008cf1, 0x027e10, 0x008d09, 0x008d0e, 0x008d6c, 0x008d84, 0x008d95, 0x008da6, - 0x027fb7, 0x008dc6, 0x008dc8, 0x008dd9, 0x008dec, 0x008e0c, 0x0047fd, 0x008dfd, - 0x008e06, 0x02808a, 0x008e14, 0x008e16, 0x008e21, 0x008e22, 0x008e27, 0x0280bb, - 0x004816, 0x008e36, 0x008e39, 0x008e4b, 0x008e54, 0x008e62, 0x008e6c, 0x008e6d, - 0x008e6f, 0x008e98, 0x008e9e, 0x008eae, 0x008eb3, 0x008eb5, 0x008eb6, 0x008ebb, - 0x028282, 0x008ed1, 0x008ed4, 0x00484e, 0x008ef9, 0x0282f3, 0x008f00, 0x008f08, - 0x008f17, 0x008f2b, 0x008f40, 0x008f4a, 0x008f58, 0x02840c, 0x008fa4, 0x008fb4, - 0x00fa66, 0x008fb6, 0x028455, 0x008fc1, 0x008fc6, 0x00fa24, 0x008fca, 0x008fcd, - 0x008fd3, 0x008fd5, 0x008fe0, 0x008ff1, 0x008ff5, 0x008ffb, 0x009002, 0x00900c, - 0x009037, 0x02856b, 0x009043, 0x009044, 0x00905d, 0x0285c8, - // plane 2 row 90 - 0x0285c9, 0x009085, 0x00908c, 0x009090, 0x00961d, 0x0090a1, 0x0048b5, 0x0090b0, - 0x0090b6, 0x0090c3, 0x0090c8, 0x0286d7, 0x0090dc, 0x0090df, 0x0286fa, 0x0090f6, - 0x0090f2, 0x009100, 0x0090eb, 0x0090fe, 0x0090ff, 0x009104, 0x009106, 0x009118, - 0x00911c, 0x00911e, 0x009137, 0x009139, 0x00913a, 0x009146, 0x009147, 0x009157, - 0x009159, 0x009161, 0x009164, 0x009174, 0x009179, 0x009185, 0x00918e, 0x0091a8, - 0x0091ae, 0x0091b3, 0x0091b6, 0x0091c3, 0x0091c4, 0x0091da, 0x028949, 0x028946, - 0x0091ec, 0x0091ee, 0x009201, 0x00920a, 0x009216, 0x009217, 0x02896b, 0x009233, - 0x009242, 0x009247, 0x00924a, 0x00924e, 0x009251, 0x009256, 0x009259, 0x009260, - 0x009261, 0x009265, 0x009267, 0x009268, 0x028987, 0x028988, 0x00927c, 0x00927d, - 0x00927f, 0x009289, 0x00928d, 0x009297, 0x009299, 0x00929f, 0x0092a7, 0x0092ab, - 0x0289ba, 0x0289bb, 0x0092b2, 0x0092bf, 0x0092c0, 0x0092c6, 0x0092ce, 0x0092d0, - 0x0092d7, 0x0092d9, 0x0092e5, 0x0092e7, 0x009311, 0x028a1e, - // plane 2 row 91 - 0x028a29, 0x0092f7, 0x0092f9, 0x0092fb, 0x009302, 0x00930d, 0x009315, 0x00931d, - 0x00931e, 0x009327, 0x009329, 0x028a71, 0x028a43, 0x009347, 0x009351, 0x009357, - 0x00935a, 0x00936b, 0x009371, 0x009373, 0x0093a1, 0x028a99, 0x028acd, 0x009388, - 0x00938b, 0x00938f, 0x00939e, 0x0093f5, 0x028ae4, 0x028add, 0x0093f1, 0x0093c1, - 0x0093c7, 0x0093dc, 0x0093e2, 0x0093e7, 0x009409, 0x00940f, 0x009416, 0x009417, - 0x0093fb, 0x009432, 0x009434, 0x00943b, 0x009445, 0x028bc1, 0x028bef, 0x00946d, - 0x00946f, 0x009578, 0x009579, 0x009586, 0x00958c, 0x00958d, 0x028d10, 0x0095ab, - 0x0095b4, 0x028d71, 0x0095c8, 0x028dfb, 0x028e1f, 0x00962c, 0x009633, 0x009634, - 0x028e36, 0x00963c, 0x009641, 0x009661, 0x028e89, 0x009682, 0x028eeb, 0x00969a, - 0x028f32, 0x0049e7, 0x0096a9, 0x0096af, 0x0096b3, 0x0096ba, 0x0096bd, 0x0049fa, - 0x028ff8, 0x0096d8, 0x0096da, 0x0096dd, 0x004a04, 0x009714, 0x009723, 0x004a29, - 0x009736, 0x009741, 0x009747, 0x009755, 0x009757, 0x00975b, - // plane 2 row 92 - 0x00976a, 0x0292a0, 0x0292b1, 0x009796, 0x00979a, 0x00979e, 0x0097a2, 0x0097b1, - 0x0097b2, 0x0097be, 0x0097cc, 0x0097d1, 0x0097d4, 0x0097d8, 0x0097d9, 0x0097e1, - 0x0097f1, 0x009804, 0x00980d, 0x00980e, 0x009814, 0x009816, 0x004abc, 0x029490, - 0x009823, 0x009832, 0x009833, 0x009825, 0x009847, 0x009866, 0x0098ab, 0x0098ad, - 0x0098b0, 0x0295cf, 0x0098b7, 0x0098b8, 0x0098bb, 0x0098bc, 0x0098bf, 0x0098c2, - 0x0098c7, 0x0098cb, 0x0098e0, 0x02967f, 0x0098e1, 0x0098e3, 0x0098e5, 0x0098ea, - 0x0098f0, 0x0098f1, 0x0098f3, 0x009908, 0x004b3b, 0x0296f0, 0x009916, 0x009917, - 0x029719, 0x00991a, 0x00991b, 0x00991c, 0x029750, 0x009931, 0x009932, 0x009933, - 0x00993a, 0x00993b, 0x00993c, 0x009940, 0x009941, 0x009946, 0x00994d, 0x00994e, - 0x00995c, 0x00995f, 0x009960, 0x0099a3, 0x0099a6, 0x0099b9, 0x0099bd, 0x0099bf, - 0x0099c3, 0x0099c9, 0x0099d4, 0x0099d9, 0x0099de, 0x0298c6, 0x0099f0, 0x0099f9, - 0x0099fc, 0x009a0a, 0x009a11, 0x009a16, 0x009a1a, 0x009a20, - // plane 2 row 93 - 0x009a31, 0x009a36, 0x009a44, 0x009a4c, 0x009a58, 0x004bc2, 0x009aaf, 0x004bca, - 0x009ab7, 0x004bd2, 0x009ab9, 0x029a72, 0x009ac6, 0x009ad0, 0x009ad2, 0x009ad5, - 0x004be8, 0x009adc, 0x009ae0, 0x009ae5, 0x009ae9, 0x009b03, 0x009b0c, 0x009b10, - 0x009b12, 0x009b16, 0x009b1c, 0x009b2b, 0x009b33, 0x009b3d, 0x004c20, 0x009b4b, - 0x009b63, 0x009b65, 0x009b6b, 0x009b6c, 0x009b73, 0x009b76, 0x009b77, 0x009ba6, - 0x009bac, 0x009bb1, 0x029ddb, 0x029e3d, 0x009bb2, 0x009bb8, 0x009bbe, 0x009bc7, - 0x009bf3, 0x009bd8, 0x009bdd, 0x009be7, 0x009bea, 0x009beb, 0x009bef, 0x009bee, - 0x029e15, 0x009bfa, 0x029e8a, 0x009bf7, 0x029e49, 0x009c16, 0x009c18, 0x009c19, - 0x009c1a, 0x009c1d, 0x009c22, 0x009c27, 0x009c29, 0x009c2a, 0x029ec4, 0x009c31, - 0x009c36, 0x009c37, 0x009c45, 0x009c5c, 0x029ee9, 0x009c49, 0x009c4a, 0x029edb, - 0x009c54, 0x009c58, 0x009c5b, 0x009c5d, 0x009c5f, 0x009c69, 0x009c6a, 0x009c6b, - 0x009c6d, 0x009c6e, 0x009c70, 0x009c72, 0x009c75, 0x009c7a, - // plane 2 row 94 - 0x009ce6, 0x009cf2, 0x009d0b, 0x009d02, 0x029fce, 0x009d11, 0x009d17, 0x009d18, - 0x02a02f, 0x004cc4, 0x02a01a, 0x009d32, 0x004cd1, 0x009d42, 0x009d4a, 0x009d5f, - 0x009d62, 0x02a0f9, 0x009d69, 0x009d6b, 0x02a082, 0x009d73, 0x009d76, 0x009d77, - 0x009d7e, 0x009d84, 0x009d8d, 0x009d99, 0x009da1, 0x009dbf, 0x009db5, 0x009db9, - 0x009dbd, 0x009dc3, 0x009dc7, 0x009dc9, 0x009dd6, 0x009dda, 0x009ddf, 0x009de0, - 0x009de3, 0x009df4, 0x004d07, 0x009e0a, 0x009e02, 0x009e0d, 0x009e19, 0x009e1c, - 0x009e1d, 0x009e7b, 0x022218, 0x009e80, 0x009e85, 0x009e9b, 0x009ea8, 0x02a38c, - 0x009ebd, 0x02a437, 0x009edf, 0x009ee7, 0x009eee, 0x009eff, 0x009f02, 0x004d77, - 0x009f03, 0x009f17, 0x009f19, 0x009f2f, 0x009f37, 0x009f3a, 0x009f3d, 0x009f41, - 0x009f45, 0x009f46, 0x009f53, 0x009f55, 0x009f58, 0x02a5f1, 0x009f5d, 0x02a602, - 0x009f69, 0x02a61a, 0x009f6d, 0x009f70, 0x009f75, 0x02a6b2, 0x00fffd, 0x00fffd, - 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd, 0x00fffd - }; - - /// Unicode registered symbols - enum UnicodeSymbol { - /// Unrecognized character (Geta character) - UNICODE_BAD_SEQUENCE = 0xfffd - }; - - // EUC-JP decoder - std::string decode_eucjp(std::string_view src) { - unsigned int len = 0; - std::u32string utf32_str; - utf32_str.resize(src.size()); - - // decoding - for (size_t i = 0; i < src.size(); ++i, ++len) { - unsigned char b1, b2, b3; - if (len >= utf32_str.size()) { - utf32_str.resize(utf32_str.size() * 2); - } - b1 = src[i]; - // end of text - if (b1 == 0x00) break; - // 1 byte sequence - if (b1 <= 0x7f) utf32_str[len] = (int)b1; - // 3 bytes sequence (JIS X 0213 plane 2) - else if (b1 == 0x8f) { - if ((i += 2) >= src.size()) break; - b2 = src[i - 1], b3 = src[i]; - if (0xa1 <= b2 && b2 <= 0xfe && 0xa1 <= b3 && b3 <= 0xfe) - utf32_str[len] = Encoding::jisx0213_2_unicode[(b2 - 0xa1 + 94) * 94 + (b3 - 0xa1)]; - // bad sequence - else { - utf32_str[len] = Encoding::UNICODE_BAD_SEQUENCE; - i -= 2; - } - } - // 2 bytes sequence - else /* b1 >= 0x80 */ { - if (++i >= src.size()) break; - b2 = src[i]; - // JIS X 0201 kana - if (b1 == 0x8e) utf32_str[len] = Encoding::jisx0201_2_unicode[b2]; - // JIS X 0213 plane 1 - else if (0xa1 <= b1 && b1 <= 0xfe && 0xa1 <= b2 && b2 <= 0xfe) - utf32_str[len] = Encoding::jisx0213_2_unicode[(b1 - 0xa1) * 94 + (b2 - 0xa1)]; - // bad sequence - else { - utf32_str[len] = Encoding::UNICODE_BAD_SEQUENCE; - --i; - } - } - } - utf32_str.resize(len); - - std::wstring_convert, char32_t> conv; - std::string utf8_str = conv.to_bytes(utf32_str); - - return utf8_str; - } -} diff --git a/test/src/euc-jp.h b/test/src/euc-jp.h deleted file mode 100644 index 9ee246f..0000000 --- a/test/src/euc-jp.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __EUC_JP_H__ -#define __EUC_JP_H__ - -#include -#include - -namespace Encoding { - std::string decode_eucjp(std::string_view src); -} - -#endif diff --git a/test/src/math_routines.cpp b/test/src/math_routines.cpp deleted file mode 100644 index e0f82aa..0000000 --- a/test/src/math_routines.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "../portultra/multilibultra.hpp" -#include "recomp.h" - - -extern "C" void __udivdi3_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu); - uint64_t ret = a / b; - - ctx->r2 = (int32_t)(ret >> 32); - ctx->r3 = (int32_t)(ret >> 0); -} - -extern "C" void __divdi3_recomp(uint8_t * rdram, recomp_context * ctx) { - int64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - int64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu); - int64_t ret = a / b; - - ctx->r2 = (int32_t)(ret >> 32); - ctx->r3 = (int32_t)(ret >> 0); -} - -extern "C" void __umoddi3_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu); - uint64_t ret = a % b; - - ctx->r2 = (int32_t)(ret >> 32); - ctx->r3 = (int32_t)(ret >> 0); -} - -extern "C" void __ull_div_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu); - uint64_t ret = a / b; - - ctx->r2 = (int32_t)(ret >> 32); - ctx->r3 = (int32_t)(ret >> 0); -} - -extern "C" void __ll_div_recomp(uint8_t * rdram, recomp_context * ctx) { - int64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - int64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu); - int64_t ret = a / b; - - ctx->r2 = (int32_t)(ret >> 32); - ctx->r3 = (int32_t)(ret >> 0); -} - -extern "C" void __ll_mul_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu); - uint64_t ret = a * b; - - ctx->r2 = (int32_t)(ret >> 32); - ctx->r3 = (int32_t)(ret >> 0); -} - -extern "C" void __ull_rem_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - uint64_t b = (ctx->r6 << 32) | ((ctx->r7 << 0) & 0xFFFFFFFFu); - uint64_t ret = a % b; - - ctx->r2 = (int32_t)(ret >> 32); - ctx->r3 = (int32_t)(ret >> 0); -} - -extern "C" void __ull_to_d_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - double ret = (double)a; - - ctx->f0.d = ret; -} - -extern "C" void __ull_to_f_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t a = (ctx->r4 << 32) | ((ctx->r5 << 0) & 0xFFFFFFFFu); - float ret = (float)a; - - ctx->f0.fl = ret; -} diff --git a/test/src/overlays.cpp b/test/src/overlays.cpp deleted file mode 100644 index ac62a5b..0000000 --- a/test/src/overlays.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include -#include -#include "recomp.h" -#include "../funcs/recomp_overlays.inl" - -constexpr size_t num_code_sections = ARRLEN(section_table); - -// SectionTableEntry sections[] defined in recomp_overlays.inl - -struct LoadedSection { - int32_t loaded_ram_addr; - size_t section_table_index; - - bool operator<(const LoadedSection& rhs) { - return loaded_ram_addr < rhs.loaded_ram_addr; - } -}; - -std::vector loaded_sections{}; -std::unordered_map func_map{}; - -void load_overlay(size_t section_table_index, int32_t ram) { - const SectionTableEntry& section = section_table[section_table_index]; - for (size_t function_index = 0; function_index < section.num_funcs; function_index++) { - const FuncEntry& func = section.funcs[function_index]; - func_map[ram + func.offset] = func.func; - } - loaded_sections.emplace_back(ram, section_table_index); - section_addresses[section.index] = ram; -} - - -extern "C" { -int32_t section_addresses[num_sections]; -} - -extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size) { - // Search for the first section that's included in the loaded rom range - // Sections were sorted by `init_overlays` so we can use the bounds functions - auto lower = std::lower_bound(§ion_table[0], §ion_table[num_code_sections], rom, - [](const SectionTableEntry& entry, uint32_t addr) { - return entry.rom_addr < addr; - } - ); - auto upper = std::upper_bound(§ion_table[0], §ion_table[num_code_sections], (uint32_t)(rom + size), - [](uint32_t addr, const SectionTableEntry& entry) { - return addr < entry.size + entry.rom_addr; - } - ); - // Load the overlays that were found - for (auto it = lower; it != upper; ++it) { - load_overlay(std::distance(§ion_table[0], it), it->rom_addr - rom + ram_addr); - } -} - -extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) { - for (auto it = loaded_sections.begin(); it != loaded_sections.end();) { - const auto& section = section_table[it->section_table_index]; - - // Check if the unloaded region overlaps with the loaded section - if (ram_addr < (it->loaded_ram_addr + section.size) && (ram_addr + size) >= it->loaded_ram_addr) { - // Check if the section isn't entirely in the loaded region - if (ram_addr > it->loaded_ram_addr || (ram_addr + size) < (it->loaded_ram_addr + section.size)) { - fprintf(stderr, - "Cannot partially unload section\n" - " rom: 0x%08X size: 0x%08X loaded_addr: 0x%08X\n" - " unloaded_ram: 0x%08X unloaded_size : 0x%08X\n", - section.rom_addr, section.size, it->loaded_ram_addr, ram_addr, size); - std::exit(EXIT_FAILURE); - } - // Determine where each function was loaded to and remove that entry from the function map - for (size_t func_index = 0; func_index < section.num_funcs; func_index++) { - const auto& func = section.funcs[func_index]; - uint32_t func_address = func.offset + it->loaded_ram_addr; - func_map.erase(func_address); - } - // Reset the section's address in the address table - section_addresses[section.index] = 0; - // Remove the section from the loaded section map - it = loaded_sections.erase(it); - // Skip incrementing the iterator - continue; - } - ++it; - } -} - -void init_overlays() { - for (size_t section_index = 0; section_index < num_code_sections; section_index++) { - section_addresses[section_table[section_index].index] = section_table[section_index].ram_addr; - } - - // Sort the executable sections by rom address - std::sort(§ion_table[0], §ion_table[num_code_sections], - [](const SectionTableEntry& a, const SectionTableEntry& b) { - return a.rom_addr < b.rom_addr; - } - ); -} - -extern "C" recomp_func_t * get_function(int32_t addr) { - auto func_find = func_map.find(addr); - if (func_find == func_map.end()) { - fprintf(stderr, "Failed to find function at 0x%08X\n", addr); - std::exit(EXIT_FAILURE); - } - return func_find->second; -} - diff --git a/test/src/pak.cpp b/test/src/pak.cpp deleted file mode 100644 index 777ece6..0000000 --- a/test/src/pak.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "recomp.h" -#include "../portultra/ultra64.h" -#include "../portultra/multilibultra.hpp" - -extern "C" void osPfsInitPak_recomp(uint8_t * rdram, recomp_context* ctx) { - ctx->r2 = 1; // PFS_ERR_NOPACK -} - -extern "C" void osPfsFreeBlocks_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 1; // PFS_ERR_NOPACK -} - -extern "C" void osPfsAllocateFile_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 1; // PFS_ERR_NOPACK -} - -extern "C" void osPfsDeleteFile_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 1; // PFS_ERR_NOPACK -} - -extern "C" void osPfsFileState_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 1; // PFS_ERR_NOPACK -} - -extern "C" void osPfsFindFile_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 1; // PFS_ERR_NOPACK -} - -extern "C" void osPfsReadWriteFile_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 1; // PFS_ERR_NOPACK -} diff --git a/test/src/pi.cpp b/test/src/pi.cpp deleted file mode 100644 index ca1807f..0000000 --- a/test/src/pi.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include -#include -#include -#include "recomp.h" -#include "../portultra/ultra64.h" -#include "../portultra/multilibultra.hpp" - -struct OSIoMesgHdr { - // These 3 reversed due to endianness - u8 status; /* Return status */ - u8 pri; /* Message priority (High or Normal) */ - u16 type; /* Message type */ - PTR(OSMesgQueue) retQueue; /* Return message queue to notify I/O completion */ -}; - -struct OSIoMesg { - OSIoMesgHdr hdr; /* Message header */ - PTR(void) dramAddr; /* RDRAM buffer address (DMA) */ - u32 devAddr; /* Device buffer address (DMA) */ - u32 size; /* DMA transfer size in bytes */ - u32 piHandle; /* PI device handle */ -}; - -struct OSPiHandle { - PTR(OSPiHandle_s) unused; /* point to next handle on the table */ - // These four members reversed due to endianness - u8 relDuration; /* domain release duration */ - u8 pageSize; /* domain page size */ - u8 latency; /* domain latency */ - u8 type; /* DEVICE_TYPE_BULK for disk */ - // These three members reversed due to endianness - u16 padding; /* struct alignment padding */ - u8 domain; /* which domain */ - u8 pulse; /* domain pulse width */ - u32 baseAddress; /* Domain address */ - u32 speed; /* for roms only */ - /* The following are "private" elements" */ - u32 transferInfo[18]; /* for disk only */ -}; - -// Flashram occupies the same physical address as sram, but that issue is avoided because libultra exposes -// a high-level interface for flashram. Because that high-level interface is reimplemented, low level accesses -// that involve physical addresses don't need to be handled for flashram. -constexpr uint32_t sram_base = 0x08000000; -constexpr uint32_t rom_base = 0x10000000; - -constexpr uint32_t k1_to_phys(uint32_t addr) { - return addr & 0x1FFFFFFF; -} - -constexpr uint32_t phys_to_k1(uint32_t addr) { - return addr | 0xA0000000; -} - -// We need a place in rdram to hold the cart handle, so pick an address in extended rdram -constexpr int32_t cart_handle = 0x80800000; - -extern std::unique_ptr rom; -extern size_t rom_size; - -extern "C" void osCartRomInit_recomp(uint8_t* rdram, recomp_context* ctx) { - OSPiHandle* handle = TO_PTR(OSPiHandle, cart_handle); - handle->type = 0; // cart - handle->baseAddress = phys_to_k1(rom_base); - handle->domain = 0; - - ctx->r2 = (gpr)cart_handle; -} - -extern "C" void osCreatePiManager_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes) { - // TODO use word copies when possible - uint8_t* rom_addr = rom.get() + physical_addr - rom_base; - for (size_t i = 0; i < num_bytes; i++) { - MEM_B(i, ram_address) = *rom_addr; - rom_addr++; - } -} - -std::array save_buffer; -const char save_filename[] = "save.bin"; - -void save_write(uint8_t* rdram, gpr rdram_address, uint32_t offset, uint32_t count) { - for (uint32_t i = 0; i < count; i++) { - save_buffer[offset + i] = MEM_B(i, rdram_address); - } - std::ofstream save_file{ save_filename, std::ios_base::binary }; - - if (save_file.good()) { - save_file.write(save_buffer.data(), save_buffer.size()); - } else { - fprintf(stderr, "Failed to save!\n"); - std::exit(EXIT_FAILURE); - } -} - -void save_read(uint8_t* rdram, gpr rdram_address, uint32_t offset, uint32_t count) { - for (size_t i = 0; i < count; i++) { - MEM_B(i, rdram_address) = save_buffer[offset + i]; - } -} - -void Multilibultra::save_init() { - std::ifstream save_file{ save_filename, std::ios_base::binary }; - - if (save_file.good()) { - save_file.read(save_buffer.data(), save_buffer.size()); - } else { - save_buffer.fill(0); - } -} - -void do_dma(uint8_t* rdram, PTR(OSMesgQueue) mq, gpr rdram_address, uint32_t physical_addr, uint32_t size, uint32_t direction) { - // TODO asynchronous transfer - // TODO implement unaligned DMA correctly - if (direction == 0) { - if (physical_addr >= rom_base) { - // read cart rom - do_rom_read(rdram, rdram_address, physical_addr, size); - - // Send a message to the mq to indicate that the transfer completed - osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); - } else if (physical_addr >= sram_base) { - // read sram - save_read(rdram, rdram_address, physical_addr - sram_base, size); - - // Send a message to the mq to indicate that the transfer completed - osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); - } else { - fprintf(stderr, "[WARN] PI DMA read from unknown region, phys address 0x%08X\n", physical_addr); - } - } else { - if (physical_addr >= rom_base) { - // write cart rom - throw std::runtime_error("ROM DMA write unimplemented"); - } else if (physical_addr >= sram_base) { - // write sram - save_write(rdram, rdram_address, physical_addr - sram_base, size); - - // Send a message to the mq to indicate that the transfer completed - osSendMesg(rdram, mq, 0, OS_MESG_NOBLOCK); - } else { - fprintf(stderr, "[WARN] PI DMA write to unknown region, phys address 0x%08X\n", physical_addr); - } - } -} - -extern "C" void osPiStartDma_recomp(uint8_t* rdram, recomp_context* ctx) { - uint32_t mb = ctx->r4; - uint32_t pri = ctx->r5; - uint32_t direction = ctx->r6; - uint32_t devAddr = ctx->r7; - gpr dramAddr = MEM_W(0x10, ctx->r29); - uint32_t size = MEM_W(0x14, ctx->r29); - PTR(OSMesgQueue) mq = MEM_W(0x18, ctx->r29); - uint32_t physical_addr = k1_to_phys(devAddr); - - debug_printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size); - - do_dma(rdram, mq, dramAddr, physical_addr, size, direction); - - ctx->r2 = 0; -} - -extern "C" void osEPiStartDma_recomp(uint8_t* rdram, recomp_context* ctx) { - OSPiHandle* handle = TO_PTR(OSPiHandle, ctx->r4); - OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r5); - uint32_t direction = ctx->r6; - uint32_t devAddr = handle->baseAddress | mb->devAddr; - gpr dramAddr = mb->dramAddr; - uint32_t size = mb->size; - PTR(OSMesgQueue) mq = mb->hdr.retQueue; - uint32_t physical_addr = k1_to_phys(devAddr); - - debug_printf("[pi] DMA from 0x%08X into 0x%08X of size 0x%08X\n", devAddr, dramAddr, size); - - do_dma(rdram, mq, dramAddr, physical_addr, size, direction); - - ctx->r2 = 0; -} - -extern "C" void osEPiReadIo_recomp(uint8_t * rdram, recomp_context * ctx) { - OSPiHandle* handle = TO_PTR(OSPiHandle, ctx->r4); - uint32_t devAddr = handle->baseAddress | ctx->r5; - gpr dramAddr = ctx->r6; - uint32_t physical_addr = k1_to_phys(devAddr); - - if (physical_addr > rom_base) { - // cart rom - do_rom_read(rdram, dramAddr, physical_addr, sizeof(uint32_t)); - } else { - // sram - assert(false && "SRAM ReadIo unimplemented"); - } - - ctx->r2 = 0; -} - -extern "C" void osPiGetStatus_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 0; -} - -extern "C" void osPiRawStartDma_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 0; -} diff --git a/test/src/portultra_translation.cpp b/test/src/portultra_translation.cpp deleted file mode 100644 index f2f9df2..0000000 --- a/test/src/portultra_translation.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#include -#include "../portultra/ultra64.h" -#include "../portultra/multilibultra.hpp" -#include "recomp.h" - -extern "C" void osInitialize_recomp(uint8_t * rdram, recomp_context * ctx) { - osInitialize(); -} - -extern "C" void __osInitialize_common_recomp(uint8_t * rdram, recomp_context * ctx) { - osInitialize(); -} - -extern "C" void osCreateThread_recomp(uint8_t* rdram, recomp_context* ctx) { - osCreateThread(rdram, (int32_t)ctx->r4, (OSId)ctx->r5, (int32_t)ctx->r6, (int32_t)ctx->r7, - (int32_t)MEM_W(0x10, ctx->r29), (OSPri)MEM_W(0x14, ctx->r29)); -} - -extern "C" void osStartThread_recomp(uint8_t* rdram, recomp_context* ctx) { - osStartThread(rdram, (int32_t)ctx->r4); -} - -extern "C" void osStopThread_recomp(uint8_t * rdram, recomp_context * ctx) { - osStopThread(rdram, (int32_t)ctx->r4); -} - -extern "C" void osDestroyThread_recomp(uint8_t * rdram, recomp_context * ctx) { - osDestroyThread(rdram, (int32_t)ctx->r4); -} - -extern "C" void osSetThreadPri_recomp(uint8_t* rdram, recomp_context* ctx) { - osSetThreadPri(rdram, (int32_t)ctx->r4, (OSPri)ctx->r5); -} - -extern "C" void osGetThreadPri_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = osGetThreadPri(rdram, (int32_t)ctx->r4); -} - -extern "C" void osGetThreadId_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = osGetThreadId(rdram, (int32_t)ctx->r4); -} - -extern "C" void osCreateMesgQueue_recomp(uint8_t* rdram, recomp_context* ctx) { - osCreateMesgQueue(rdram, (int32_t)ctx->r4, (int32_t)ctx->r5, (s32)ctx->r6); -} - -extern "C" void osRecvMesg_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = osRecvMesg(rdram, (int32_t)ctx->r4, (int32_t)ctx->r5, (s32)ctx->r6); -} - -extern "C" void osSendMesg_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = osSendMesg(rdram, (int32_t)ctx->r4, (OSMesg)ctx->r5, (s32)ctx->r6); -} - -extern "C" void osJamMesg_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = osJamMesg(rdram, (int32_t)ctx->r4, (OSMesg)ctx->r5, (s32)ctx->r6); -} - -extern "C" void osSetEventMesg_recomp(uint8_t* rdram, recomp_context* ctx) { - osSetEventMesg(rdram, (OSEvent)ctx->r4, (int32_t)ctx->r5, (OSMesg)ctx->r6); -} - -extern "C" void osViSetEvent_recomp(uint8_t * rdram, recomp_context * ctx) { - osViSetEvent(rdram, (int32_t)ctx->r4, (OSMesg)ctx->r5, (u32)ctx->r6); -} - -extern "C" void osGetCount_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = osGetCount(); -} - -extern "C" void osGetTime_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t total_count = osGetTime(); - ctx->r2 = (int32_t)(total_count >> 32); - ctx->r3 = (int32_t)(total_count >> 0); -} - -extern "C" void osSetTimer_recomp(uint8_t * rdram, recomp_context * ctx) { - uint64_t countdown = ((uint64_t)(ctx->r6) << 32) | ((ctx->r7) & 0xFFFFFFFFu); - uint64_t interval = load_doubleword(rdram, ctx->r29, 0x10); - ctx->r2 = osSetTimer(rdram, (int32_t)ctx->r4, countdown, interval, (int32_t)MEM_W(0x18, ctx->r29), (OSMesg)MEM_W(0x1C, ctx->r29)); -} - -extern "C" void osStopTimer_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = osStopTimer(rdram, (int32_t)ctx->r4); -} - -extern "C" void osVirtualToPhysical_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = osVirtualToPhysical((int32_t)ctx->r2); -} - -extern "C" void osInvalDCache_recomp(uint8_t * rdram, recomp_context * ctx) { - ; -} - -extern "C" void osInvalICache_recomp(uint8_t * rdram, recomp_context * ctx) { - ; -} - -extern "C" void osWritebackDCache_recomp(uint8_t * rdram, recomp_context * ctx) { - ; -} - -extern "C" void osWritebackDCacheAll_recomp(uint8_t * rdram, recomp_context * ctx) { - ; -} - -extern "C" void osSetIntMask_recomp(uint8_t * rdram, recomp_context * ctx) { - ; -} - -extern "C" void __osDisableInt_recomp(uint8_t * rdram, recomp_context * ctx) { - ; -} - -extern "C" void __osRestoreInt_recomp(uint8_t * rdram, recomp_context * ctx) { - ; -} - -extern "C" void __osSetFpcCsr_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 0; -} - -// For the Mario Party games (not working) -//extern "C" void longjmp_recomp(uint8_t * rdram, recomp_context * ctx) { -// RecompJmpBuf* buf = TO_PTR(RecompJmpBuf, ctx->r4); -// -// // Check if this is a buffer that was set up with setjmp -// if (buf->magic == SETJMP_MAGIC) { -// // If so, longjmp to it -// // Setjmp/longjmp does not work across threads, so verify that this buffer was made by this thread -// assert(buf->owner == Multilibultra::this_thread()); -// longjmp(buf->storage->buffer, ctx->r5); -// } else { -// // Otherwise, check if it was one built manually by the game with $ra pointing to a function -// gpr sp = MEM_W(0, ctx->r4); -// gpr ra = MEM_W(4, ctx->r4); -// ctx->r29 = sp; -// recomp_func_t* target = LOOKUP_FUNC(ra); -// if (target == nullptr) { -// fprintf(stderr, "Failed to find function for manual longjmp\n"); -// std::quick_exit(EXIT_FAILURE); -// } -// target(rdram, ctx); -// -// // TODO kill this thread if the target function returns -// assert(false); -// } -//} -// -//#undef setjmp_recomp -//extern "C" void setjmp_recomp(uint8_t * rdram, recomp_context * ctx) { -// fprintf(stderr, "Program called setjmp_recomp\n"); -// std::quick_exit(EXIT_FAILURE); -//} -// -//extern "C" int32_t osGetThreadEx(void) { -// return Multilibultra::this_thread(); -//} diff --git a/test/src/print.cpp b/test/src/print.cpp deleted file mode 100644 index c6e227b..0000000 --- a/test/src/print.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "../portultra/ultra64.h" -#include "../portultra/multilibultra.hpp" -#include "recomp.h" -#include "euc-jp.h" - -extern "C" void __checkHardware_msp_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 0; -} - -extern "C" void __checkHardware_kmc_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 0; -} - -extern "C" void __checkHardware_isv_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 0; -} - -extern "C" void __osInitialize_msp_recomp(uint8_t * rdram, recomp_context * ctx) { -} - -extern "C" void __osInitialize_kmc_recomp(uint8_t * rdram, recomp_context * ctx) { -} - -extern "C" void __osInitialize_isv_recomp(uint8_t * rdram, recomp_context * ctx) { -} - -extern "C" void isPrintfInit_recomp(uint8_t * rdram, recomp_context * ctx) { -} - -extern "C" void __osRdbSend_recomp(uint8_t * rdram, recomp_context * ctx) { - gpr buf = ctx->r4; - size_t size = ctx->r5; - u32 type = (u32)ctx->r6; - std::unique_ptr to_print = std::make_unique(size + 1); - - for (size_t i = 0; i < size; i++) { - to_print[i] = MEM_B(i, buf); - } - to_print[size] = '\x00'; - - fwrite(to_print.get(), 1, size, stdout); - - ctx->r2 = size; -} - -extern "C" void is_proutSyncPrintf_recomp(uint8_t * rdram, recomp_context * ctx) { - // Buffering to speed up print performance - static std::vector print_buffer; - - gpr buf = ctx->r5; - size_t size = ctx->r6; - - for (size_t i = 0; i < size; i++) { - // Add the new character to the buffer - char cur_char = MEM_B(i, buf); - - // If the new character is a newline, flush the buffer - if (cur_char == '\n') { - std::string utf8_str = Encoding::decode_eucjp(std::string_view{ print_buffer.data(), print_buffer.size() }); - puts(utf8_str.c_str()); - print_buffer.clear(); - } else { - print_buffer.push_back(cur_char); - } - } - - //fwrite(to_print.get(), size, 1, stdout); - - ctx->r2 = 1; -} diff --git a/test/src/recomp.cpp b/test/src/recomp.cpp deleted file mode 100644 index 957311c..0000000 --- a/test/src/recomp.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#ifdef _WIN32 -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include "recomp.h" -#include "../portultra/multilibultra.hpp" - -#ifdef _MSC_VER -inline uint32_t byteswap(uint32_t val) { - return _byteswap_ulong(val); -} -#else -constexpr uint32_t byteswap(uint32_t val) { - return __builtin_bswap32(val); -} -#endif - -extern "C" void _bzero(uint8_t* rdram, recomp_context* ctx) { - gpr start_addr = ctx->r4; - gpr size = ctx->r5; - - for (uint32_t i = 0; i < size; i++) { - MEM_B(start_addr, i) = 0; - } -} - -extern "C" void osGetMemSize_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 8 * 1024 * 1024; -} - -extern "C" void switch_error(const char* func, uint32_t vram, uint32_t jtbl) { - printf("Switch-case out of bounds in %s at 0x%08X for jump table at 0x%08X\n", func, vram, jtbl); - exit(EXIT_FAILURE); -} - -extern "C" void do_break(uint32_t vram) { - printf("Encountered break at original vram 0x%08X\n", vram); - exit(EXIT_FAILURE); -} - -void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t arg) { - recomp_context ctx{}; - ctx.r29 = sp; - ctx.r4 = arg; - recomp_func_t* func = get_function(addr); - func(rdram, &ctx); -} - -void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t dev_address, size_t num_bytes); - -std::unique_ptr rom; -size_t rom_size; - -// Recomp generation functions -extern "C" void recomp_entrypoint(uint8_t * rdram, recomp_context * ctx); -gpr get_entrypoint_address(); -const char* get_rom_name(); -void init_overlays(); -extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size); -extern "C" void unload_overlays(int32_t ram_addr, uint32_t size); - -#ifdef _WIN32 -#include -#endif - -int main(int argc, char **argv) { - //if (argc != 2) { - // printf("Usage: %s [baserom]\n", argv[0]); - // exit(EXIT_SUCCESS); - //} - -#ifdef _WIN32 - // Set up console output to accept UTF-8 on windows - SetConsoleOutputCP(CP_UTF8); - - // Change to a font that supports Japanese characters - CONSOLE_FONT_INFOEX cfi; - cfi.cbSize = sizeof cfi; - cfi.nFont = 0; - cfi.dwFontSize.X = 0; - cfi.dwFontSize.Y = 16; - cfi.FontFamily = FF_DONTCARE; - cfi.FontWeight = FW_NORMAL; - wcscpy_s(cfi.FaceName, L"NSimSun"); - SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi); -#else - std::setlocale(LC_ALL, "en_US.UTF-8"); -#endif - - { - std::basic_ifstream rom_file{ get_rom_name(), std::ios::binary }; - - size_t iobuf_size = 0x100000; - std::unique_ptr iobuf = std::make_unique(iobuf_size); - rom_file.rdbuf()->pubsetbuf(iobuf.get(), iobuf_size); - - if (!rom_file) { - fprintf(stderr, "Failed to open rom: %s\n", get_rom_name()); - exit(EXIT_FAILURE); - } - - rom_file.seekg(0, std::ios::end); - rom_size = rom_file.tellg(); - rom_file.seekg(0, std::ios::beg); - - rom = std::make_unique(rom_size); - - rom_file.read(rom.get(), rom_size); - - // TODO remove this - // Modify the name in the rom header so RT64 doesn't find it - rom[0x2F] = 'O'; - } - - // Initialize the overlays - init_overlays(); - - // Get entrypoint from recomp function - gpr entrypoint = get_entrypoint_address(); - - // Load overlays in the first 1MB - load_overlays(0x1000, (int32_t)entrypoint, 1024 * 1024); - - // Allocate rdram_buffer (16MB to give room for any extra addressable data used by recomp) - std::unique_ptr rdram_buffer = std::make_unique(16 * 1024 * 1024); - std::memset(rdram_buffer.get(), 0, 8 * 1024 * 1024); - recomp_context context{}; - - // Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000) - do_rom_read(rdram_buffer.get(), entrypoint, 0x10001000, 0x100000); - - // Set up stack pointer - context.r29 = 0xFFFFFFFF803FFFF0u; - - // Initialize variables normally set by IPL3 - constexpr int32_t osTvType = 0x80000300; - constexpr int32_t osRomType = 0x80000304; - constexpr int32_t osRomBase = 0x80000308; - constexpr int32_t osResetType = 0x8000030c; - constexpr int32_t osCicId = 0x80000310; - constexpr int32_t osVersion = 0x80000314; - constexpr int32_t osMemSize = 0x80000318; - constexpr int32_t osAppNMIBuffer = 0x8000031c; - uint8_t *rdram = rdram_buffer.get(); - MEM_W(osTvType, 0) = 1; // NTSC - MEM_W(osRomBase, 0) = 0xB0000000u; // standard rom base - MEM_W(osResetType, 0) = 0; // cold reset - MEM_W(osMemSize, 0) = 8 * 1024 * 1024; // 8MB - - debug_printf("[Recomp] Starting\n"); - - Multilibultra::preinit(rdram_buffer.get(), rom.get()); - - recomp_entrypoint(rdram_buffer.get(), &context); - - debug_printf("[Recomp] Quitting\n"); - - return EXIT_SUCCESS; -} diff --git a/test/src/rsp.h b/test/src/rsp.h deleted file mode 100644 index a5e53fb..0000000 --- a/test/src/rsp.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef __RSP_H__ -#define __RSP_H__ - -#include "rsp_vu.h" -#include "recomp.h" - -enum class RspExitReason { - Invalid, - Broke, - ImemOverrun, - UnhandledJumpTarget -}; - -extern uint8_t dmem[]; -extern uint16_t rspReciprocals[512]; -extern uint16_t rspInverseSquareRoots[512]; - -#define RSP_MEM_W(offset, addr) \ - (*reinterpret_cast(dmem + (offset) + (addr))) - -#define RSP_MEM_H(offset, addr) \ - (*reinterpret_cast(dmem + (((offset) + (addr)) ^ 2))) - -#define RSP_MEM_HU(offset, addr) \ - (*reinterpret_cast(dmem + (((offset) + (addr)) ^ 2))) - -#define RSP_MEM_B(offset, addr) \ - (*reinterpret_cast(dmem + (((offset) + (addr)) ^ 3))) - -#define RSP_MEM_BU(offset, addr) \ - (*reinterpret_cast(dmem + (((offset) + (addr)) ^ 3))) - -#define RSP_ADD32(a, b) \ - ((int32_t)((a) + (b))) - -#define RSP_SUB32(a, b) \ - ((int32_t)((a) - (b))) - -#define RSP_SIGNED(val) \ - ((int32_t)(val)) - -#define SET_DMA_DMEM(dmem_addr) dma_dmem_address = (dmem_addr) -#define SET_DMA_DRAM(dram_addr) dma_dram_address = (dram_addr) -#define DO_DMA_READ(rd_len) dma_rdram_to_dmem(rdram, dma_dmem_address, dma_dram_address, (rd_len)) -#define DO_DMA_WRITE(wr_len) dma_dmem_to_rdram(rdram, dma_dmem_address, dma_dram_address, (wr_len)) - -static inline void dma_rdram_to_dmem(uint8_t* rdram, uint32_t dmem_addr, uint32_t dram_addr, uint32_t rd_len) { - rd_len += 1; // Read length is inclusive - dram_addr &= 0xFFFFF8; - assert(dmem_addr + rd_len <= 0x1000); - for (uint32_t i = 0; i < rd_len; i++) { - RSP_MEM_B(i, dmem_addr) = MEM_B(0, (int64_t)(int32_t)(dram_addr + i + 0x80000000)); - } -} - -static inline void dma_dmem_to_rdram(uint8_t* rdram, uint32_t dmem_addr, uint32_t dram_addr, uint32_t wr_len) { - wr_len += 1; // Write length is inclusive - dram_addr &= 0xFFFFF8; - assert(dmem_addr + wr_len <= 0x1000); - for (uint32_t i = 0; i < wr_len; i++) { - MEM_B(0, (int64_t)(int32_t)(dram_addr + i + 0x80000000)) = RSP_MEM_B(i, dmem_addr); - } -} - -#endif diff --git a/test/src/rsp_vu.h b/test/src/rsp_vu.h deleted file mode 100644 index d60e62f..0000000 --- a/test/src/rsp_vu.h +++ /dev/null @@ -1,199 +0,0 @@ -// This file is modified from the Ares N64 emulator core. Ares can -// be found at https://github.com/ares-emulator/ares. The original license -// for this portion of Ares is as follows: -// ---------------------------------------------------------------------- -// ares -// -// Copyright(c) 2004 - 2021 ares team, Near et al -// -// Permission to use, copy, modify, and /or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright noticeand this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS.IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// ---------------------------------------------------------------------- -#include - -#define ARCHITECTURE_AMD64 -#define ARCHITECTURE_SUPPORTS_SSE4_1 1 - -#if defined(ARCHITECTURE_AMD64) -#include -using v128 = __m128i; -#elif defined(ARCHITECTURE_ARM64) -#include -using v128 = __m128i; -#endif - -namespace Accuracy { - namespace RSP { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - constexpr bool SISD = false; - constexpr bool SIMD = true; -#else - constexpr bool SISD = true; - constexpr bool SIMD = false; -#endif - } -} - -using u8 = uint8_t; -using s8 = int8_t; -using u16 = uint16_t; -using s16 = int16_t; -using u32 = uint32_t; -using s32 = int32_t; -using u64 = uint64_t; -using s64 = int64_t; -using uint128_t = uint64_t[2]; - -template inline auto sclamp(s64 x) -> s64 { - enum : s64 { b = 1ull << (bits - 1), m = b - 1 }; - return (x > m) ? m : (x < -b) ? -b : x; -} - -struct RSP { - using r32 = uint32_t; - using cr32 = const r32; - - union r128 { - struct { uint64_t u128[2]; }; -#if ARCHITECTURE_SUPPORTS_SSE4_1 - struct { __m128i v128; }; - - operator __m128i() const { return v128; } - auto operator=(__m128i value) { v128 = value; } -#endif - - auto byte(u32 index) -> uint8_t& { return ((uint8_t*)&u128)[15 - index]; } - auto byte(u32 index) const -> uint8_t { return ((uint8_t*)&u128)[15 - index]; } - - auto element(u32 index) -> uint16_t& { return ((uint16_t*)&u128)[7 - index]; } - auto element(u32 index) const -> uint16_t { return ((uint16_t*)&u128)[7 - index]; } - - auto u8(u32 index) -> uint8_t& { return ((uint8_t*)&u128)[15 - index]; } - auto u8(u32 index) const -> uint8_t { return ((uint8_t*)&u128)[15 - index]; } - - auto s16(u32 index) -> int16_t& { return ((int16_t*)&u128)[7 - index]; } - auto s16(u32 index) const -> int16_t { return ((int16_t*)&u128)[7 - index]; } - - auto u16(u32 index) -> uint16_t& { return ((uint16_t*)&u128)[7 - index]; } - auto u16(u32 index) const -> uint16_t { return ((uint16_t*)&u128)[7 - index]; } - - //VCx registers - auto get(u32 index) const -> bool { return u16(index) != 0; } - auto set(u32 index, bool value) -> bool { return u16(index) = 0 - value, value; } - - //vu-registers.cpp - inline auto operator()(u32 index) const -> r128; - }; - using cr128 = const r128; - - struct VU { - r128 r[32]; - r128 acch, accm, accl; - r128 vcoh, vcol; //16-bit little endian - r128 vcch, vccl; //16-bit little endian - r128 vce; // 8-bit little endian - s16 divin; - s16 divout; - bool divdp; - } vpu; - - static constexpr r128 zero{0}; - static constexpr r128 invert{(uint64_t)-1, (uint64_t)-1}; - - inline auto accumulatorGet(u32 index) const -> u64; - inline auto accumulatorSet(u32 index, u64 value) -> void; - inline auto accumulatorSaturate(u32 index, bool slice, u16 negative, u16 positive) const -> u16; - - inline auto CFC2(r32& rt, u8 rd) -> void; - inline auto CTC2(cr32& rt, u8 rd) -> void; - template inline auto LBV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LDV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LFV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LHV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LLV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LPV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LQV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LRV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LSV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LTV(u8 vt, cr32& rs, s8 imm) -> void; - template inline auto LUV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto LWV(r128& vt, cr32& rs, s8 imm) -> void; - template inline auto MFC2(r32& rt, cr128& vs) -> void; - template inline auto MTC2(cr32& rt, r128& vs) -> void; - template inline auto SBV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SDV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SFV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SHV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SLV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SPV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SQV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SRV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SSV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto STV(u8 vt, cr32& rs, s8 imm) -> void; - template inline auto SUV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto SWV(cr128& vt, cr32& rs, s8 imm) -> void; - template inline auto VABS(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VADD(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VADDC(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VAND(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VCH(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VCL(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VCR(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VEQ(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VGE(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VLT(r128& vd, cr128& vs, cr128& vt) -> void; - template - inline auto VMACF(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMACF(r128& vd, cr128& vs, cr128& vt) -> void { VMACF<0, e>(vd, vs, vt); } - template inline auto VMACU(r128& vd, cr128& vs, cr128& vt) -> void { VMACF<1, e>(vd, vs, vt); } - inline auto VMACQ(r128& vd) -> void; - template inline auto VMADH(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMADL(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMADM(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMADN(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMOV(r128& vd, u8 de, cr128& vt) -> void; - template inline auto VMRG(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMUDH(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMUDL(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMUDM(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VMUDN(r128& vd, cr128& vs, cr128& vt) -> void; - template - inline auto VMULF(r128& rd, cr128& vs, cr128& vt) -> void; - template inline auto VMULF(r128& rd, cr128& vs, cr128& vt) -> void { VMULF<0, e>(rd, vs, vt); } - template inline auto VMULU(r128& rd, cr128& vs, cr128& vt) -> void { VMULF<1, e>(rd, vs, vt); } - template inline auto VMULQ(r128& rd, cr128& vs, cr128& vt) -> void; - template inline auto VNAND(r128& rd, cr128& vs, cr128& vt) -> void; - template inline auto VNE(r128& vd, cr128& vs, cr128& vt) -> void; - inline auto VNOP() -> void; - template inline auto VNOR(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VNXOR(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VOR(r128& vd, cr128& vs, cr128& vt) -> void; - template - inline auto VRCP(r128& vd, u8 de, cr128& vt) -> void; - template inline auto VRCP(r128& vd, u8 de, cr128& vt) -> void { VRCP<0, e>(vd, de, vt); } - template inline auto VRCPL(r128& vd, u8 de, cr128& vt) -> void { VRCP<1, e>(vd, de, vt); } - template inline auto VRCPH(r128& vd, u8 de, cr128& vt) -> void; - template - inline auto VRND(r128& vd, u8 vs, cr128& vt) -> void; - template inline auto VRNDN(r128& vd, u8 vs, cr128& vt) -> void { VRND<0, e>(vd, vs, vt); } - template inline auto VRNDP(r128& vd, u8 vs, cr128& vt) -> void { VRND<1, e>(vd, vs, vt); } - template - inline auto VRSQ(r128& vd, u8 de, cr128& vt) -> void; - template inline auto VRSQ(r128& vd, u8 de, cr128& vt) -> void { VRSQ<0, e>(vd, de, vt); } - template inline auto VRSQL(r128& vd, u8 de, cr128& vt) -> void { VRSQ<1, e>(vd, de, vt); } - template inline auto VRSQH(r128& vd, u8 de, cr128& vt) -> void; - template inline auto VSAR(r128& vd, cr128& vs) -> void; - template inline auto VSUB(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VSUBC(r128& vd, cr128& vs, cr128& vt) -> void; - template inline auto VXOR(r128& rd, cr128& vs, cr128& vt) -> void; - template inline auto VZERO(r128& rd, cr128& vs, cr128& vt) -> void; -}; diff --git a/test/src/rsp_vu_impl.h b/test/src/rsp_vu_impl.h deleted file mode 100644 index 8c22d14..0000000 --- a/test/src/rsp_vu_impl.h +++ /dev/null @@ -1,1537 +0,0 @@ -// This file is modified from the Ares N64 emulator core. Ares can -// be found at https://github.com/ares-emulator/ares. The original license -// for this portion of Ares is as follows: -// ---------------------------------------------------------------------- -// ares -// -// Copyright(c) 2004 - 2021 ares team, Near et al -// -// Permission to use, copy, modify, and /or distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright noticeand this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS.IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// ---------------------------------------------------------------------- - -#include -#include -using u32 = uint32_t; - -#define ACCH vpu.acch -#define ACCM vpu.accm -#define ACCL vpu.accl -#define VCOH vpu.vcoh -#define VCOL vpu.vcol -#define VCCH vpu.vcch -#define VCCL vpu.vccl -#define VCE vpu.vce - -#define DIVIN vpu.divin -#define DIVOUT vpu.divout -#define DIVDP vpu.divdp - -auto RSP::r128::operator()(u32 index) const -> r128 { - if constexpr (Accuracy::RSP::SISD) { - r128 v{ *this }; - switch (index) { - case 0: break; - case 1: break; - case 2: v.u16(1) = v.u16(0); v.u16(3) = v.u16(2); v.u16(5) = v.u16(4); v.u16(7) = v.u16(6); break; - case 3: v.u16(0) = v.u16(1); v.u16(2) = v.u16(3); v.u16(4) = v.u16(5); v.u16(6) = v.u16(7); break; - case 4: v.u16(1) = v.u16(2) = v.u16(3) = v.u16(0); v.u16(5) = v.u16(6) = v.u16(7) = v.u16(4); break; - case 5: v.u16(0) = v.u16(2) = v.u16(3) = v.u16(1); v.u16(4) = v.u16(6) = v.u16(7) = v.u16(5); break; - case 6: v.u16(0) = v.u16(1) = v.u16(3) = v.u16(2); v.u16(4) = v.u16(5) = v.u16(7) = v.u16(6); break; - case 7: v.u16(0) = v.u16(1) = v.u16(2) = v.u16(3); v.u16(4) = v.u16(5) = v.u16(6) = v.u16(7); break; - case 8: for (u32 n = 0; n < 8; n++) v.u16(n) = v.u16(0); break; - case 9: for (u32 n = 0; n < 8; n++) v.u16(n) = v.u16(1); break; - case 10: for (u32 n = 0; n < 8; n++) v.u16(n) = v.u16(2); break; - case 11: for (u32 n = 0; n < 8; n++) v.u16(n) = v.u16(3); break; - case 12: for (u32 n = 0; n < 8; n++) v.u16(n) = v.u16(4); break; - case 13: for (u32 n = 0; n < 8; n++) v.u16(n) = v.u16(5); break; - case 14: for (u32 n = 0; n < 8; n++) v.u16(n) = v.u16(6); break; - case 15: for (u32 n = 0; n < 8; n++) v.u16(n) = v.u16(7); break; - } - return v; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - static const __m128i shuffle[16] = { - //vector - _mm_set_epi8(15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0), //01234567 - _mm_set_epi8(15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0), //01234567 - //scalar quarter - _mm_set_epi8(15,14,15,14,11,10,11,10, 7, 6, 7, 6, 3, 2, 3, 2), //00224466 - _mm_set_epi8(13,12,13,12, 9, 8, 9, 8, 5, 4, 5, 4, 1, 0, 1, 0), //11335577 - //scalar half - _mm_set_epi8(15,14,15,14,15,14,15,14, 7, 6, 7, 6, 7, 6, 7, 6), //00004444 - _mm_set_epi8(13,12,13,12,13,12,13,12, 5, 4, 5, 4, 5, 4, 5, 4), //11115555 - _mm_set_epi8(11,10,11,10,11,10,11,10, 3, 2, 3, 2, 3, 2, 3, 2), //22226666 - _mm_set_epi8(9, 8, 9, 8, 9, 8, 9, 8, 1, 0, 1, 0, 1, 0, 1, 0), //33337777 - //scalar whole - _mm_set_epi8(15,14,15,14,15,14,15,14,15,14,15,14,15,14,15,14), //00000000 - _mm_set_epi8(13,12,13,12,13,12,13,12,13,12,13,12,13,12,13,12), //11111111 - _mm_set_epi8(11,10,11,10,11,10,11,10,11,10,11,10,11,10,11,10), //22222222 - _mm_set_epi8(9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8), //33333333 - _mm_set_epi8(7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6), //44444444 - _mm_set_epi8(5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4), //55555555 - _mm_set_epi8(3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2), //66666666 - _mm_set_epi8(1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0), //77777777 - }; - //todo: benchmark to see if testing for cases 0&1 to return value directly is faster - r128 ret; - ret.v128 = _mm_shuffle_epi8(v128, shuffle[index]); - return ret; -#endif - } -} - -auto RSP::accumulatorGet(u32 index) const -> u64 { - return (u64)ACCH.u16(index) << 32 | (u64)ACCM.u16(index) << 16 | (u64)ACCL.u16(index) << 0; -} - -auto RSP::accumulatorSet(u32 index, u64 value) -> void { - ACCH.u16(index) = value >> 32; - ACCM.u16(index) = value >> 16; - ACCL.u16(index) = value >> 0; -} - -auto RSP::accumulatorSaturate(u32 index, bool slice, u16 negative, u16 positive) const -> u16 { - if (ACCH.s16(index) < 0) { - if (ACCH.u16(index) != 0xffff) return negative; - if (ACCM.s16(index) >= 0) return negative; - } else { - if (ACCH.u16(index) != 0x0000) return positive; - if (ACCM.s16(index) < 0) return positive; - } - return !slice ? ACCL.u16(index) : ACCM.u16(index); -} - -auto RSP::CFC2(r32& rt, u8 rd) -> void { - r128 hi, lo; - switch (rd & 3) { - case 0x00: hi = VCOH; lo = VCOL; break; - case 0x01: hi = VCCH; lo = VCCL; break; - case 0x02: hi = zero; lo = VCE; break; - case 0x03: hi = zero; lo = VCE; break; //unverified - } - - if constexpr (Accuracy::RSP::SISD) { - rt = 0; - for (u32 n = 0; n < 8; n++) { - rt |= lo.get(n) << 0 + n; - rt |= hi.get(n) << 8 + n; - } - rt = s16(rt); - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - static const v128 reverse = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - rt = s16(_mm_movemask_epi8(_mm_shuffle_epi8(_mm_packs_epi16(hi, lo), reverse))); -#endif - } -} - -auto RSP::CTC2(cr32& rt, u8 rd) -> void { - r128* hi; r128* lo; - r128 null; - switch (rd & 3) { - case 0x00: hi = &VCOH; lo = &VCOL; break; - case 0x01: hi = &VCCH; lo = &VCCL; break; - case 0x02: hi = &null; lo = &VCE; break; - case 0x03: hi = &null; lo = &VCE; break; //unverified - } - - if constexpr (Accuracy::RSP::SISD) { - for (u32 n = 0; n < 8; n++) { - lo->set(n, rt & 1 << 0 + n); - hi->set(n, rt & 1 << 8 + n); - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - static const v128 mask = _mm_set_epi16(0x0101, 0x0202, 0x0404, 0x0808, 0x1010, 0x2020, 0x4040, 0x8080); - lo->v128 = _mm_cmpeq_epi8(_mm_and_si128(_mm_shuffle_epi8(r128{ ~rt >> 0 }, zero), mask), zero); - hi->v128 = _mm_cmpeq_epi8(_mm_and_si128(_mm_shuffle_epi8(r128{ ~rt >> 8 }, zero), mask), zero); -#endif - } -} - -template -auto RSP::LBV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm; - vt.byte(e) = RSP_MEM_B(0, address); -} - -template -auto RSP::LDV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 8; - auto start = e; - auto end = std::min(start + 8, 16); - for (u32 offset = start; offset < end; offset++) { - vt.byte(offset & 15) = RSP_MEM_B(0, address++); - } -} - -template -auto RSP::LFV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto index = (address & 7) - e; - address &= ~7; - auto start = e; - auto end = std::min(start + 8, 16); - r128 tmp; - for (u32 offset = 0; offset < 4; offset++) { - tmp.element(offset + 0) = RSP_MEM_B(0, address + (index + offset * 4 + 0 & 15)) << 7; - tmp.element(offset + 4) = RSP_MEM_B(0, address + (index + offset * 4 + 8 & 15)) << 7; - } - for (u32 offset = start; offset < end; offset++) { - vt.byte(offset) = tmp.byte(offset); - } -} - -template -auto RSP::LHV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto index = (address & 7) - e; - address &= ~7; - for (u32 offset = 0; offset < 8; offset++) { - vt.element(offset) = RSP_MEM_B(0, address + (index + offset * 2 & 15)) << 7; - } -} - -template -auto RSP::LLV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 4; - auto start = e; - auto end = std::min(start + 4, 16); - for (u32 offset = start; offset < end; offset++) { - vt.byte(offset & 15) = RSP_MEM_B(0, address++); - } -} - -template -auto RSP::LPV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 8; - auto index = (address & 7) - e; - address &= ~7; - for (u32 offset = 0; offset < 8; offset++) { - vt.element(offset) = RSP_MEM_B(0, address + (index + offset & 15)) << 8; - } -} - -template -auto RSP::LQV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto start = e; - auto end = std::min((u32)(16 + e - (address & 15)), (u32)16); - for (u32 offset = start; offset < end; offset++) { - vt.byte(offset & 15) = RSP_MEM_B(0, address++); - } -} - -template -auto RSP::LRV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto index = e; - auto start = 16 - ((address & 15) - index); - address &= ~15; - for (u32 offset = start; offset < 16; offset++) { - vt.byte(offset & 15) = RSP_MEM_B(0, address++); - } -} - -template -auto RSP::LSV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 2; - auto start = e; - auto end = std::min(start + 2, 16); - for (u32 offset = start; offset < end; offset++) { - vt.byte(offset & 15) = RSP_MEM_B(0, address++); - } -} - -template -auto RSP::LTV(u8 vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto begin = address & ~7; - address = begin + ((e + (address & 8)) & 15); - auto vtbase = vt & ~7; - auto vtoff = e >> 1; - for (u32 i = 0; i < 8; i++) { - vpu.r[vtbase + vtoff].byte(i * 2 + 0) = RSP_MEM_B(0, address++); - if (address == begin + 16) address = begin; - vpu.r[vtbase + vtoff].byte(i * 2 + 1) = RSP_MEM_B(0, address++); - if (address == begin + 16) address = begin; - vtoff = vtoff + 1 & 7; - } -} - -template -auto RSP::LUV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 8; - auto index = (address & 7) - e; - address &= ~7; - for (u32 offset = 0; offset < 8; offset++) { - vt.element(offset) = RSP_MEM_B(0, address + (index + offset & 15)) << 7; - } -} - -template -auto RSP::LWV(r128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto start = 16 - e; - auto end = e + 16; - for (u32 offset = start; offset < end; offset++) { - vt.byte(offset & 15) = RSP_MEM_B(0, address); - address += 4; - } -} - -template -auto RSP::MFC2(r32& rt, cr128& vs) -> void { - auto hi = vs.byte(e + 0 & 15); - auto lo = vs.byte(e + 1 & 15); - rt = s16(hi << 8 | lo << 0); -} - -template -auto RSP::MTC2(cr32& rt, r128& vs) -> void { - vs.byte(e + 0) = rt >> 8; - if (e != 15) vs.byte(e + 1) = rt >> 0; -} - -template -auto RSP::SBV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm; - RSP_MEM_B(0, address) = vt.byte(e); -} - -template -auto RSP::SDV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 8; - auto start = e; - auto end = start + 8; - for (u32 offset = start; offset < end; offset++) { - RSP_MEM_B(0, address++) = vt.byte(offset & 15); - } -} - -template -auto RSP::SFV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto base = address & 7; - address &= ~7; - switch (e) { - case 0: case 15: - RSP_MEM_B(0, address + (base + 0 & 15)) = vt.element(0) >> 7; - RSP_MEM_B(0, address + (base + 4 & 15)) = vt.element(1) >> 7; - RSP_MEM_B(0, address + (base + 8 & 15)) = vt.element(2) >> 7; - RSP_MEM_B(0, address + (base + 12 & 15)) = vt.element(3) >> 7; - break; - case 1: - RSP_MEM_B(0, address + (base + 0 & 15)) = vt.element(6) >> 7; - RSP_MEM_B(0, address + (base + 4 & 15)) = vt.element(7) >> 7; - RSP_MEM_B(0, address + (base + 8 & 15)) = vt.element(4) >> 7; - RSP_MEM_B(0, address + (base + 12 & 15)) = vt.element(5) >> 7; - break; - case 4: - RSP_MEM_B(0, address + (base + 0 & 15)) = vt.element(1) >> 7; - RSP_MEM_B(0, address + (base + 4 & 15)) = vt.element(2) >> 7; - RSP_MEM_B(0, address + (base + 8 & 15)) = vt.element(3) >> 7; - RSP_MEM_B(0, address + (base + 12 & 15)) = vt.element(0) >> 7; - break; - case 5: - RSP_MEM_B(0, address + (base + 0 & 15)) = vt.element(7) >> 7; - RSP_MEM_B(0, address + (base + 4 & 15)) = vt.element(4) >> 7; - RSP_MEM_B(0, address + (base + 8 & 15)) = vt.element(5) >> 7; - RSP_MEM_B(0, address + (base + 12 & 15)) = vt.element(6) >> 7; - break; - case 8: - RSP_MEM_B(0, address + (base + 0 & 15)) = vt.element(4) >> 7; - RSP_MEM_B(0, address + (base + 4 & 15)) = vt.element(5) >> 7; - RSP_MEM_B(0, address + (base + 8 & 15)) = vt.element(6) >> 7; - RSP_MEM_B(0, address + (base + 12 & 15)) = vt.element(7) >> 7; - break; - case 11: - RSP_MEM_B(0, address + (base + 0 & 15)) = vt.element(3) >> 7; - RSP_MEM_B(0, address + (base + 4 & 15)) = vt.element(0) >> 7; - RSP_MEM_B(0, address + (base + 8 & 15)) = vt.element(1) >> 7; - RSP_MEM_B(0, address + (base + 12 & 15)) = vt.element(2) >> 7; - break; - case 12: - RSP_MEM_B(0, address + (base + 0 & 15)) = vt.element(5) >> 7; - RSP_MEM_B(0, address + (base + 4 & 15)) = vt.element(6) >> 7; - RSP_MEM_B(0, address + (base + 8 & 15)) = vt.element(7) >> 7; - RSP_MEM_B(0, address + (base + 12 & 15)) = vt.element(4) >> 7; - break; - default: - RSP_MEM_B(0, address + (base + 0 & 15)) = 0; - RSP_MEM_B(0, address + (base + 4 & 15)) = 0; - RSP_MEM_B(0, address + (base + 8 & 15)) = 0; - RSP_MEM_B(0, address + (base + 12 & 15)) = 0; - break; - } -} - -template -auto RSP::SHV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto index = address & 7; - address &= ~7; - for (u32 offset = 0; offset < 8; offset++) { - auto byte = e + offset * 2; - auto value = vt.byte(byte + 0 & 15) << 1 | vt.byte(byte + 1 & 15) >> 7; - RSP_MEM_B(0, address + (index + offset * 2 & 15)) = value; - } -} - -template -auto RSP::SLV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 4; - auto start = e; - auto end = start + 4; - for (u32 offset = start; offset < end; offset++) { - RSP_MEM_B(0, address++) = vt.byte(offset & 15); - } -} - -template -auto RSP::SPV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 8; - auto start = e; - auto end = start + 8; - for (u32 offset = start; offset < end; offset++) { - if ((offset & 15) < 8) { - RSP_MEM_B(0, address++) = vt.byte((offset & 7) << 1); - } else { - RSP_MEM_B(0, address++) = vt.element(offset & 7) >> 7; - } - } -} - -template -auto RSP::SQV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto start = e; - auto end = start + (16 - (address & 15)); - for (u32 offset = start; offset < end; offset++) { - RSP_MEM_B(0, address++) = vt.byte(offset & 15); - } -} - -template -auto RSP::SRV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto start = e; - auto end = start + (address & 15); - auto base = 16 - (address & 15); - address &= ~15; - for (u32 offset = start; offset < end; offset++) { - RSP_MEM_B(0, address++) = vt.byte(offset + base & 15); - } -} - -template -auto RSP::SSV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 2; - auto start = e; - auto end = start + 2; - for (u32 offset = start; offset < end; offset++) { - RSP_MEM_B(0, address++) = vt.byte(offset & 15); - } -} - -template -auto RSP::STV(u8 vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto start = vt & ~7; - auto end = start + 8; - auto element = 16 - (e & ~1); - auto base = (address & 7) - (e & ~1); - address &= ~7; - for (u32 offset = start; offset < end; offset++) { - RSP_MEM_B(0, address + (base++ & 15)) = vpu.r[offset].byte(element++ & 15); - RSP_MEM_B(0, address + (base++ & 15)) = vpu.r[offset].byte(element++ & 15); - } -} - -template -auto RSP::SUV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 8; - auto start = e; - auto end = start + 8; - for (u32 offset = start; offset < end; offset++) { - if ((offset & 15) < 8) { - RSP_MEM_B(0, address++) = vt.element(offset & 7) >> 7; - } else { - RSP_MEM_B(0, address++) = vt.byte((offset & 7) << 1); - } - } -} - -template -auto RSP::SWV(cr128& vt, cr32& rs, s8 imm) -> void { - auto address = rs + imm * 16; - auto start = e; - auto end = start + 16; - auto base = address & 7; - address &= ~7; - for (u32 offset = start; offset < end; offset++) { - RSP_MEM_B(0, address + (base++ & 15)) = vt.byte(offset & 15); - } -} - -template -auto RSP::VABS(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - r128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - if (vs.s16(n) < 0) { - if (vte.s16(n) == -32768) { - ACCL.s16(n) = -32768; - vd.s16(n) = 32767; - } else { - ACCL.s16(n) = -vte.s16(n); - vd.s16(n) = -vte.s16(n); - } - } else if (vs.s16(n) > 0) { - ACCL.s16(n) = +vte.s16(n); - vd.s16(n) = +vte.s16(n); - } else { - ACCL.s16(n) = 0; - vd.s16(n) = 0; - } - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vs0, slt; - vs0 = _mm_cmpeq_epi16(vs, zero); - slt = _mm_srai_epi16(vs, 15); - vd = _mm_andnot_si128(vs0, vt(e)); - vd = _mm_xor_si128(vd, slt); - ACCL = _mm_sub_epi16(vd, slt); - vd = _mm_subs_epi16(vd, slt); -#endif - } -} - -template -auto RSP::VADD(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - s32 result = vs.s16(n) + vte.s16(n) + VCOL.get(n); - ACCL.s16(n) = result; - vd.s16(n) = sclamp<16>(result); - } - VCOL = zero; - VCOH = zero; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), sum, min, max; - sum = _mm_add_epi16(vs, vte); - ACCL = _mm_sub_epi16(sum, VCOL); - min = _mm_min_epi16(vs, vte); - max = _mm_max_epi16(vs, vte); - min = _mm_subs_epi16(min, VCOL); - vd = _mm_adds_epi16(min, max); - VCOL = zero; - VCOH = zero; -#endif - } -} - -template -auto RSP::VADDC(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - u32 result = vs.u16(n) + vte.u16(n); - ACCL.u16(n) = result; - VCOL.set(n, result >> 16); - } - VCOH = zero; - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), sum; - sum = _mm_adds_epu16(vs, vte); - ACCL = _mm_add_epi16(vs, vte); - VCOL = _mm_cmpeq_epi16(sum, ACCL); - VCOL = _mm_cmpeq_epi16(VCOL, zero); - VCOH = zero; - vd = ACCL; -#endif - } -} - -template -auto RSP::VAND(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - r128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = vs.u16(n) & vte.u16(n); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - ACCL = _mm_and_si128(vs, vt(e)); - vd = ACCL; -#endif - } -} - -template -auto RSP::VCH(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - if ((vs.s16(n) ^ vte.s16(n)) < 0) { - s16 result = vs.s16(n) + vte.s16(n); - ACCL.s16(n) = (result <= 0 ? -vte.s16(n) : vs.s16(n)); - VCCL.set(n, result <= 0); - VCCH.set(n, vte.s16(n) < 0); - VCOL.set(n, 1); - VCOH.set(n, result != 0 && vs.u16(n) != (vte.u16(n) ^ 0xffff)); - VCE.set(n, result == -1); - } else { - s16 result = vs.s16(n) - vte.s16(n); - ACCL.s16(n) = (result >= 0 ? vte.s16(n) : vs.s16(n)); - VCCL.set(n, vte.s16(n) < 0); - VCCH.set(n, result >= 0); - VCOL.set(n, 0); - VCOH.set(n, result != 0 && vs.u16(n) != (vte.u16(n) ^ 0xffff)); - VCE.set(n, 0); - } - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), nvt, diff, diff0, vtn, dlez, dgez, mask; - VCOL = _mm_xor_si128(vs, vte); - VCOL = _mm_cmplt_epi16(VCOL, zero); - nvt = _mm_xor_si128(vte, VCOL); - nvt = _mm_sub_epi16(nvt, VCOL); - diff = _mm_sub_epi16(vs, nvt); - diff0 = _mm_cmpeq_epi16(diff, zero); - vtn = _mm_cmplt_epi16(vte, zero); - dlez = _mm_cmpgt_epi16(diff, zero); - dgez = _mm_or_si128(dlez, diff0); - dlez = _mm_cmpeq_epi16(zero, dlez); - VCCH = _mm_blendv_epi8(dgez, vtn, VCOL); - VCCL = _mm_blendv_epi8(vtn, dlez, VCOL); - VCE = _mm_cmpeq_epi16(diff, VCOL); - VCE = _mm_and_si128(VCE, VCOL); - VCOH = _mm_or_si128(diff0, VCE); - VCOH = _mm_cmpeq_epi16(VCOH, zero); - mask = _mm_blendv_epi8(VCCH, VCCL, VCOL); - ACCL = _mm_blendv_epi8(vs, nvt, mask); - vd = ACCL; -#endif - } -} - -template -auto RSP::VCL(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - if (VCOL.get(n)) { - if (VCOH.get(n)) { - ACCL.u16(n) = VCCL.get(n) ? -vte.u16(n) : vs.u16(n); - } else { - u16 sum = vs.u16(n) + vte.u16(n); - bool carry = (vs.u16(n) + vte.u16(n)) != sum; - if (VCE.get(n)) { - ACCL.u16(n) = VCCL.set(n, (!sum || !carry)) ? -vte.u16(n) : vs.u16(n); - } else { - ACCL.u16(n) = VCCL.set(n, (!sum && !carry)) ? -vte.u16(n) : vs.u16(n); - } - } - } else { - if (VCOH.get(n)) { - ACCL.u16(n) = VCCH.get(n) ? vte.u16(n) : vs.u16(n); - } else { - ACCL.u16(n) = VCCH.set(n, (s32)vs.u16(n) - (s32)vte.u16(n) >= 0) ? vte.u16(n) : vs.u16(n); - } - } - } - VCOL = zero; - VCOH = zero; - VCE = zero; - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), nvt, diff, ncarry, nvce, diff0, lec1, lec2, leeq, geeq, le, ge, mask; - nvt = _mm_xor_si128(vte, VCOL); - nvt = _mm_sub_epi16(nvt, VCOL); - diff = _mm_sub_epi16(vs, nvt); - ncarry = _mm_adds_epu16(vs, vte); - ncarry = _mm_cmpeq_epi16(diff, ncarry); - nvce = _mm_cmpeq_epi16(VCE, zero); - diff0 = _mm_cmpeq_epi16(diff, zero); - lec1 = _mm_and_si128(diff0, ncarry); - lec1 = _mm_and_si128(nvce, lec1); - lec2 = _mm_or_si128(diff0, ncarry); - lec2 = _mm_and_si128(VCE, lec2); - leeq = _mm_or_si128(lec1, lec2); - geeq = _mm_subs_epu16(vte, vs); - geeq = _mm_cmpeq_epi16(geeq, zero); - le = _mm_andnot_si128(VCOH, VCOL); - le = _mm_blendv_epi8(VCCL, leeq, le); - ge = _mm_or_si128(VCOL, VCOH); - ge = _mm_blendv_epi8(geeq, VCCH, ge); - mask = _mm_blendv_epi8(ge, le, VCOL); - ACCL = _mm_blendv_epi8(vs, nvt, mask); - VCCH = ge; - VCCL = le; - VCOH = zero; - VCOL = zero; - VCE = zero; - vd = ACCL; -#endif - } -} - -template -auto RSP::VCR(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - if ((vs.s16(n) ^ vte.s16(n)) < 0) { - VCCH.set(n, vte.s16(n) < 0); - ACCL.u16(n) = VCCL.set(n, vs.s16(n) + vte.s16(n) + 1 <= 0) ? ~vte.u16(n) : vs.u16(n); - } else { - VCCL.set(n, vte.s16(n) < 0); - ACCL.u16(n) = VCCH.set(n, vs.s16(n) - vte.s16(n) >= 0) ? vte.u16(n) : vs.u16(n); - } - } - VCOL = zero; - VCOH = zero; - VCE = zero; - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), sign, dlez, dgez, nvt, mask; - sign = _mm_xor_si128(vs, vte); - sign = _mm_srai_epi16(sign, 15); - dlez = _mm_and_si128(vs, sign); - dlez = _mm_add_epi16(dlez, vte); - VCCL = _mm_srai_epi16(dlez, 15); - dgez = _mm_or_si128(vs, sign); - dgez = _mm_min_epi16(dgez, vte); - VCCH = _mm_cmpeq_epi16(dgez, vte); - nvt = _mm_xor_si128(vte, sign); - mask = _mm_blendv_epi8(VCCH, VCCL, sign); - ACCL = _mm_blendv_epi8(vs, nvt, mask); - vd = ACCL; - VCOL = zero; - VCOH = zero; - VCE = zero; -#endif - } -} - -template -auto RSP::VEQ(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = VCCL.set(n, !VCOH.get(n) && vs.u16(n) == vte.u16(n)) ? vs.u16(n) : vte.u16(n); - } - VCCH = zero; //unverified - VCOL = zero; - VCOH = zero; - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), eq; - eq = _mm_cmpeq_epi16(vs, vte); - VCCL = _mm_andnot_si128(VCOH, eq); - ACCL = _mm_blendv_epi8(vte, vs, VCCL); - VCCH = zero; //unverified - VCOH = zero; - VCOL = zero; - vd = ACCL; -#endif - } -} - -template -auto RSP::VGE(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = VCCL.set(n, vs.s16(n) > vte.s16(n) || (vs.s16(n) == vte.s16(n) && (!VCOL.get(n) || !VCOH.get(n)))) ? vs.u16(n) : vte.u16(n); - } - VCCH = zero; //unverified - VCOL = zero; - VCOH = zero; - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), eq, gt, es; - eq = _mm_cmpeq_epi16(vs, vte); - gt = _mm_cmpgt_epi16(vs, vte); - es = _mm_and_si128(VCOH, VCOL); - eq = _mm_andnot_si128(es, eq); - VCCL = _mm_or_si128(gt, eq); - ACCL = _mm_blendv_epi8(vte, vs, VCCL); - VCCH = zero; - VCOH = zero; - VCOL = zero; - vd = ACCL; -#endif - } -} - -template -auto RSP::VLT(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = VCCL.set(n, vs.s16(n) < vte.s16(n) || (vs.s16(n) == vte.s16(n) && VCOL.get(n) && VCOH.get(n))) ? vs.u16(n) : vte.u16(n); - } - VCCH = zero; - VCOL = zero; - VCOH = zero; - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), eq, lt; - eq = _mm_cmpeq_epi16(vs, vte); - lt = _mm_cmplt_epi16(vs, vte); - eq = _mm_and_si128(VCOH, eq); - eq = _mm_and_si128(VCOL, eq); - VCCL = _mm_or_si128(lt, eq); - ACCL = _mm_blendv_epi8(vte, vs, VCCL); - VCCH = zero; - VCOH = zero; - VCOL = zero; - vd = ACCL; -#endif - } -} - -template -auto RSP::VMACF(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, accumulatorGet(n) + (s64)vs.s16(n) * (s64)vte.s16(n) * 2); - if constexpr (U == 0) { - vd.u16(n) = accumulatorSaturate(n, 1, 0x8000, 0x7fff); - } - if constexpr (U == 1) { - vd.u16(n) = ACCH.s16(n) < 0 ? 0x0000 : ACCH.s16(n) || ACCM.s16(n) < 0 ? 0xffff : ACCM.u16(n); - } - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), lo, md, hi, carry, omask; - lo = _mm_mullo_epi16(vs, vte); - hi = _mm_mulhi_epi16(vs, vte); - md = _mm_slli_epi16(hi, 1); - carry = _mm_srli_epi16(lo, 15); - hi = _mm_srai_epi16(hi, 15); - md = _mm_or_si128(md, carry); - lo = _mm_slli_epi16(lo, 1); - omask = _mm_adds_epu16(ACCL, lo); - ACCL = _mm_add_epi16(ACCL, lo); - omask = _mm_cmpeq_epi16(ACCL, omask); - omask = _mm_cmpeq_epi16(omask, zero); - md = _mm_sub_epi16(md, omask); - carry = _mm_cmpeq_epi16(md, zero); - carry = _mm_and_si128(carry, omask); - hi = _mm_sub_epi16(hi, carry); - omask = _mm_adds_epu16(ACCM, md); - ACCM = _mm_add_epi16(ACCM, md); - omask = _mm_cmpeq_epi16(ACCM, omask); - omask = _mm_cmpeq_epi16(omask, zero); - ACCH = _mm_add_epi16(ACCH, hi); - ACCH = _mm_sub_epi16(ACCH, omask); - if constexpr (!U) { - lo = _mm_unpacklo_epi16(ACCM, ACCH); - hi = _mm_unpackhi_epi16(ACCM, ACCH); - vd = _mm_packs_epi32(lo, hi); - } else { - r128 mmask, hmask; - mmask = _mm_srai_epi16(ACCM, 15); - hmask = _mm_srai_epi16(ACCH, 15); - md = _mm_or_si128(mmask, ACCM); - omask = _mm_cmpgt_epi16(ACCH, zero); - md = _mm_andnot_si128(hmask, md); - vd = _mm_or_si128(omask, md); - } -#endif - } -} - -auto RSP::VMACQ(r128& vd) -> void { - for (u32 n = 0; n < 8; n++) { - s32 product = ACCH.element(n) << 16 | ACCM.element(n) << 0; - if (product < 0 && !(product & 1 << 5)) product += 32; - else if (product >= 32 && !(product & 1 << 5)) product -= 32; - ACCH.element(n) = product >> 16; - ACCM.element(n) = product >> 0; - vd.element(n) = sclamp<16>(product >> 1) & ~15; - } -} - -template -auto RSP::VMADH(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - s32 result = (accumulatorGet(n) >> 16) + vs.s16(n) * vte.s16(n); - ACCH.u16(n) = result >> 16; - ACCM.u16(n) = result >> 0; - vd.u16(n) = accumulatorSaturate(n, 1, 0x8000, 0x7fff); - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), lo, hi, omask; - lo = _mm_mullo_epi16(vs, vte); - hi = _mm_mulhi_epi16(vs, vte); - omask = _mm_adds_epu16(ACCM, lo); - ACCM = _mm_add_epi16(ACCM, lo); - omask = _mm_cmpeq_epi16(ACCM, omask); - omask = _mm_cmpeq_epi16(omask, zero); - hi = _mm_sub_epi16(hi, omask); - ACCH = _mm_add_epi16(ACCH, hi); - lo = _mm_unpacklo_epi16(ACCM, ACCH); - hi = _mm_unpackhi_epi16(ACCM, ACCH); - vd = _mm_packs_epi32(lo, hi); -#endif - } -} - -template -auto RSP::VMADL(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, accumulatorGet(n) + (u32(vs.u16(n) * vte.u16(n)) >> 16)); - vd.u16(n) = accumulatorSaturate(n, 0, 0x0000, 0xffff); - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), hi, omask, nhi, nmd, shi, smd, cmask, cval; - hi = _mm_mulhi_epu16(vs, vte); - omask = _mm_adds_epu16(ACCL, hi); - ACCL = _mm_add_epi16(ACCL, hi); - omask = _mm_cmpeq_epi16(ACCL, omask); - omask = _mm_cmpeq_epi16(omask, zero); - hi = _mm_sub_epi16(zero, omask); - omask = _mm_adds_epu16(ACCM, hi); - ACCM = _mm_add_epi16(ACCM, hi); - omask = _mm_cmpeq_epi16(ACCM, omask); - omask = _mm_cmpeq_epi16(omask, zero); - ACCH = _mm_sub_epi16(ACCH, omask); - nhi = _mm_srai_epi16(ACCH, 15); - nmd = _mm_srai_epi16(ACCM, 15); - shi = _mm_cmpeq_epi16(nhi, ACCH); - smd = _mm_cmpeq_epi16(nhi, nmd); - cmask = _mm_and_si128(smd, shi); - cval = _mm_cmpeq_epi16(nhi, zero); - vd = _mm_blendv_epi8(cval, ACCL, cmask); -#endif - } -} - -template -auto RSP::VMADM(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, accumulatorGet(n) + vs.s16(n) * vte.u16(n)); - vd.u16(n) = accumulatorSaturate(n, 1, 0x8000, 0x7fff); - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), lo, hi, sign, vta, omask; - lo = _mm_mullo_epi16(vs, vte); - hi = _mm_mulhi_epu16(vs, vte); - sign = _mm_srai_epi16(vs, 15); - vta = _mm_and_si128(vte, sign); - hi = _mm_sub_epi16(hi, vta); - omask = _mm_adds_epu16(ACCL, lo); - ACCL = _mm_add_epi16(ACCL, lo); - omask = _mm_cmpeq_epi16(ACCL, omask); - omask = _mm_cmpeq_epi16(omask, zero); - hi = _mm_sub_epi16(hi, omask); - omask = _mm_adds_epu16(ACCM, hi); - ACCM = _mm_add_epi16(ACCM, hi); - omask = _mm_cmpeq_epi16(ACCM, omask); - omask = _mm_cmpeq_epi16(omask, zero); - hi = _mm_srai_epi16(hi, 15); - ACCH = _mm_add_epi16(ACCH, hi); - ACCH = _mm_sub_epi16(ACCH, omask); - lo = _mm_unpacklo_epi16(ACCM, ACCH); - hi = _mm_unpackhi_epi16(ACCM, ACCH); - vd = _mm_packs_epi32(lo, hi); -#endif - } -} - -template -auto RSP::VMADN(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, accumulatorGet(n) + s64(vs.u16(n) * vte.s16(n))); - vd.u16(n) = accumulatorSaturate(n, 0, 0x0000, 0xffff); - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), lo, hi, sign, vsa, omask, nhi, nmd, shi, smd, cmask, cval; - lo = _mm_mullo_epi16(vs, vte); - hi = _mm_mulhi_epu16(vs, vte); - sign = _mm_srai_epi16(vte, 15); - vsa = _mm_and_si128(vs, sign); - hi = _mm_sub_epi16(hi, vsa); - omask = _mm_adds_epu16(ACCL, lo); - ACCL = _mm_add_epi16(ACCL, lo); - omask = _mm_cmpeq_epi16(ACCL, omask); - omask = _mm_cmpeq_epi16(omask, zero); - hi = _mm_sub_epi16(hi, omask); - omask = _mm_adds_epu16(ACCM, hi); - ACCM = _mm_add_epi16(ACCM, hi); - omask = _mm_cmpeq_epi16(ACCM, omask); - omask = _mm_cmpeq_epi16(omask, zero); - hi = _mm_srai_epi16(hi, 15); - ACCH = _mm_add_epi16(ACCH, hi); - ACCH = _mm_sub_epi16(ACCH, omask); - nhi = _mm_srai_epi16(ACCH, 15); - nmd = _mm_srai_epi16(ACCM, 15); - shi = _mm_cmpeq_epi16(nhi, ACCH); - smd = _mm_cmpeq_epi16(nhi, nmd); - cmask = _mm_and_si128(smd, shi); - cval = _mm_cmpeq_epi16(nhi, zero); - vd = _mm_blendv_epi8(cval, ACCL, cmask); -#endif - } -} - -template -auto RSP::VMOV(r128& vd, u8 de, cr128& vt) -> void { - cr128 vte = vt(e); - vd.u16(de) = vte.u16(de); - ACCL = vte; -} - -template -auto RSP::VMRG(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = VCCL.get(n) ? vs.u16(n) : vte.u16(n); - } - VCOH = zero; - VCOL = zero; - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - ACCL = _mm_blendv_epi8(vt(e), vs, VCCL); - VCOH = zero; - VCOL = zero; - vd = ACCL; -#endif - } -} - -template -auto RSP::VMUDH(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, s64(vs.s16(n) * vte.s16(n)) << 16); - vd.u16(n) = accumulatorSaturate(n, 1, 0x8000, 0x7fff); - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), lo, hi; - ACCL = zero; - ACCM = _mm_mullo_epi16(vs, vte); - ACCH = _mm_mulhi_epi16(vs, vte); - lo = _mm_unpacklo_epi16(ACCM, ACCH); - hi = _mm_unpackhi_epi16(ACCM, ACCH); - vd = _mm_packs_epi32(lo, hi); -#endif - } -} - -template -auto RSP::VMUDL(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, u16(vs.u16(n) * vte.u16(n) >> 16)); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - ACCL = _mm_mulhi_epu16(vs, vt(e)); - ACCM = zero; - ACCH = zero; - vd = ACCL; -#endif - } -} - -template -auto RSP::VMUDM(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, s32(vs.s16(n) * vte.u16(n))); - } - vd = ACCM; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), sign, vta; - ACCL = _mm_mullo_epi16(vs, vte); - ACCM = _mm_mulhi_epu16(vs, vte); - sign = _mm_srai_epi16(vs, 15); - vta = _mm_and_si128(vte, sign); - ACCM = _mm_sub_epi16(ACCM, vta); - ACCH = _mm_srai_epi16(ACCM, 15); - vd = ACCM; -#endif - } -} - -template -auto RSP::VMUDN(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, s32(vs.u16(n) * vte.s16(n))); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), sign, vsa; - ACCL = _mm_mullo_epi16(vs, vte); - ACCM = _mm_mulhi_epu16(vs, vte); - sign = _mm_srai_epi16(vte, 15); - vsa = _mm_and_si128(vs, sign); - ACCM = _mm_sub_epi16(ACCM, vsa); - ACCH = _mm_srai_epi16(ACCM, 15); - vd = ACCL; -#endif - } -} - -template -auto RSP::VMULF(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - accumulatorSet(n, (s64)vs.s16(n) * (s64)vte.s16(n) * 2 + 0x8000); - if constexpr (U == 0) { - vd.u16(n) = accumulatorSaturate(n, 1, 0x8000, 0x7fff); - } - if constexpr (U == 1) { - vd.u16(n) = ACCH.s16(n) < 0 ? 0x0000 : (ACCH.s16(n) ^ ACCM.s16(n)) < 0 ? 0xffff : ACCM.u16(n); - } - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), lo, hi, round, sign1, sign2, neq, eq, neg; - lo = _mm_mullo_epi16(vs, vte); - round = _mm_cmpeq_epi16(zero, zero); - sign1 = _mm_srli_epi16(lo, 15); - lo = _mm_add_epi16(lo, lo); - round = _mm_slli_epi16(round, 15); - hi = _mm_mulhi_epi16(vs, vte); - sign2 = _mm_srli_epi16(lo, 15); - ACCL = _mm_add_epi16(round, lo); - sign1 = _mm_add_epi16(sign1, sign2); - hi = _mm_slli_epi16(hi, 1); - neq = _mm_cmpeq_epi16(vs, vte); - ACCM = _mm_add_epi16(hi, sign1); - neg = _mm_srai_epi16(ACCM, 15); - if constexpr (!U) { - eq = _mm_and_si128(neq, neg); - ACCH = _mm_andnot_si128(neq, neg); - vd = _mm_add_epi16(ACCM, eq); - } else { - ACCH = _mm_andnot_si128(neq, neg); - hi = _mm_or_si128(ACCM, neg); - vd = _mm_andnot_si128(ACCH, hi); - } -#endif - } -} - -template -auto RSP::VMULQ(r128& vd, cr128& vs, cr128& vt) -> void { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - s32 product = (s16)vs.element(n) * (s16)vte.element(n); - if (product < 0) product += 31; //round - ACCH.element(n) = product >> 16; - ACCM.element(n) = product >> 0; - ACCL.element(n) = 0; - vd.element(n) = sclamp<16>(product >> 1) & ~15; - } -} - -template -auto RSP::VNAND(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = ~(vs.u16(n) & vte.u16(n)); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - ACCL = _mm_and_si128(vs, vt(e)); - ACCL = _mm_xor_si128(ACCL, invert); - vd = ACCL; -#endif - } -} - -template -auto RSP::VNE(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = VCCL.set(n, vs.u16(n) != vte.u16(n) || VCOH.get(n)) ? vs.u16(n) : vte.u16(n); - } - VCCH = zero; //unverified - VCOL = zero; - VCOH = zero; - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), eq, ne; - eq = _mm_cmpeq_epi16(vs, vte); - ne = _mm_cmpeq_epi16(eq, zero); - VCCL = _mm_and_si128(VCOH, eq); - VCCL = _mm_or_si128(VCCL, ne); - ACCL = _mm_blendv_epi8(vte, vs, VCCL); - VCCH = zero; - VCOH = zero; - VCOL = zero; - vd = ACCL; -#endif - } -} - -auto RSP::VNOP() -> void { -} - -template -auto RSP::VNOR(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = ~(vs.u16(n) | vte.u16(n)); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - ACCL = _mm_or_si128(vs, vt(e)); - ACCL = _mm_xor_si128(ACCL, invert); - vd = ACCL; -#endif - } -} - -template -auto RSP::VNXOR(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = ~(vs.u16(n) ^ vte.u16(n)); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - ACCL = _mm_xor_si128(vs, vt(e)); - ACCL = _mm_xor_si128(ACCL, invert); - vd = ACCL; -#endif - } -} - -template -auto RSP::VOR(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = vs.u16(n) | vte.u16(n); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - ACCL = _mm_or_si128(vs, vt(e)); - vd = ACCL; -#endif - } -} - -template -auto RSP::VRCP(r128& vd, u8 de, cr128& vt) -> void { - s32 result = 0; - s32 input = L && DIVDP ? DIVIN << 16 | vt.element(e & 7) : s16(vt.element(e & 7)); - s32 mask = input >> 31; - s32 data = input ^ mask; - if (input > -32768) data -= mask; - if (data == 0) { - result = 0x7fff'ffff; - } else if (input == -32768) { - result = 0xffff'0000; - } else { - u32 shift = __builtin_clz(data); - u32 index = (u64(data) << shift & 0x7fc0'0000) >> 22; - result = rspReciprocals[index]; - result = (0x10000 | result) << 14; - result = result >> 31 - shift ^ mask; - } - DIVDP = 0; - DIVOUT = result >> 16; - ACCL = vt(e); - vd.element(de) = result; -} - -template -auto RSP::VRCPH(r128& vd, u8 de, cr128& vt) -> void { - ACCL = vt(e); - DIVDP = 1; - DIVIN = vt.element(e & 7); - vd.element(de) = DIVOUT; -} - -template -auto RSP::VRND(r128& vd, u8 vs, cr128& vt) -> void { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - s32 product = (s16)vte.element(n); - if (vs & 1) product <<= 16; - s64 acc = 0; - acc |= ACCH.element(n); acc <<= 16; - acc |= ACCM.element(n); acc <<= 16; - acc |= ACCL.element(n); acc <<= 16; - acc >>= 16; - if (D == 0 && acc < 0) acc = sclip<48>(acc + product); - if (D == 1 && acc >= 0) acc = sclip<48>(acc + product); - ACCH.element(n) = acc >> 32; - ACCM.element(n) = acc >> 16; - ACCL.element(n) = acc >> 0; - vd.element(n) = sclamp<16>(acc >> 16); - } -} - -template -auto RSP::VRSQ(r128& vd, u8 de, cr128& vt) -> void { - s32 result = 0; - s32 input = L && DIVDP ? DIVIN << 16 | vt.element(e & 7) : s16(vt.element(e & 7)); - s32 mask = input >> 31; - s32 data = input ^ mask; - if (input > -32768) data -= mask; - if (data == 0) { - result = 0x7fff'ffff; - } else if (input == -32768) { - result = 0xffff'0000; - } else { - u32 shift = __builtin_clz(data); - u32 index = (u64(data) << shift & 0x7fc0'0000) >> 22; - result = rspInverseSquareRoots[index & 0x1fe | shift & 1]; - result = (0x10000 | result) << 14; - result = result >> (31 - shift >> 1) ^ mask; - } - DIVDP = 0; - DIVOUT = result >> 16; - ACCL = vt(e); - vd.element(de) = result; -} - -template -auto RSP::VRSQH(r128& vd, u8 de, cr128& vt) -> void { - ACCL = vt(e); - DIVDP = 1; - DIVIN = vt.element(e & 7); - vd.element(de) = DIVOUT; -} - -template -auto RSP::VSAR(r128& vd, cr128& vs) -> void { - switch (e) { - case 0x8: vd = ACCH; break; - case 0x9: vd = ACCM; break; - case 0xa: vd = ACCL; break; - default: vd = zero; break; - } -} - -template -auto RSP::VSUB(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - s32 result = vs.s16(n) - vte.s16(n) - VCOL.get(n); - ACCL.s16(n) = result; - vd.s16(n) = sclamp<16>(result); - } - VCOL = zero; - VCOH = zero; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), udiff, sdiff, ov; - udiff = _mm_sub_epi16(vte, VCOL); - sdiff = _mm_subs_epi16(vte, VCOL); - ACCL = _mm_sub_epi16(vs, udiff); - ov = _mm_cmpgt_epi16(sdiff, udiff); - vd = _mm_subs_epi16(vs, sdiff); - vd = _mm_adds_epi16(vd, ov); - VCOL = zero; - VCOH = zero; -#endif - } -} - -template -auto RSP::VSUBC(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - u32 result = vs.u16(n) - vte.u16(n); - ACCL.u16(n) = result; - VCOL.set(n, result >> 16); - VCOH.set(n, result != 0); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), equal, udiff, diff0; - udiff = _mm_subs_epu16(vs, vte); - equal = _mm_cmpeq_epi16(vs, vte); - diff0 = _mm_cmpeq_epi16(udiff, zero); - VCOH = _mm_cmpeq_epi16(equal, zero); - VCOL = _mm_andnot_si128(equal, diff0); - ACCL = _mm_sub_epi16(vs, vte); - vd = ACCL; -#endif - } -} - -template -auto RSP::VXOR(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - ACCL.u16(n) = vs.u16(n) ^ vte.u16(n); - } - vd = ACCL; - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - ACCL = _mm_xor_si128(vs, vt(e)); - vd = ACCL; -#endif - } -} - -template -auto RSP::VZERO(r128& vd, cr128& vs, cr128& vt) -> void { - if constexpr (Accuracy::RSP::SISD) { - cr128 vte = vt(e); - for (u32 n = 0; n < 8; n++) { - s32 result = vs.s16(n) + vte.s16(n); - ACCL.s16(n) = result; - vd.s16(n) = 0; - } - } - - if constexpr (Accuracy::RSP::SIMD) { -#if ARCHITECTURE_SUPPORTS_SSE4_1 - r128 vte = vt(e), sum, min, max; - ACCL = _mm_add_epi16(vs, vte); - vd = _mm_xor_si128(vd, vd); -#endif - } -} - -#undef ACCH -#undef ACCM -#undef ACCL -#undef VCOH -#undef VCOL -#undef VCCH -#undef VCCL -#undef VCE - -#undef DIVIN -#undef DIVOUT -#undef DIVDP diff --git a/test/src/sp.cpp b/test/src/sp.cpp deleted file mode 100644 index d443948..0000000 --- a/test/src/sp.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include "../portultra/multilibultra.hpp" -#include "recomp.h" - -extern "C" void osSpTaskLoad_recomp(uint8_t* rdram, recomp_context* ctx) { - // Nothing to do here -} - -bool dump_frame = false; - -extern "C" void osSpTaskStartGo_recomp(uint8_t* rdram, recomp_context* ctx) { - //printf("[sp] osSpTaskStartGo(0x%08X)\n", (uint32_t)ctx->r4); - OSTask* task = TO_PTR(OSTask, ctx->r4); - if (task->t.type == M_GFXTASK) { - //printf("[sp] Gfx task: %08X\n", (uint32_t)ctx->r4); - } else if (task->t.type == M_AUDTASK) { - //printf("[sp] Audio task: %08X\n", (uint32_t)ctx->r4); - } - // For debugging - if (dump_frame) { - char addr_str[32]; - constexpr size_t ram_size = 0x800000; - std::unique_ptr ram_unswapped = std::make_unique(ram_size); - sprintf(addr_str, "%08X", task->t.data_ptr); - std::ofstream dump_file{ "../../ramdump" + std::string{ addr_str } + ".bin", std::ios::binary}; - - for (size_t i = 0; i < ram_size; i++) { - ram_unswapped[i] = rdram[i ^ 3]; - } - - dump_file.write(ram_unswapped.get(), ram_size); - dump_frame = false; - } - Multilibultra::submit_rsp_task(rdram, ctx->r4); -} - -extern "C" void osSpTaskYield_recomp(uint8_t* rdram, recomp_context* ctx) { - // Ignore yield requests (acts as if the task completed before it received the yield request) -} - -extern "C" void osSpTaskYielded_recomp(uint8_t* rdram, recomp_context* ctx) { - // Task yield requests are ignored, so always return 0 as tasks will never be yielded - ctx->r2 = 0; -} - -extern "C" void __osSpSetPc_recomp(uint8_t* rdram, recomp_context* ctx) { - assert(false); -} diff --git a/test/src/vi.cpp b/test/src/vi.cpp deleted file mode 100644 index 0b1f714..0000000 --- a/test/src/vi.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../portultra/multilibultra.hpp" -#include "recomp.h" - -extern "C" void osViSetYScale_recomp(uint8_t* rdram, recomp_context * ctx) { - ; -} - -extern "C" void osViSetXScale_recomp(uint8_t* rdram, recomp_context * ctx) { - ; -} - -extern "C" void osCreateViManager_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osViBlack_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osViSetSpecialFeatures_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osViGetCurrentFramebuffer_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = (gpr)(int32_t)osViGetCurrentFramebuffer(); -} - -extern "C" void osViGetNextFramebuffer_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = (gpr)(int32_t)osViGetNextFramebuffer(); -} - -extern "C" void osViSwapBuffer_recomp(uint8_t* rdram, recomp_context* ctx) { - osViSwapBuffer(rdram, (int32_t)ctx->r4); -} - -extern "C" void osViSetMode_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} diff --git a/test/thirdparty/blockingconcurrentqueue.h b/test/thirdparty/blockingconcurrentqueue.h deleted file mode 100644 index 205a4db..0000000 --- a/test/thirdparty/blockingconcurrentqueue.h +++ /dev/null @@ -1,582 +0,0 @@ -// Provides an efficient blocking version of moodycamel::ConcurrentQueue. -// ©2015-2020 Cameron Desrochers. Distributed under the terms of the simplified -// BSD license, available at the top of concurrentqueue.h. -// Also dual-licensed under the Boost Software License (see LICENSE.md) -// Uses Jeff Preshing's semaphore implementation (under the terms of its -// separate zlib license, see lightweightsemaphore.h). - -#pragma once - -#include "concurrentqueue.h" -#include "lightweightsemaphore.h" - -#include -#include -#include -#include -#include - -namespace moodycamel -{ -// This is a blocking version of the queue. It has an almost identical interface to -// the normal non-blocking version, with the addition of various wait_dequeue() methods -// and the removal of producer-specific dequeue methods. -template -class BlockingConcurrentQueue -{ -private: - typedef ::moodycamel::ConcurrentQueue ConcurrentQueue; - typedef ::moodycamel::LightweightSemaphore LightweightSemaphore; - -public: - typedef typename ConcurrentQueue::producer_token_t producer_token_t; - typedef typename ConcurrentQueue::consumer_token_t consumer_token_t; - - typedef typename ConcurrentQueue::index_t index_t; - typedef typename ConcurrentQueue::size_t size_t; - typedef typename std::make_signed::type ssize_t; - - static const size_t BLOCK_SIZE = ConcurrentQueue::BLOCK_SIZE; - static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = ConcurrentQueue::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD; - static const size_t EXPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::EXPLICIT_INITIAL_INDEX_SIZE; - static const size_t IMPLICIT_INITIAL_INDEX_SIZE = ConcurrentQueue::IMPLICIT_INITIAL_INDEX_SIZE; - static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = ConcurrentQueue::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; - static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = ConcurrentQueue::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE; - static const size_t MAX_SUBQUEUE_SIZE = ConcurrentQueue::MAX_SUBQUEUE_SIZE; - -public: - // Creates a queue with at least `capacity` element slots; note that the - // actual number of elements that can be inserted without additional memory - // allocation depends on the number of producers and the block size (e.g. if - // the block size is equal to `capacity`, only a single block will be allocated - // up-front, which means only a single producer will be able to enqueue elements - // without an extra allocation -- blocks aren't shared between producers). - // This method is not thread safe -- it is up to the user to ensure that the - // queue is fully constructed before it starts being used by other threads (this - // includes making the memory effects of construction visible, possibly with a - // memory barrier). - explicit BlockingConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE) - : inner(capacity), sema(create(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy) - { - assert(reinterpret_cast((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member"); - if (!sema) { - MOODYCAMEL_THROW(std::bad_alloc()); - } - } - - BlockingConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers) - : inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create(0, (int)Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy) - { - assert(reinterpret_cast((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member"); - if (!sema) { - MOODYCAMEL_THROW(std::bad_alloc()); - } - } - - // Disable copying and copy assignment - BlockingConcurrentQueue(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; - BlockingConcurrentQueue& operator=(BlockingConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; - - // Moving is supported, but note that it is *not* a thread-safe operation. - // Nobody can use the queue while it's being moved, and the memory effects - // of that move must be propagated to other threads before they can use it. - // Note: When a queue is moved, its tokens are still valid but can only be - // used with the destination queue (i.e. semantically they are moved along - // with the queue itself). - BlockingConcurrentQueue(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT - : inner(std::move(other.inner)), sema(std::move(other.sema)) - { } - - inline BlockingConcurrentQueue& operator=(BlockingConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT - { - return swap_internal(other); - } - - // Swaps this queue's state with the other's. Not thread-safe. - // Swapping two queues does not invalidate their tokens, however - // the tokens that were created for one queue must be used with - // only the swapped queue (i.e. the tokens are tied to the - // queue's movable state, not the object itself). - inline void swap(BlockingConcurrentQueue& other) MOODYCAMEL_NOEXCEPT - { - swap_internal(other); - } - -private: - BlockingConcurrentQueue& swap_internal(BlockingConcurrentQueue& other) - { - if (this == &other) { - return *this; - } - - inner.swap(other.inner); - sema.swap(other.sema); - return *this; - } - -public: - // Enqueues a single item (by copying it). - // Allocates memory if required. Only fails if memory allocation fails (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, - // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(T const& item) - { - if ((details::likely)(inner.enqueue(item))) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues a single item (by moving it, if possible). - // Allocates memory if required. Only fails if memory allocation fails (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, - // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(T&& item) - { - if ((details::likely)(inner.enqueue(std::move(item)))) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues a single item (by copying it) using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails (or - // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(producer_token_t const& token, T const& item) - { - if ((details::likely)(inner.enqueue(token, item))) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues a single item (by moving it, if possible) using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails (or - // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(producer_token_t const& token, T&& item) - { - if ((details::likely)(inner.enqueue(token, std::move(item)))) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues several items. - // Allocates memory if required. Only fails if memory allocation fails (or - // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Note: Use std::make_move_iterator if the elements should be moved instead of copied. - // Thread-safe. - template - inline bool enqueue_bulk(It itemFirst, size_t count) - { - if ((details::likely)(inner.enqueue_bulk(std::forward(itemFirst), count))) { - sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count); - return true; - } - return false; - } - - // Enqueues several items using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails - // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - inline bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) - { - if ((details::likely)(inner.enqueue_bulk(token, std::forward(itemFirst), count))) { - sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count); - return true; - } - return false; - } - - // Enqueues a single item (by copying it). - // Does not allocate memory. Fails if not enough room to enqueue (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - // is 0). - // Thread-safe. - inline bool try_enqueue(T const& item) - { - if (inner.try_enqueue(item)) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues a single item (by moving it, if possible). - // Does not allocate memory (except for one-time implicit producer). - // Fails if not enough room to enqueue (or implicit production is - // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). - // Thread-safe. - inline bool try_enqueue(T&& item) - { - if (inner.try_enqueue(std::move(item))) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues a single item (by copying it) using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Thread-safe. - inline bool try_enqueue(producer_token_t const& token, T const& item) - { - if (inner.try_enqueue(token, item)) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues a single item (by moving it, if possible) using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Thread-safe. - inline bool try_enqueue(producer_token_t const& token, T&& item) - { - if (inner.try_enqueue(token, std::move(item))) { - sema->signal(); - return true; - } - return false; - } - - // Enqueues several items. - // Does not allocate memory (except for one-time implicit producer). - // Fails if not enough room to enqueue (or implicit production is - // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - inline bool try_enqueue_bulk(It itemFirst, size_t count) - { - if (inner.try_enqueue_bulk(std::forward(itemFirst), count)) { - sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count); - return true; - } - return false; - } - - // Enqueues several items using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - inline bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) - { - if (inner.try_enqueue_bulk(token, std::forward(itemFirst), count)) { - sema->signal((LightweightSemaphore::ssize_t)(ssize_t)count); - return true; - } - return false; - } - - - // Attempts to dequeue from the queue. - // Returns false if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - inline bool try_dequeue(U& item) - { - if (sema->tryWait()) { - while (!inner.try_dequeue(item)) { - continue; - } - return true; - } - return false; - } - - // Attempts to dequeue from the queue using an explicit consumer token. - // Returns false if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - inline bool try_dequeue(consumer_token_t& token, U& item) - { - if (sema->tryWait()) { - while (!inner.try_dequeue(token, item)) { - continue; - } - return true; - } - return false; - } - - // Attempts to dequeue several elements from the queue. - // Returns the number of items actually dequeued. - // Returns 0 if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - inline size_t try_dequeue_bulk(It itemFirst, size_t max) - { - size_t count = 0; - max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max); - while (count != max) { - count += inner.template try_dequeue_bulk(itemFirst, max - count); - } - return count; - } - - // Attempts to dequeue several elements from the queue using an explicit consumer token. - // Returns the number of items actually dequeued. - // Returns 0 if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - inline size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max) - { - size_t count = 0; - max = (size_t)sema->tryWaitMany((LightweightSemaphore::ssize_t)(ssize_t)max); - while (count != max) { - count += inner.template try_dequeue_bulk(token, itemFirst, max - count); - } - return count; - } - - - - // Blocks the current thread until there's something to dequeue, then - // dequeues it. - // Never allocates. Thread-safe. - template - inline void wait_dequeue(U& item) - { - while (!sema->wait()) { - continue; - } - while (!inner.try_dequeue(item)) { - continue; - } - } - - // Blocks the current thread until either there's something to dequeue - // or the timeout (specified in microseconds) expires. Returns false - // without setting `item` if the timeout expires, otherwise assigns - // to `item` and returns true. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue. - // Never allocates. Thread-safe. - template - inline bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs) - { - if (!sema->wait(timeout_usecs)) { - return false; - } - while (!inner.try_dequeue(item)) { - continue; - } - return true; - } - - // Blocks the current thread until either there's something to dequeue - // or the timeout expires. Returns false without setting `item` if the - // timeout expires, otherwise assigns to `item` and returns true. - // Never allocates. Thread-safe. - template - inline bool wait_dequeue_timed(U& item, std::chrono::duration const& timeout) - { - return wait_dequeue_timed(item, std::chrono::duration_cast(timeout).count()); - } - - // Blocks the current thread until there's something to dequeue, then - // dequeues it using an explicit consumer token. - // Never allocates. Thread-safe. - template - inline void wait_dequeue(consumer_token_t& token, U& item) - { - while (!sema->wait()) { - continue; - } - while (!inner.try_dequeue(token, item)) { - continue; - } - } - - // Blocks the current thread until either there's something to dequeue - // or the timeout (specified in microseconds) expires. Returns false - // without setting `item` if the timeout expires, otherwise assigns - // to `item` and returns true. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue. - // Never allocates. Thread-safe. - template - inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::int64_t timeout_usecs) - { - if (!sema->wait(timeout_usecs)) { - return false; - } - while (!inner.try_dequeue(token, item)) { - continue; - } - return true; - } - - // Blocks the current thread until either there's something to dequeue - // or the timeout expires. Returns false without setting `item` if the - // timeout expires, otherwise assigns to `item` and returns true. - // Never allocates. Thread-safe. - template - inline bool wait_dequeue_timed(consumer_token_t& token, U& item, std::chrono::duration const& timeout) - { - return wait_dequeue_timed(token, item, std::chrono::duration_cast(timeout).count()); - } - - // Attempts to dequeue several elements from the queue. - // Returns the number of items actually dequeued, which will - // always be at least one (this method blocks until the queue - // is non-empty) and at most max. - // Never allocates. Thread-safe. - template - inline size_t wait_dequeue_bulk(It itemFirst, size_t max) - { - size_t count = 0; - max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max); - while (count != max) { - count += inner.template try_dequeue_bulk(itemFirst, max - count); - } - return count; - } - - // Attempts to dequeue several elements from the queue. - // Returns the number of items actually dequeued, which can - // be 0 if the timeout expires while waiting for elements, - // and at most max. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue_bulk. - // Never allocates. Thread-safe. - template - inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::int64_t timeout_usecs) - { - size_t count = 0; - max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs); - while (count != max) { - count += inner.template try_dequeue_bulk(itemFirst, max - count); - } - return count; - } - - // Attempts to dequeue several elements from the queue. - // Returns the number of items actually dequeued, which can - // be 0 if the timeout expires while waiting for elements, - // and at most max. - // Never allocates. Thread-safe. - template - inline size_t wait_dequeue_bulk_timed(It itemFirst, size_t max, std::chrono::duration const& timeout) - { - return wait_dequeue_bulk_timed(itemFirst, max, std::chrono::duration_cast(timeout).count()); - } - - // Attempts to dequeue several elements from the queue using an explicit consumer token. - // Returns the number of items actually dequeued, which will - // always be at least one (this method blocks until the queue - // is non-empty) and at most max. - // Never allocates. Thread-safe. - template - inline size_t wait_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max) - { - size_t count = 0; - max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max); - while (count != max) { - count += inner.template try_dequeue_bulk(token, itemFirst, max - count); - } - return count; - } - - // Attempts to dequeue several elements from the queue using an explicit consumer token. - // Returns the number of items actually dequeued, which can - // be 0 if the timeout expires while waiting for elements, - // and at most max. - // Using a negative timeout indicates an indefinite timeout, - // and is thus functionally equivalent to calling wait_dequeue_bulk. - // Never allocates. Thread-safe. - template - inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::int64_t timeout_usecs) - { - size_t count = 0; - max = (size_t)sema->waitMany((LightweightSemaphore::ssize_t)(ssize_t)max, timeout_usecs); - while (count != max) { - count += inner.template try_dequeue_bulk(token, itemFirst, max - count); - } - return count; - } - - // Attempts to dequeue several elements from the queue using an explicit consumer token. - // Returns the number of items actually dequeued, which can - // be 0 if the timeout expires while waiting for elements, - // and at most max. - // Never allocates. Thread-safe. - template - inline size_t wait_dequeue_bulk_timed(consumer_token_t& token, It itemFirst, size_t max, std::chrono::duration const& timeout) - { - return wait_dequeue_bulk_timed(token, itemFirst, max, std::chrono::duration_cast(timeout).count()); - } - - - // Returns an estimate of the total number of elements currently in the queue. This - // estimate is only accurate if the queue has completely stabilized before it is called - // (i.e. all enqueue and dequeue operations have completed and their memory effects are - // visible on the calling thread, and no further operations start while this method is - // being called). - // Thread-safe. - inline size_t size_approx() const - { - return (size_t)sema->availableApprox(); - } - - - // Returns true if the underlying atomic variables used by - // the queue are lock-free (they should be on most platforms). - // Thread-safe. - static constexpr bool is_lock_free() - { - return ConcurrentQueue::is_lock_free(); - } - - -private: - template - static inline U* create(A1&& a1, A2&& a2) - { - void* p = (Traits::malloc)(sizeof(U)); - return p != nullptr ? new (p) U(std::forward(a1), std::forward(a2)) : nullptr; - } - - template - static inline void destroy(U* p) - { - if (p != nullptr) { - p->~U(); - } - (Traits::free)(p); - } - -private: - ConcurrentQueue inner; - std::unique_ptr sema; -}; - - -template -inline void swap(BlockingConcurrentQueue& a, BlockingConcurrentQueue& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -} // end namespace moodycamel diff --git a/test/thirdparty/concurrentqueue.h b/test/thirdparty/concurrentqueue.h deleted file mode 100644 index 4b2ad79..0000000 --- a/test/thirdparty/concurrentqueue.h +++ /dev/null @@ -1,3747 +0,0 @@ -// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue. -// An overview, including benchmark results, is provided here: -// http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++ -// The full design is also described in excruciating detail at: -// http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue - -// Simplified BSD license: -// Copyright (c) 2013-2020, Cameron Desrochers. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// - Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// - Redistributions in binary form must reproduce the above copyright notice, this list of -// conditions and the following disclaimer in the documentation and/or other materials -// provided with the distribution. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT -// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Also dual-licensed under the Boost Software License (see LICENSE.md) - -#pragma once - -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) -// Disable -Wconversion warnings (spuriously triggered when Traits::size_t and -// Traits::index_t are set to < 32 bits, causing integer promotion, causing warnings -// upon assigning any computed values) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" - -#ifdef MCDBGQ_USE_RELACY -#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" -#endif -#endif - -#if defined(_MSC_VER) && (!defined(_HAS_CXX17) || !_HAS_CXX17) -// VS2019 with /W4 warns about constant conditional expressions but unless /std=c++17 or higher -// does not support `if constexpr`, so we have no choice but to simply disable the warning -#pragma warning(push) -#pragma warning(disable: 4127) // conditional expression is constant -#endif - -#if defined(__APPLE__) -#include "TargetConditionals.h" -#endif - -#ifdef MCDBGQ_USE_RELACY -#include "relacy/relacy_std.hpp" -#include "relacy_shims.h" -// We only use malloc/free anyway, and the delete macro messes up `= delete` method declarations. -// We'll override the default trait malloc ourselves without a macro. -#undef new -#undef delete -#undef malloc -#undef free -#else -#include // Requires C++11. Sorry VS2010. -#include -#endif -#include // for max_align_t -#include -#include -#include -#include -#include -#include -#include // for CHAR_BIT -#include -#include // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading -#include // used for thread exit synchronization - -// Platform-specific definitions of a numeric thread ID type and an invalid value -namespace moodycamel { namespace details { - template struct thread_id_converter { - typedef thread_id_t thread_id_numeric_size_t; - typedef thread_id_t thread_id_hash_t; - static thread_id_hash_t prehash(thread_id_t const& x) { return x; } - }; -} } -#if defined(MCDBGQ_USE_RELACY) -namespace moodycamel { namespace details { - typedef std::uint32_t thread_id_t; - static const thread_id_t invalid_thread_id = 0xFFFFFFFFU; - static const thread_id_t invalid_thread_id2 = 0xFFFFFFFEU; - static inline thread_id_t thread_id() { return rl::thread_index(); } -} } -#elif defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__) -// No sense pulling in windows.h in a header, we'll manually declare the function -// we use and rely on backwards-compatibility for this not to break -extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void); -namespace moodycamel { namespace details { - static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows"); - typedef std::uint32_t thread_id_t; - static const thread_id_t invalid_thread_id = 0; // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx - static const thread_id_t invalid_thread_id2 = 0xFFFFFFFFU; // Not technically guaranteed to be invalid, but is never used in practice. Note that all Win32 thread IDs are presently multiples of 4. - static inline thread_id_t thread_id() { return static_cast(::GetCurrentThreadId()); } -} } -#elif defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || (defined(__APPLE__) && TARGET_OS_IPHONE) || defined(MOODYCAMEL_NO_THREAD_LOCAL) -namespace moodycamel { namespace details { - static_assert(sizeof(std::thread::id) == 4 || sizeof(std::thread::id) == 8, "std::thread::id is expected to be either 4 or 8 bytes"); - - typedef std::thread::id thread_id_t; - static const thread_id_t invalid_thread_id; // Default ctor creates invalid ID - - // Note we don't define a invalid_thread_id2 since std::thread::id doesn't have one; it's - // only used if MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is defined anyway, which it won't - // be. - static inline thread_id_t thread_id() { return std::this_thread::get_id(); } - - template struct thread_id_size { }; - template<> struct thread_id_size<4> { typedef std::uint32_t numeric_t; }; - template<> struct thread_id_size<8> { typedef std::uint64_t numeric_t; }; - - template<> struct thread_id_converter { - typedef thread_id_size::numeric_t thread_id_numeric_size_t; -#ifndef __APPLE__ - typedef std::size_t thread_id_hash_t; -#else - typedef thread_id_numeric_size_t thread_id_hash_t; -#endif - - static thread_id_hash_t prehash(thread_id_t const& x) - { -#ifndef __APPLE__ - return std::hash()(x); -#else - return *reinterpret_cast(&x); -#endif - } - }; -} } -#else -// Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475 -// In order to get a numeric thread ID in a platform-independent way, we use a thread-local -// static variable's address as a thread identifier :-) -#if defined(__GNUC__) || defined(__INTEL_COMPILER) -#define MOODYCAMEL_THREADLOCAL __thread -#elif defined(_MSC_VER) -#define MOODYCAMEL_THREADLOCAL __declspec(thread) -#else -// Assume C++11 compliant compiler -#define MOODYCAMEL_THREADLOCAL thread_local -#endif -namespace moodycamel { namespace details { - typedef std::uintptr_t thread_id_t; - static const thread_id_t invalid_thread_id = 0; // Address can't be nullptr - static const thread_id_t invalid_thread_id2 = 1; // Member accesses off a null pointer are also generally invalid. Plus it's not aligned. - inline thread_id_t thread_id() { static MOODYCAMEL_THREADLOCAL int x; return reinterpret_cast(&x); } -} } -#endif - -// Constexpr if -#ifndef MOODYCAMEL_CONSTEXPR_IF -#if (defined(_MSC_VER) && defined(_HAS_CXX17) && _HAS_CXX17) || __cplusplus > 201402L -#define MOODYCAMEL_CONSTEXPR_IF if constexpr -#define MOODYCAMEL_MAYBE_UNUSED [[maybe_unused]] -#else -#define MOODYCAMEL_CONSTEXPR_IF if -#define MOODYCAMEL_MAYBE_UNUSED -#endif -#endif - -// Exceptions -#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED -#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__)) -#define MOODYCAMEL_EXCEPTIONS_ENABLED -#endif -#endif -#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED -#define MOODYCAMEL_TRY try -#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__) -#define MOODYCAMEL_RETHROW throw -#define MOODYCAMEL_THROW(expr) throw (expr) -#else -#define MOODYCAMEL_TRY MOODYCAMEL_CONSTEXPR_IF (true) -#define MOODYCAMEL_CATCH(...) else MOODYCAMEL_CONSTEXPR_IF (false) -#define MOODYCAMEL_RETHROW -#define MOODYCAMEL_THROW(expr) -#endif - -#ifndef MOODYCAMEL_NOEXCEPT -#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED) -#define MOODYCAMEL_NOEXCEPT -#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) true -#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) true -#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1800 -// VS2012's std::is_nothrow_[move_]constructible is broken and returns true when it shouldn't :-( -// We have to assume *all* non-trivial constructors may throw on VS2012! -#define MOODYCAMEL_NOEXCEPT _NOEXCEPT -#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value : std::is_trivially_copy_constructible::value) -#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) -#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1900 -#define MOODYCAMEL_NOEXCEPT _NOEXCEPT -#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value || std::is_nothrow_move_constructible::value : std::is_trivially_copy_constructible::value || std::is_nothrow_copy_constructible::value) -#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) -#else -#define MOODYCAMEL_NOEXCEPT noexcept -#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) noexcept(expr) -#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) noexcept(expr) -#endif -#endif - -#ifndef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED -#ifdef MCDBGQ_USE_RELACY -#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED -#else -// VS2013 doesn't support `thread_local`, and MinGW-w64 w/ POSIX threading has a crippling bug: http://sourceforge.net/p/mingw-w64/bugs/445 -// g++ <=4.7 doesn't support thread_local either. -// Finally, iOS/ARM doesn't have support for it either, and g++/ARM allows it to compile but it's unconfirmed to actually work -#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && (!defined(__MINGW32__) && !defined(__MINGW64__) || !defined(__WINPTHREADS_VERSION)) && (!defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && (!defined(__APPLE__) || !TARGET_OS_IPHONE) && !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__) -// Assume `thread_local` is fully supported in all other C++11 compilers/platforms -#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED // tentatively enabled for now; years ago several users report having problems with it on -#endif -#endif -#endif - -// VS2012 doesn't support deleted functions. -// In this case, we declare the function normally but don't define it. A link error will be generated if the function is called. -#ifndef MOODYCAMEL_DELETE_FUNCTION -#if defined(_MSC_VER) && _MSC_VER < 1800 -#define MOODYCAMEL_DELETE_FUNCTION -#else -#define MOODYCAMEL_DELETE_FUNCTION = delete -#endif -#endif - -namespace moodycamel { namespace details { -#ifndef MOODYCAMEL_ALIGNAS -// VS2013 doesn't support alignas or alignof, and align() requires a constant literal -#if defined(_MSC_VER) && _MSC_VER <= 1800 -#define MOODYCAMEL_ALIGNAS(alignment) __declspec(align(alignment)) -#define MOODYCAMEL_ALIGNOF(obj) __alignof(obj) -#define MOODYCAMEL_ALIGNED_TYPE_LIKE(T, obj) typename details::Vs2013Aligned::value, T>::type - template struct Vs2013Aligned { }; // default, unsupported alignment - template struct Vs2013Aligned<1, T> { typedef __declspec(align(1)) T type; }; - template struct Vs2013Aligned<2, T> { typedef __declspec(align(2)) T type; }; - template struct Vs2013Aligned<4, T> { typedef __declspec(align(4)) T type; }; - template struct Vs2013Aligned<8, T> { typedef __declspec(align(8)) T type; }; - template struct Vs2013Aligned<16, T> { typedef __declspec(align(16)) T type; }; - template struct Vs2013Aligned<32, T> { typedef __declspec(align(32)) T type; }; - template struct Vs2013Aligned<64, T> { typedef __declspec(align(64)) T type; }; - template struct Vs2013Aligned<128, T> { typedef __declspec(align(128)) T type; }; - template struct Vs2013Aligned<256, T> { typedef __declspec(align(256)) T type; }; -#else - template struct identity { typedef T type; }; -#define MOODYCAMEL_ALIGNAS(alignment) alignas(alignment) -#define MOODYCAMEL_ALIGNOF(obj) alignof(obj) -#define MOODYCAMEL_ALIGNED_TYPE_LIKE(T, obj) alignas(alignof(obj)) typename details::identity::type -#endif -#endif -} } - - -// TSAN can false report races in lock-free code. To enable TSAN to be used from projects that use this one, -// we can apply per-function compile-time suppression. -// See https://clang.llvm.org/docs/ThreadSanitizer.html#has-feature-thread-sanitizer -#define MOODYCAMEL_NO_TSAN -#if defined(__has_feature) - #if __has_feature(thread_sanitizer) - #undef MOODYCAMEL_NO_TSAN - #define MOODYCAMEL_NO_TSAN __attribute__((no_sanitize("thread"))) - #endif // TSAN -#endif // TSAN - -// Compiler-specific likely/unlikely hints -namespace moodycamel { namespace details { -#if defined(__GNUC__) - static inline bool (likely)(bool x) { return __builtin_expect((x), true); } - static inline bool (unlikely)(bool x) { return __builtin_expect((x), false); } -#else - static inline bool (likely)(bool x) { return x; } - static inline bool (unlikely)(bool x) { return x; } -#endif -} } - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG -#include "internal/concurrentqueue_internal_debug.h" -#endif - -namespace moodycamel { -namespace details { - template - struct const_numeric_max { - static_assert(std::is_integral::value, "const_numeric_max can only be used with integers"); - static const T value = std::numeric_limits::is_signed - ? (static_cast(1) << (sizeof(T) * CHAR_BIT - 1)) - static_cast(1) - : static_cast(-1); - }; - -#if defined(__GLIBCXX__) - typedef ::max_align_t std_max_align_t; // libstdc++ forgot to add it to std:: for a while -#else - typedef std::max_align_t std_max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std:: -#endif - - // Some platforms have incorrectly set max_align_t to a type with <8 bytes alignment even while supporting - // 8-byte aligned scalar values (*cough* 32-bit iOS). Work around this with our own union. See issue #64. - typedef union { - std_max_align_t x; - long long y; - void* z; - } max_align_t; -} - -// Default traits for the ConcurrentQueue. To change some of the -// traits without re-implementing all of them, inherit from this -// struct and shadow the declarations you wish to be different; -// since the traits are used as a template type parameter, the -// shadowed declarations will be used where defined, and the defaults -// otherwise. -struct ConcurrentQueueDefaultTraits -{ - // General-purpose size type. std::size_t is strongly recommended. - typedef std::size_t size_t; - - // The type used for the enqueue and dequeue indices. Must be at least as - // large as size_t. Should be significantly larger than the number of elements - // you expect to hold at once, especially if you have a high turnover rate; - // for example, on 32-bit x86, if you expect to have over a hundred million - // elements or pump several million elements through your queue in a very - // short space of time, using a 32-bit type *may* trigger a race condition. - // A 64-bit int type is recommended in that case, and in practice will - // prevent a race condition no matter the usage of the queue. Note that - // whether the queue is lock-free with a 64-int type depends on the whether - // std::atomic is lock-free, which is platform-specific. - typedef std::size_t index_t; - - // Internally, all elements are enqueued and dequeued from multi-element - // blocks; this is the smallest controllable unit. If you expect few elements - // but many producers, a smaller block size should be favoured. For few producers - // and/or many elements, a larger block size is preferred. A sane default - // is provided. Must be a power of 2. - static const size_t BLOCK_SIZE = 32; - - // For explicit producers (i.e. when using a producer token), the block is - // checked for being empty by iterating through a list of flags, one per element. - // For large block sizes, this is too inefficient, and switching to an atomic - // counter-based approach is faster. The switch is made for block sizes strictly - // larger than this threshold. - static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = 32; - - // How many full blocks can be expected for a single explicit producer? This should - // reflect that number's maximum for optimal performance. Must be a power of 2. - static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 32; - - // How many full blocks can be expected for a single implicit producer? This should - // reflect that number's maximum for optimal performance. Must be a power of 2. - static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 32; - - // The initial size of the hash table mapping thread IDs to implicit producers. - // Note that the hash is resized every time it becomes half full. - // Must be a power of two, and either 0 or at least 1. If 0, implicit production - // (using the enqueue methods without an explicit producer token) is disabled. - static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32; - - // Controls the number of items that an explicit consumer (i.e. one with a token) - // must consume before it causes all consumers to rotate and move on to the next - // internal queue. - static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 256; - - // The maximum number of elements (inclusive) that can be enqueued to a sub-queue. - // Enqueue operations that would cause this limit to be surpassed will fail. Note - // that this limit is enforced at the block level (for performance reasons), i.e. - // it's rounded up to the nearest block size. - static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max::value; - - // The number of times to spin before sleeping when waiting on a semaphore. - // Recommended values are on the order of 1000-10000 unless the number of - // consumer threads exceeds the number of idle cores (in which case try 0-100). - // Only affects instances of the BlockingConcurrentQueue. - static const int MAX_SEMA_SPINS = 10000; - - // Whether to recycle dynamically-allocated blocks into an internal free list or - // not. If false, only pre-allocated blocks (controlled by the constructor - // arguments) will be recycled, and all others will be `free`d back to the heap. - // Note that blocks consumed by explicit producers are only freed on destruction - // of the queue (not following destruction of the token) regardless of this trait. - static const bool RECYCLE_ALLOCATED_BLOCKS = false; - - -#ifndef MCDBGQ_USE_RELACY - // Memory allocation can be customized if needed. - // malloc should return nullptr on failure, and handle alignment like std::malloc. -#if defined(malloc) || defined(free) - // Gah, this is 2015, stop defining macros that break standard code already! - // Work around malloc/free being special macros: - static inline void* WORKAROUND_malloc(size_t size) { return malloc(size); } - static inline void WORKAROUND_free(void* ptr) { return free(ptr); } - static inline void* (malloc)(size_t size) { return WORKAROUND_malloc(size); } - static inline void (free)(void* ptr) { return WORKAROUND_free(ptr); } -#else - static inline void* malloc(size_t size) { return std::malloc(size); } - static inline void free(void* ptr) { return std::free(ptr); } -#endif -#else - // Debug versions when running under the Relacy race detector (ignore - // these in user code) - static inline void* malloc(size_t size) { return rl::rl_malloc(size, $); } - static inline void free(void* ptr) { return rl::rl_free(ptr, $); } -#endif -}; - - -// When producing or consuming many elements, the most efficient way is to: -// 1) Use one of the bulk-operation methods of the queue with a token -// 2) Failing that, use the bulk-operation methods without a token -// 3) Failing that, create a token and use that with the single-item methods -// 4) Failing that, use the single-parameter methods of the queue -// Having said that, don't create tokens willy-nilly -- ideally there should be -// a maximum of one token per thread (of each kind). -struct ProducerToken; -struct ConsumerToken; - -template class ConcurrentQueue; -template class BlockingConcurrentQueue; -class ConcurrentQueueTests; - - -namespace details -{ - struct ConcurrentQueueProducerTypelessBase - { - ConcurrentQueueProducerTypelessBase* next; - std::atomic inactive; - ProducerToken* token; - - ConcurrentQueueProducerTypelessBase() - : next(nullptr), inactive(false), token(nullptr) - { - } - }; - - template struct _hash_32_or_64 { - static inline std::uint32_t hash(std::uint32_t h) - { - // MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp - // Since the thread ID is already unique, all we really want to do is propagate that - // uniqueness evenly across all the bits, so that we can use a subset of the bits while - // reducing collisions significantly - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - return h ^ (h >> 16); - } - }; - template<> struct _hash_32_or_64<1> { - static inline std::uint64_t hash(std::uint64_t h) - { - h ^= h >> 33; - h *= 0xff51afd7ed558ccd; - h ^= h >> 33; - h *= 0xc4ceb9fe1a85ec53; - return h ^ (h >> 33); - } - }; - template struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> { }; - - static inline size_t hash_thread_id(thread_id_t id) - { - static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values"); - return static_cast(hash_32_or_64::thread_id_hash_t)>::hash( - thread_id_converter::prehash(id))); - } - - template - static inline bool circular_less_than(T a, T b) - { - static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "circular_less_than is intended to be used only with unsigned integer types"); - return static_cast(a - b) > static_cast(static_cast(1) << (static_cast(sizeof(T) * CHAR_BIT - 1))); - // Note: extra parens around rhs of operator<< is MSVC bug: https://developercommunity2.visualstudio.com/t/C4554-triggers-when-both-lhs-and-rhs-is/10034931 - // silencing the bug requires #pragma warning(disable: 4554) around the calling code and has no effect when done here. - } - - template - static inline char* align_for(char* ptr) - { - const std::size_t alignment = std::alignment_of::value; - return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; - } - - template - static inline T ceil_to_pow_2(T x) - { - static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "ceil_to_pow_2 is intended to be used only with unsigned integer types"); - - // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - for (std::size_t i = 1; i < sizeof(T); i <<= 1) { - x |= x >> (i << 3); - } - ++x; - return x; - } - - template - static inline void swap_relaxed(std::atomic& left, std::atomic& right) - { - T temp = std::move(left.load(std::memory_order_relaxed)); - left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed); - right.store(std::move(temp), std::memory_order_relaxed); - } - - template - static inline T const& nomove(T const& x) - { - return x; - } - - template - struct nomove_if - { - template - static inline T const& eval(T const& x) - { - return x; - } - }; - - template<> - struct nomove_if - { - template - static inline auto eval(U&& x) - -> decltype(std::forward(x)) - { - return std::forward(x); - } - }; - - template - static inline auto deref_noexcept(It& it) MOODYCAMEL_NOEXCEPT -> decltype(*it) - { - return *it; - } - -#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) - template struct is_trivially_destructible : std::is_trivially_destructible { }; -#else - template struct is_trivially_destructible : std::has_trivial_destructor { }; -#endif - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED -#ifdef MCDBGQ_USE_RELACY - typedef RelacyThreadExitListener ThreadExitListener; - typedef RelacyThreadExitNotifier ThreadExitNotifier; -#else - class ThreadExitNotifier; - - struct ThreadExitListener - { - typedef void (*callback_t)(void*); - callback_t callback; - void* userData; - - ThreadExitListener* next; // reserved for use by the ThreadExitNotifier - ThreadExitNotifier* chain; // reserved for use by the ThreadExitNotifier - }; - - class ThreadExitNotifier - { - public: - static void subscribe(ThreadExitListener* listener) - { - auto& tlsInst = instance(); - std::lock_guard guard(mutex()); - listener->next = tlsInst.tail; - listener->chain = &tlsInst; - tlsInst.tail = listener; - } - - static void unsubscribe(ThreadExitListener* listener) - { - std::lock_guard guard(mutex()); - if (!listener->chain) { - return; // race with ~ThreadExitNotifier - } - auto& tlsInst = *listener->chain; - listener->chain = nullptr; - ThreadExitListener** prev = &tlsInst.tail; - for (auto ptr = tlsInst.tail; ptr != nullptr; ptr = ptr->next) { - if (ptr == listener) { - *prev = ptr->next; - break; - } - prev = &ptr->next; - } - } - - private: - ThreadExitNotifier() : tail(nullptr) { } - ThreadExitNotifier(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; - ThreadExitNotifier& operator=(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; - - ~ThreadExitNotifier() - { - // This thread is about to exit, let everyone know! - assert(this == &instance() && "If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined."); - std::lock_guard guard(mutex()); - for (auto ptr = tail; ptr != nullptr; ptr = ptr->next) { - ptr->chain = nullptr; - ptr->callback(ptr->userData); - } - } - - // Thread-local - static inline ThreadExitNotifier& instance() - { - static thread_local ThreadExitNotifier notifier; - return notifier; - } - - static inline std::mutex& mutex() - { - // Must be static because the ThreadExitNotifier could be destroyed while unsubscribe is called - static std::mutex mutex; - return mutex; - } - - private: - ThreadExitListener* tail; - }; -#endif -#endif - - template struct static_is_lock_free_num { enum { value = 0 }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_CHAR_LOCK_FREE }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_SHORT_LOCK_FREE }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_INT_LOCK_FREE }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_LONG_LOCK_FREE }; }; - template<> struct static_is_lock_free_num { enum { value = ATOMIC_LLONG_LOCK_FREE }; }; - template struct static_is_lock_free : static_is_lock_free_num::type> { }; - template<> struct static_is_lock_free { enum { value = ATOMIC_BOOL_LOCK_FREE }; }; - template struct static_is_lock_free { enum { value = ATOMIC_POINTER_LOCK_FREE }; }; -} - - -struct ProducerToken -{ - template - explicit ProducerToken(ConcurrentQueue& queue); - - template - explicit ProducerToken(BlockingConcurrentQueue& queue); - - ProducerToken(ProducerToken&& other) MOODYCAMEL_NOEXCEPT - : producer(other.producer) - { - other.producer = nullptr; - if (producer != nullptr) { - producer->token = this; - } - } - - inline ProducerToken& operator=(ProducerToken&& other) MOODYCAMEL_NOEXCEPT - { - swap(other); - return *this; - } - - void swap(ProducerToken& other) MOODYCAMEL_NOEXCEPT - { - std::swap(producer, other.producer); - if (producer != nullptr) { - producer->token = this; - } - if (other.producer != nullptr) { - other.producer->token = &other; - } - } - - // A token is always valid unless: - // 1) Memory allocation failed during construction - // 2) It was moved via the move constructor - // (Note: assignment does a swap, leaving both potentially valid) - // 3) The associated queue was destroyed - // Note that if valid() returns true, that only indicates - // that the token is valid for use with a specific queue, - // but not which one; that's up to the user to track. - inline bool valid() const { return producer != nullptr; } - - ~ProducerToken() - { - if (producer != nullptr) { - producer->token = nullptr; - producer->inactive.store(true, std::memory_order_release); - } - } - - // Disable copying and assignment - ProducerToken(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION; - ProducerToken& operator=(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION; - -private: - template friend class ConcurrentQueue; - friend class ConcurrentQueueTests; - -protected: - details::ConcurrentQueueProducerTypelessBase* producer; -}; - - -struct ConsumerToken -{ - template - explicit ConsumerToken(ConcurrentQueue& q); - - template - explicit ConsumerToken(BlockingConcurrentQueue& q); - - ConsumerToken(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT - : initialOffset(other.initialOffset), lastKnownGlobalOffset(other.lastKnownGlobalOffset), itemsConsumedFromCurrent(other.itemsConsumedFromCurrent), currentProducer(other.currentProducer), desiredProducer(other.desiredProducer) - { - } - - inline ConsumerToken& operator=(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT - { - swap(other); - return *this; - } - - void swap(ConsumerToken& other) MOODYCAMEL_NOEXCEPT - { - std::swap(initialOffset, other.initialOffset); - std::swap(lastKnownGlobalOffset, other.lastKnownGlobalOffset); - std::swap(itemsConsumedFromCurrent, other.itemsConsumedFromCurrent); - std::swap(currentProducer, other.currentProducer); - std::swap(desiredProducer, other.desiredProducer); - } - - // Disable copying and assignment - ConsumerToken(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION; - ConsumerToken& operator=(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION; - -private: - template friend class ConcurrentQueue; - friend class ConcurrentQueueTests; - -private: // but shared with ConcurrentQueue - std::uint32_t initialOffset; - std::uint32_t lastKnownGlobalOffset; - std::uint32_t itemsConsumedFromCurrent; - details::ConcurrentQueueProducerTypelessBase* currentProducer; - details::ConcurrentQueueProducerTypelessBase* desiredProducer; -}; - -// Need to forward-declare this swap because it's in a namespace. -// See http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces -template -inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT; - - -template -class ConcurrentQueue -{ -public: - typedef ::moodycamel::ProducerToken producer_token_t; - typedef ::moodycamel::ConsumerToken consumer_token_t; - - typedef typename Traits::index_t index_t; - typedef typename Traits::size_t size_t; - - static const size_t BLOCK_SIZE = static_cast(Traits::BLOCK_SIZE); - static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = static_cast(Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD); - static const size_t EXPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::EXPLICIT_INITIAL_INDEX_SIZE); - static const size_t IMPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::IMPLICIT_INITIAL_INDEX_SIZE); - static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = static_cast(Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE); - static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = static_cast(Traits::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE); -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4307) // + integral constant overflow (that's what the ternary expression is for!) -#pragma warning(disable: 4309) // static_cast: Truncation of constant value -#endif - static const size_t MAX_SUBQUEUE_SIZE = (details::const_numeric_max::value - static_cast(Traits::MAX_SUBQUEUE_SIZE) < BLOCK_SIZE) ? details::const_numeric_max::value : ((static_cast(Traits::MAX_SUBQUEUE_SIZE) + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::size_t must be an unsigned integral type"); - static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::index_t must be an unsigned integral type"); - static_assert(sizeof(index_t) >= sizeof(size_t), "Traits::index_t must be at least as wide as Traits::size_t"); - static_assert((BLOCK_SIZE > 1) && !(BLOCK_SIZE & (BLOCK_SIZE - 1)), "Traits::BLOCK_SIZE must be a power of 2 (and at least 2)"); - static_assert((EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD > 1) && !(EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD & (EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD - 1)), "Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD must be a power of 2 (and greater than 1)"); - static_assert((EXPLICIT_INITIAL_INDEX_SIZE > 1) && !(EXPLICIT_INITIAL_INDEX_SIZE & (EXPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::EXPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); - static_assert((IMPLICIT_INITIAL_INDEX_SIZE > 1) && !(IMPLICIT_INITIAL_INDEX_SIZE & (IMPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::IMPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); - static_assert((INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) || !(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE & (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - 1)), "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be a power of 2"); - static_assert(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0 || INITIAL_IMPLICIT_PRODUCER_HASH_SIZE >= 1, "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be at least 1 (or 0 to disable implicit enqueueing)"); - -public: - // Creates a queue with at least `capacity` element slots; note that the - // actual number of elements that can be inserted without additional memory - // allocation depends on the number of producers and the block size (e.g. if - // the block size is equal to `capacity`, only a single block will be allocated - // up-front, which means only a single producer will be able to enqueue elements - // without an extra allocation -- blocks aren't shared between producers). - // This method is not thread safe -- it is up to the user to ensure that the - // queue is fully constructed before it starts being used by other threads (this - // includes making the memory effects of construction visible, possibly with a - // memory barrier). - explicit ConcurrentQueue(size_t capacity = 32 * BLOCK_SIZE) - : producerListTail(nullptr), - producerCount(0), - initialBlockPoolIndex(0), - nextExplicitConsumerId(0), - globalExplicitConsumerOffset(0) - { - implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); - populate_initial_implicit_producer_hash(); - populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1)); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - // Track all the producers using a fully-resolved typed list for - // each kind; this makes it possible to debug them starting from - // the root queue object (otherwise wacky casts are needed that - // don't compile in the debugger's expression evaluator). - explicitProducers.store(nullptr, std::memory_order_relaxed); - implicitProducers.store(nullptr, std::memory_order_relaxed); -#endif - } - - // Computes the correct amount of pre-allocated blocks for you based - // on the minimum number of elements you want available at any given - // time, and the maximum concurrent number of each type of producer. - ConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers) - : producerListTail(nullptr), - producerCount(0), - initialBlockPoolIndex(0), - nextExplicitConsumerId(0), - globalExplicitConsumerOffset(0) - { - implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); - populate_initial_implicit_producer_hash(); - size_t blocks = (((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + 2 * (maxExplicitProducers + maxImplicitProducers); - populate_initial_block_list(blocks); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - explicitProducers.store(nullptr, std::memory_order_relaxed); - implicitProducers.store(nullptr, std::memory_order_relaxed); -#endif - } - - // Note: The queue should not be accessed concurrently while it's - // being deleted. It's up to the user to synchronize this. - // This method is not thread safe. - ~ConcurrentQueue() - { - // Destroy producers - auto ptr = producerListTail.load(std::memory_order_relaxed); - while (ptr != nullptr) { - auto next = ptr->next_prod(); - if (ptr->token != nullptr) { - ptr->token->producer = nullptr; - } - destroy(ptr); - ptr = next; - } - - // Destroy implicit producer hash tables - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) { - auto hash = implicitProducerHash.load(std::memory_order_relaxed); - while (hash != nullptr) { - auto prev = hash->prev; - if (prev != nullptr) { // The last hash is part of this object and was not allocated dynamically - for (size_t i = 0; i != hash->capacity; ++i) { - hash->entries[i].~ImplicitProducerKVP(); - } - hash->~ImplicitProducerHash(); - (Traits::free)(hash); - } - hash = prev; - } - } - - // Destroy global free list - auto block = freeList.head_unsafe(); - while (block != nullptr) { - auto next = block->freeListNext.load(std::memory_order_relaxed); - if (block->dynamicallyAllocated) { - destroy(block); - } - block = next; - } - - // Destroy initial free list - destroy_array(initialBlockPool, initialBlockPoolSize); - } - - // Disable copying and copy assignment - ConcurrentQueue(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; - ConcurrentQueue& operator=(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; - - // Moving is supported, but note that it is *not* a thread-safe operation. - // Nobody can use the queue while it's being moved, and the memory effects - // of that move must be propagated to other threads before they can use it. - // Note: When a queue is moved, its tokens are still valid but can only be - // used with the destination queue (i.e. semantically they are moved along - // with the queue itself). - ConcurrentQueue(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT - : producerListTail(other.producerListTail.load(std::memory_order_relaxed)), - producerCount(other.producerCount.load(std::memory_order_relaxed)), - initialBlockPoolIndex(other.initialBlockPoolIndex.load(std::memory_order_relaxed)), - initialBlockPool(other.initialBlockPool), - initialBlockPoolSize(other.initialBlockPoolSize), - freeList(std::move(other.freeList)), - nextExplicitConsumerId(other.nextExplicitConsumerId.load(std::memory_order_relaxed)), - globalExplicitConsumerOffset(other.globalExplicitConsumerOffset.load(std::memory_order_relaxed)) - { - // Move the other one into this, and leave the other one as an empty queue - implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); - populate_initial_implicit_producer_hash(); - swap_implicit_producer_hashes(other); - - other.producerListTail.store(nullptr, std::memory_order_relaxed); - other.producerCount.store(0, std::memory_order_relaxed); - other.nextExplicitConsumerId.store(0, std::memory_order_relaxed); - other.globalExplicitConsumerOffset.store(0, std::memory_order_relaxed); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - explicitProducers.store(other.explicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.explicitProducers.store(nullptr, std::memory_order_relaxed); - implicitProducers.store(other.implicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); - other.implicitProducers.store(nullptr, std::memory_order_relaxed); -#endif - - other.initialBlockPoolIndex.store(0, std::memory_order_relaxed); - other.initialBlockPoolSize = 0; - other.initialBlockPool = nullptr; - - reown_producers(); - } - - inline ConcurrentQueue& operator=(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT - { - return swap_internal(other); - } - - // Swaps this queue's state with the other's. Not thread-safe. - // Swapping two queues does not invalidate their tokens, however - // the tokens that were created for one queue must be used with - // only the swapped queue (i.e. the tokens are tied to the - // queue's movable state, not the object itself). - inline void swap(ConcurrentQueue& other) MOODYCAMEL_NOEXCEPT - { - swap_internal(other); - } - -private: - ConcurrentQueue& swap_internal(ConcurrentQueue& other) - { - if (this == &other) { - return *this; - } - - details::swap_relaxed(producerListTail, other.producerListTail); - details::swap_relaxed(producerCount, other.producerCount); - details::swap_relaxed(initialBlockPoolIndex, other.initialBlockPoolIndex); - std::swap(initialBlockPool, other.initialBlockPool); - std::swap(initialBlockPoolSize, other.initialBlockPoolSize); - freeList.swap(other.freeList); - details::swap_relaxed(nextExplicitConsumerId, other.nextExplicitConsumerId); - details::swap_relaxed(globalExplicitConsumerOffset, other.globalExplicitConsumerOffset); - - swap_implicit_producer_hashes(other); - - reown_producers(); - other.reown_producers(); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - details::swap_relaxed(explicitProducers, other.explicitProducers); - details::swap_relaxed(implicitProducers, other.implicitProducers); -#endif - - return *this; - } - -public: - // Enqueues a single item (by copying it). - // Allocates memory if required. Only fails if memory allocation fails (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, - // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(T const& item) - { - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - else return inner_enqueue(item); - } - - // Enqueues a single item (by moving it, if possible). - // Allocates memory if required. Only fails if memory allocation fails (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, - // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(T&& item) - { - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - else return inner_enqueue(std::move(item)); - } - - // Enqueues a single item (by copying it) using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails (or - // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(producer_token_t const& token, T const& item) - { - return inner_enqueue(token, item); - } - - // Enqueues a single item (by moving it, if possible) using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails (or - // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Thread-safe. - inline bool enqueue(producer_token_t const& token, T&& item) - { - return inner_enqueue(token, std::move(item)); - } - - // Enqueues several items. - // Allocates memory if required. Only fails if memory allocation fails (or - // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Note: Use std::make_move_iterator if the elements should be moved instead of copied. - // Thread-safe. - template - bool enqueue_bulk(It itemFirst, size_t count) - { - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - else return inner_enqueue_bulk(itemFirst, count); - } - - // Enqueues several items using an explicit producer token. - // Allocates memory if required. Only fails if memory allocation fails - // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) - { - return inner_enqueue_bulk(token, itemFirst, count); - } - - // Enqueues a single item (by copying it). - // Does not allocate memory. Fails if not enough room to enqueue (or implicit - // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - // is 0). - // Thread-safe. - inline bool try_enqueue(T const& item) - { - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - else return inner_enqueue(item); - } - - // Enqueues a single item (by moving it, if possible). - // Does not allocate memory (except for one-time implicit producer). - // Fails if not enough room to enqueue (or implicit production is - // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). - // Thread-safe. - inline bool try_enqueue(T&& item) - { - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - else return inner_enqueue(std::move(item)); - } - - // Enqueues a single item (by copying it) using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Thread-safe. - inline bool try_enqueue(producer_token_t const& token, T const& item) - { - return inner_enqueue(token, item); - } - - // Enqueues a single item (by moving it, if possible) using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Thread-safe. - inline bool try_enqueue(producer_token_t const& token, T&& item) - { - return inner_enqueue(token, std::move(item)); - } - - // Enqueues several items. - // Does not allocate memory (except for one-time implicit producer). - // Fails if not enough room to enqueue (or implicit production is - // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - bool try_enqueue_bulk(It itemFirst, size_t count) - { - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; - else return inner_enqueue_bulk(itemFirst, count); - } - - // Enqueues several items using an explicit producer token. - // Does not allocate memory. Fails if not enough room to enqueue. - // Note: Use std::make_move_iterator if the elements should be moved - // instead of copied. - // Thread-safe. - template - bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) - { - return inner_enqueue_bulk(token, itemFirst, count); - } - - - - // Attempts to dequeue from the queue. - // Returns false if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - bool try_dequeue(U& item) - { - // Instead of simply trying each producer in turn (which could cause needless contention on the first - // producer), we score them heuristically. - size_t nonEmptyCount = 0; - ProducerBase* best = nullptr; - size_t bestSize = 0; - for (auto ptr = producerListTail.load(std::memory_order_acquire); nonEmptyCount < 3 && ptr != nullptr; ptr = ptr->next_prod()) { - auto size = ptr->size_approx(); - if (size > 0) { - if (size > bestSize) { - bestSize = size; - best = ptr; - } - ++nonEmptyCount; - } - } - - // If there was at least one non-empty queue but it appears empty at the time - // we try to dequeue from it, we need to make sure every queue's been tried - if (nonEmptyCount > 0) { - if ((details::likely)(best->dequeue(item))) { - return true; - } - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - if (ptr != best && ptr->dequeue(item)) { - return true; - } - } - } - return false; - } - - // Attempts to dequeue from the queue. - // Returns false if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // This differs from the try_dequeue(item) method in that this one does - // not attempt to reduce contention by interleaving the order that producer - // streams are dequeued from. So, using this method can reduce overall throughput - // under contention, but will give more predictable results in single-threaded - // consumer scenarios. This is mostly only useful for internal unit tests. - // Never allocates. Thread-safe. - template - bool try_dequeue_non_interleaved(U& item) - { - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - if (ptr->dequeue(item)) { - return true; - } - } - return false; - } - - // Attempts to dequeue from the queue using an explicit consumer token. - // Returns false if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - bool try_dequeue(consumer_token_t& token, U& item) - { - // The idea is roughly as follows: - // Every 256 items from one producer, make everyone rotate (increase the global offset) -> this means the highest efficiency consumer dictates the rotation speed of everyone else, more or less - // If you see that the global offset has changed, you must reset your consumption counter and move to your designated place - // If there's no items where you're supposed to be, keep moving until you find a producer with some items - // If the global offset has not changed but you've run out of items to consume, move over from your current position until you find an producer with something in it - - if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { - if (!update_current_producer_after_rotation(token)) { - return false; - } - } - - // If there was at least one non-empty queue but it appears empty at the time - // we try to dequeue from it, we need to make sure every queue's been tried - if (static_cast(token.currentProducer)->dequeue(item)) { - if (++token.itemsConsumedFromCurrent == EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { - globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); - } - return true; - } - - auto tail = producerListTail.load(std::memory_order_acquire); - auto ptr = static_cast(token.currentProducer)->next_prod(); - if (ptr == nullptr) { - ptr = tail; - } - while (ptr != static_cast(token.currentProducer)) { - if (ptr->dequeue(item)) { - token.currentProducer = ptr; - token.itemsConsumedFromCurrent = 1; - return true; - } - ptr = ptr->next_prod(); - if (ptr == nullptr) { - ptr = tail; - } - } - return false; - } - - // Attempts to dequeue several elements from the queue. - // Returns the number of items actually dequeued. - // Returns 0 if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - size_t try_dequeue_bulk(It itemFirst, size_t max) - { - size_t count = 0; - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - count += ptr->dequeue_bulk(itemFirst, max - count); - if (count == max) { - break; - } - } - return count; - } - - // Attempts to dequeue several elements from the queue using an explicit consumer token. - // Returns the number of items actually dequeued. - // Returns 0 if all producer streams appeared empty at the time they - // were checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max) - { - if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { - if (!update_current_producer_after_rotation(token)) { - return 0; - } - } - - size_t count = static_cast(token.currentProducer)->dequeue_bulk(itemFirst, max); - if (count == max) { - if ((token.itemsConsumedFromCurrent += static_cast(max)) >= EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { - globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); - } - return max; - } - token.itemsConsumedFromCurrent += static_cast(count); - max -= count; - - auto tail = producerListTail.load(std::memory_order_acquire); - auto ptr = static_cast(token.currentProducer)->next_prod(); - if (ptr == nullptr) { - ptr = tail; - } - while (ptr != static_cast(token.currentProducer)) { - auto dequeued = ptr->dequeue_bulk(itemFirst, max); - count += dequeued; - if (dequeued != 0) { - token.currentProducer = ptr; - token.itemsConsumedFromCurrent = static_cast(dequeued); - } - if (dequeued == max) { - break; - } - max -= dequeued; - ptr = ptr->next_prod(); - if (ptr == nullptr) { - ptr = tail; - } - } - return count; - } - - - - // Attempts to dequeue from a specific producer's inner queue. - // If you happen to know which producer you want to dequeue from, this - // is significantly faster than using the general-case try_dequeue methods. - // Returns false if the producer's queue appeared empty at the time it - // was checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - inline bool try_dequeue_from_producer(producer_token_t const& producer, U& item) - { - return static_cast(producer.producer)->dequeue(item); - } - - // Attempts to dequeue several elements from a specific producer's inner queue. - // Returns the number of items actually dequeued. - // If you happen to know which producer you want to dequeue from, this - // is significantly faster than using the general-case try_dequeue methods. - // Returns 0 if the producer's queue appeared empty at the time it - // was checked (so, the queue is likely but not guaranteed to be empty). - // Never allocates. Thread-safe. - template - inline size_t try_dequeue_bulk_from_producer(producer_token_t const& producer, It itemFirst, size_t max) - { - return static_cast(producer.producer)->dequeue_bulk(itemFirst, max); - } - - - // Returns an estimate of the total number of elements currently in the queue. This - // estimate is only accurate if the queue has completely stabilized before it is called - // (i.e. all enqueue and dequeue operations have completed and their memory effects are - // visible on the calling thread, and no further operations start while this method is - // being called). - // Thread-safe. - size_t size_approx() const - { - size_t size = 0; - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - size += ptr->size_approx(); - } - return size; - } - - - // Returns true if the underlying atomic variables used by - // the queue are lock-free (they should be on most platforms). - // Thread-safe. - static constexpr bool is_lock_free() - { - return - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::value == 2 && - details::static_is_lock_free::thread_id_numeric_size_t>::value == 2; - } - - -private: - friend struct ProducerToken; - friend struct ConsumerToken; - struct ExplicitProducer; - friend struct ExplicitProducer; - struct ImplicitProducer; - friend struct ImplicitProducer; - friend class ConcurrentQueueTests; - - enum AllocationMode { CanAlloc, CannotAlloc }; - - - /////////////////////////////// - // Queue methods - /////////////////////////////// - - template - inline bool inner_enqueue(producer_token_t const& token, U&& element) - { - return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue(std::forward(element)); - } - - template - inline bool inner_enqueue(U&& element) - { - auto producer = get_or_add_implicit_producer(); - return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue(std::forward(element)); - } - - template - inline bool inner_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) - { - return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue_bulk(itemFirst, count); - } - - template - inline bool inner_enqueue_bulk(It itemFirst, size_t count) - { - auto producer = get_or_add_implicit_producer(); - return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue_bulk(itemFirst, count); - } - - inline bool update_current_producer_after_rotation(consumer_token_t& token) - { - // Ah, there's been a rotation, figure out where we should be! - auto tail = producerListTail.load(std::memory_order_acquire); - if (token.desiredProducer == nullptr && tail == nullptr) { - return false; - } - auto prodCount = producerCount.load(std::memory_order_relaxed); - auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed); - if ((details::unlikely)(token.desiredProducer == nullptr)) { - // Aha, first time we're dequeueing anything. - // Figure out our local position - // Note: offset is from start, not end, but we're traversing from end -- subtract from count first - std::uint32_t offset = prodCount - 1 - (token.initialOffset % prodCount); - token.desiredProducer = tail; - for (std::uint32_t i = 0; i != offset; ++i) { - token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); - if (token.desiredProducer == nullptr) { - token.desiredProducer = tail; - } - } - } - - std::uint32_t delta = globalOffset - token.lastKnownGlobalOffset; - if (delta >= prodCount) { - delta = delta % prodCount; - } - for (std::uint32_t i = 0; i != delta; ++i) { - token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); - if (token.desiredProducer == nullptr) { - token.desiredProducer = tail; - } - } - - token.lastKnownGlobalOffset = globalOffset; - token.currentProducer = token.desiredProducer; - token.itemsConsumedFromCurrent = 0; - return true; - } - - - /////////////////////////// - // Free list - /////////////////////////// - - template - struct FreeListNode - { - FreeListNode() : freeListRefs(0), freeListNext(nullptr) { } - - std::atomic freeListRefs; - std::atomic freeListNext; - }; - - // A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention, but - // simple and correct (assuming nodes are never freed until after the free list is destroyed), and fairly - // speedy under low contention. - template // N must inherit FreeListNode or have the same fields (and initialization of them) - struct FreeList - { - FreeList() : freeListHead(nullptr) { } - FreeList(FreeList&& other) : freeListHead(other.freeListHead.load(std::memory_order_relaxed)) { other.freeListHead.store(nullptr, std::memory_order_relaxed); } - void swap(FreeList& other) { details::swap_relaxed(freeListHead, other.freeListHead); } - - FreeList(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; - FreeList& operator=(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; - - inline void add(N* node) - { -#ifdef MCDBGQ_NOLOCKFREE_FREELIST - debug::DebugLock lock(mutex); -#endif - // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to - // set it using a fetch_add - if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) { - // Oh look! We were the last ones referencing this node, and we know - // we want to add it to the free list, so let's do it! - add_knowing_refcount_is_zero(node); - } - } - - inline N* try_get() - { -#ifdef MCDBGQ_NOLOCKFREE_FREELIST - debug::DebugLock lock(mutex); -#endif - auto head = freeListHead.load(std::memory_order_acquire); - while (head != nullptr) { - auto prevHead = head; - auto refs = head->freeListRefs.load(std::memory_order_relaxed); - if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, std::memory_order_relaxed)) { - head = freeListHead.load(std::memory_order_acquire); - continue; - } - - // Good, reference count has been incremented (it wasn't at zero), which means we can read the - // next and not worry about it changing between now and the time we do the CAS - auto next = head->freeListNext.load(std::memory_order_relaxed); - if (freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, std::memory_order_relaxed)) { - // Yay, got the node. This means it was on the list, which means shouldBeOnFreeList must be false no - // matter the refcount (because nobody else knows it's been taken off yet, it can't have been put back on). - assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0); - - // Decrease refcount twice, once for our ref, and once for the list's ref - head->freeListRefs.fetch_sub(2, std::memory_order_release); - return head; - } - - // OK, the head must have changed on us, but we still need to decrease the refcount we increased. - // Note that we don't need to release any memory effects, but we do need to ensure that the reference - // count decrement happens-after the CAS on the head. - refs = prevHead->freeListRefs.fetch_sub(1, std::memory_order_acq_rel); - if (refs == SHOULD_BE_ON_FREELIST + 1) { - add_knowing_refcount_is_zero(prevHead); - } - } - - return nullptr; - } - - // Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes) - N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); } - - private: - inline void add_knowing_refcount_is_zero(N* node) - { - // Since the refcount is zero, and nobody can increase it once it's zero (except us, and we run - // only one copy of this method per node at a time, i.e. the single thread case), then we know - // we can safely change the next pointer of the node; however, once the refcount is back above - // zero, then other threads could increase it (happens under heavy contention, when the refcount - // goes to zero in between a load and a refcount increment of a node in try_get, then back up to - // something non-zero, then the refcount increment is done by the other thread) -- so, if the CAS - // to add the node to the actual list fails, decrease the refcount and leave the add operation to - // the next thread who puts the refcount back at zero (which could be us, hence the loop). - auto head = freeListHead.load(std::memory_order_relaxed); - while (true) { - node->freeListNext.store(head, std::memory_order_relaxed); - node->freeListRefs.store(1, std::memory_order_release); - if (!freeListHead.compare_exchange_strong(head, node, std::memory_order_release, std::memory_order_relaxed)) { - // Hmm, the add failed, but we can only try again when the refcount goes back to zero - if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) { - continue; - } - } - return; - } - } - - private: - // Implemented like a stack, but where node order doesn't matter (nodes are inserted out of order under contention) - std::atomic freeListHead; - - static const std::uint32_t REFS_MASK = 0x7FFFFFFF; - static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000; - -#ifdef MCDBGQ_NOLOCKFREE_FREELIST - debug::DebugMutex mutex; -#endif - }; - - - /////////////////////////// - // Block - /////////////////////////// - - enum InnerQueueContext { implicit_context = 0, explicit_context = 1 }; - - struct Block - { - Block() - : next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), dynamicallyAllocated(true) - { -#ifdef MCDBGQ_TRACKMEM - owner = nullptr; -#endif - } - - template - inline bool is_empty() const - { - MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Check flags - for (size_t i = 0; i < BLOCK_SIZE; ++i) { - if (!emptyFlags[i].load(std::memory_order_relaxed)) { - return false; - } - } - - // Aha, empty; make sure we have all other memory effects that happened before the empty flags were set - std::atomic_thread_fence(std::memory_order_acquire); - return true; - } - else { - // Check counter - if (elementsCompletelyDequeued.load(std::memory_order_relaxed) == BLOCK_SIZE) { - std::atomic_thread_fence(std::memory_order_acquire); - return true; - } - assert(elementsCompletelyDequeued.load(std::memory_order_relaxed) <= BLOCK_SIZE); - return false; - } - } - - // Returns true if the block is now empty (does not apply in explicit context) - template - inline bool set_empty(MOODYCAMEL_MAYBE_UNUSED index_t i) - { - MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Set flag - assert(!emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].load(std::memory_order_relaxed)); - emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].store(true, std::memory_order_release); - return false; - } - else { - // Increment counter - auto prevVal = elementsCompletelyDequeued.fetch_add(1, std::memory_order_release); - assert(prevVal < BLOCK_SIZE); - return prevVal == BLOCK_SIZE - 1; - } - } - - // Sets multiple contiguous item statuses to 'empty' (assumes no wrapping and count > 0). - // Returns true if the block is now empty (does not apply in explicit context). - template - inline bool set_many_empty(MOODYCAMEL_MAYBE_UNUSED index_t i, size_t count) - { - MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Set flags - std::atomic_thread_fence(std::memory_order_release); - i = BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1)) - count + 1; - for (size_t j = 0; j != count; ++j) { - assert(!emptyFlags[i + j].load(std::memory_order_relaxed)); - emptyFlags[i + j].store(true, std::memory_order_relaxed); - } - return false; - } - else { - // Increment counter - auto prevVal = elementsCompletelyDequeued.fetch_add(count, std::memory_order_release); - assert(prevVal + count <= BLOCK_SIZE); - return prevVal + count == BLOCK_SIZE; - } - } - - template - inline void set_all_empty() - { - MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Set all flags - for (size_t i = 0; i != BLOCK_SIZE; ++i) { - emptyFlags[i].store(true, std::memory_order_relaxed); - } - } - else { - // Reset counter - elementsCompletelyDequeued.store(BLOCK_SIZE, std::memory_order_relaxed); - } - } - - template - inline void reset_empty() - { - MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { - // Reset flags - for (size_t i = 0; i != BLOCK_SIZE; ++i) { - emptyFlags[i].store(false, std::memory_order_relaxed); - } - } - else { - // Reset counter - elementsCompletelyDequeued.store(0, std::memory_order_relaxed); - } - } - - inline T* operator[](index_t idx) MOODYCAMEL_NOEXCEPT { return static_cast(static_cast(elements)) + static_cast(idx & static_cast(BLOCK_SIZE - 1)); } - inline T const* operator[](index_t idx) const MOODYCAMEL_NOEXCEPT { return static_cast(static_cast(elements)) + static_cast(idx & static_cast(BLOCK_SIZE - 1)); } - - private: - static_assert(std::alignment_of::value <= sizeof(T), "The queue does not support types with an alignment greater than their size at this time"); - MOODYCAMEL_ALIGNED_TYPE_LIKE(char[sizeof(T) * BLOCK_SIZE], T) elements; - public: - Block* next; - std::atomic elementsCompletelyDequeued; - std::atomic emptyFlags[BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD ? BLOCK_SIZE : 1]; - public: - std::atomic freeListRefs; - std::atomic freeListNext; - bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool' - -#ifdef MCDBGQ_TRACKMEM - void* owner; -#endif - }; - static_assert(std::alignment_of::value >= std::alignment_of::value, "Internal error: Blocks must be at least as aligned as the type they are wrapping"); - - -#ifdef MCDBGQ_TRACKMEM -public: - struct MemStats; -private: -#endif - - /////////////////////////// - // Producer base - /////////////////////////// - - struct ProducerBase : public details::ConcurrentQueueProducerTypelessBase - { - ProducerBase(ConcurrentQueue* parent_, bool isExplicit_) : - tailIndex(0), - headIndex(0), - dequeueOptimisticCount(0), - dequeueOvercommit(0), - tailBlock(nullptr), - isExplicit(isExplicit_), - parent(parent_) - { - } - - virtual ~ProducerBase() { } - - template - inline bool dequeue(U& element) - { - if (isExplicit) { - return static_cast(this)->dequeue(element); - } - else { - return static_cast(this)->dequeue(element); - } - } - - template - inline size_t dequeue_bulk(It& itemFirst, size_t max) - { - if (isExplicit) { - return static_cast(this)->dequeue_bulk(itemFirst, max); - } - else { - return static_cast(this)->dequeue_bulk(itemFirst, max); - } - } - - inline ProducerBase* next_prod() const { return static_cast(next); } - - inline size_t size_approx() const - { - auto tail = tailIndex.load(std::memory_order_relaxed); - auto head = headIndex.load(std::memory_order_relaxed); - return details::circular_less_than(head, tail) ? static_cast(tail - head) : 0; - } - - inline index_t getTail() const { return tailIndex.load(std::memory_order_relaxed); } - protected: - std::atomic tailIndex; // Where to enqueue to next - std::atomic headIndex; // Where to dequeue from next - - std::atomic dequeueOptimisticCount; - std::atomic dequeueOvercommit; - - Block* tailBlock; - - public: - bool isExplicit; - ConcurrentQueue* parent; - - protected: -#ifdef MCDBGQ_TRACKMEM - friend struct MemStats; -#endif - }; - - - /////////////////////////// - // Explicit queue - /////////////////////////// - - struct ExplicitProducer : public ProducerBase - { - explicit ExplicitProducer(ConcurrentQueue* parent_) : - ProducerBase(parent_, true), - blockIndex(nullptr), - pr_blockIndexSlotsUsed(0), - pr_blockIndexSize(EXPLICIT_INITIAL_INDEX_SIZE >> 1), - pr_blockIndexFront(0), - pr_blockIndexEntries(nullptr), - pr_blockIndexRaw(nullptr) - { - size_t poolBasedIndexSize = details::ceil_to_pow_2(parent_->initialBlockPoolSize) >> 1; - if (poolBasedIndexSize > pr_blockIndexSize) { - pr_blockIndexSize = poolBasedIndexSize; - } - - new_block_index(0); // This creates an index with double the number of current entries, i.e. EXPLICIT_INITIAL_INDEX_SIZE - } - - ~ExplicitProducer() - { - // Destruct any elements not yet dequeued. - // Since we're in the destructor, we can assume all elements - // are either completely dequeued or completely not (no halfways). - if (this->tailBlock != nullptr) { // Note this means there must be a block index too - // First find the block that's partially dequeued, if any - Block* halfDequeuedBlock = nullptr; - if ((this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) != 0) { - // The head's not on a block boundary, meaning a block somewhere is partially dequeued - // (or the head block is the tail block and was fully dequeued, but the head/tail are still not on a boundary) - size_t i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & (pr_blockIndexSize - 1); - while (details::circular_less_than(pr_blockIndexEntries[i].base + BLOCK_SIZE, this->headIndex.load(std::memory_order_relaxed))) { - i = (i + 1) & (pr_blockIndexSize - 1); - } - assert(details::circular_less_than(pr_blockIndexEntries[i].base, this->headIndex.load(std::memory_order_relaxed))); - halfDequeuedBlock = pr_blockIndexEntries[i].block; - } - - // Start at the head block (note the first line in the loop gives us the head from the tail on the first iteration) - auto block = this->tailBlock; - do { - block = block->next; - if (block->ConcurrentQueue::Block::template is_empty()) { - continue; - } - - size_t i = 0; // Offset into block - if (block == halfDequeuedBlock) { - i = static_cast(this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)); - } - - // Walk through all the items in the block; if this is the tail block, we need to stop when we reach the tail index - auto lastValidIndex = (this->tailIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE : static_cast(this->tailIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)); - while (i != BLOCK_SIZE && (block != this->tailBlock || i != lastValidIndex)) { - (*block)[i++]->~T(); - } - } while (block != this->tailBlock); - } - - // Destroy all blocks that we own - if (this->tailBlock != nullptr) { - auto block = this->tailBlock; - do { - auto nextBlock = block->next; - this->parent->add_block_to_free_list(block); - block = nextBlock; - } while (block != this->tailBlock); - } - - // Destroy the block indices - auto header = static_cast(pr_blockIndexRaw); - while (header != nullptr) { - auto prev = static_cast(header->prev); - header->~BlockIndexHeader(); - (Traits::free)(header); - header = prev; - } - } - - template - inline bool enqueue(U&& element) - { - index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); - index_t newTailIndex = 1 + currentTailIndex; - if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { - // We reached the end of a block, start a new one - auto startBlock = this->tailBlock; - auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; - if (this->tailBlock != nullptr && this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { - // We can re-use the block ahead of us, it's empty! - this->tailBlock = this->tailBlock->next; - this->tailBlock->ConcurrentQueue::Block::template reset_empty(); - - // We'll put the block on the block index (guaranteed to be room since we're conceptually removing the - // last block from it first -- except instead of removing then adding, we can just overwrite). - // Note that there must be a valid block index here, since even if allocation failed in the ctor, - // it would have been re-attempted when adding the first block to the queue; since there is such - // a block, a block index must have been successfully allocated. - } - else { - // Whatever head value we see here is >= the last value we saw here (relatively), - // and <= its current value. Since we have the most recent tail, the head must be - // <= to it. - auto head = this->headIndex.load(std::memory_order_relaxed); - assert(!details::circular_less_than(currentTailIndex, head)); - if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) - || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { - // We can't enqueue in another block because there's not enough leeway -- the - // tail could surpass the head by the time the block fills up! (Or we'll exceed - // the size limit, if the second part of the condition was true.) - return false; - } - // We're going to need a new block; check that the block index has room - if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize) { - // Hmm, the circular block index is already full -- we'll need - // to allocate a new index. Note pr_blockIndexRaw can only be nullptr if - // the initial allocation failed in the constructor. - - MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) { - return false; - } - else if (!new_block_index(pr_blockIndexSlotsUsed)) { - return false; - } - } - - // Insert a new block in the circular linked list - auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); - if (newBlock == nullptr) { - return false; - } -#ifdef MCDBGQ_TRACKMEM - newBlock->owner = this; -#endif - newBlock->ConcurrentQueue::Block::template reset_empty(); - if (this->tailBlock == nullptr) { - newBlock->next = newBlock; - } - else { - newBlock->next = this->tailBlock->next; - this->tailBlock->next = newBlock; - } - this->tailBlock = newBlock; - ++pr_blockIndexSlotsUsed; - } - - MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (static_cast(nullptr)) T(std::forward(element)))) { - // The constructor may throw. We want the element not to appear in the queue in - // that case (without corrupting the queue): - MOODYCAMEL_TRY { - new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); - } - MOODYCAMEL_CATCH (...) { - // Revert change to the current block, but leave the new block available - // for next time - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? this->tailBlock : startBlock; - MOODYCAMEL_RETHROW; - } - } - else { - (void)startBlock; - (void)originalBlockIndexSlotsUsed; - } - - // Add block to block index - auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; - entry.base = currentTailIndex; - entry.block = this->tailBlock; - blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release); - pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); - - MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (static_cast(nullptr)) T(std::forward(element)))) { - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - } - - // Enqueue - new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); - - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - - template - bool dequeue(U& element) - { - auto tail = this->tailIndex.load(std::memory_order_relaxed); - auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); - if (details::circular_less_than(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { - // Might be something to dequeue, let's give it a try - - // Note that this if is purely for performance purposes in the common case when the queue is - // empty and the values are eventually consistent -- we may enter here spuriously. - - // Note that whatever the values of overcommit and tail are, they are not going to change (unless we - // change them) and must be the same value at this point (inside the if) as when the if condition was - // evaluated. - - // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below. - // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in - // the fetch_add below will result in a value at least as recent as that (and therefore at least as large). - // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all - // read-modify-write operations are guaranteed to work on the latest value in the modification order), but - // unfortunately that can't be shown to be correct using only the C++11 standard. - // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case - std::atomic_thread_fence(std::memory_order_acquire); - - // Increment optimistic counter, then check if it went over the boundary - auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); - - // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever - // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now - // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon - // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount. - // However, we can't assert this since both dequeueOptimisticCount and dequeueOvercommit may (independently) - // overflow; in such a case, though, the logic still holds since the difference between the two is maintained. - - // Note that we reload tail here in case it changed; it will be the same value as before or greater, since - // this load is sequenced after (happens after) the earlier load above. This is supported by read-read - // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order - tail = this->tailIndex.load(std::memory_order_acquire); - if ((details::likely)(details::circular_less_than(myDequeueCount - overcommit, tail))) { - // Guaranteed to be at least one element to dequeue! - - // Get the index. Note that since there's guaranteed to be at least one element, this - // will never exceed tail. We need to do an acquire-release fence here since it's possible - // that whatever condition got us to this point was for an earlier enqueued element (that - // we already see the memory effects for), but that by the time we increment somebody else - // has incremented it, and we need to see the memory effects for *that* element, which is - // in such a case is necessarily visible on the thread that incremented it in the first - // place with the more current condition (they must have acquired a tail that is at least - // as recent). - auto index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); - - - // Determine which block the element is in - - auto localBlockIndex = blockIndex.load(std::memory_order_acquire); - auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); - - // We need to be careful here about subtracting and dividing because of index wrap-around. - // When an index wraps, we need to preserve the sign of the offset when dividing it by the - // block size (in order to get a correct signed block count offset in all cases): - auto headBase = localBlockIndex->entries[localBlockIndexHead].base; - auto blockBaseIndex = index & ~static_cast(BLOCK_SIZE - 1); - auto offset = static_cast(static_cast::type>(blockBaseIndex - headBase) / static_cast::type>(BLOCK_SIZE)); - auto block = localBlockIndex->entries[(localBlockIndexHead + offset) & (localBlockIndex->size - 1)].block; - - // Dequeue - auto& el = *((*block)[index]); - if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { - // Make sure the element is still fully dequeued and destroyed even if the assignment - // throws - struct Guard { - Block* block; - index_t index; - - ~Guard() - { - (*block)[index]->~T(); - block->ConcurrentQueue::Block::template set_empty(index); - } - } guard = { block, index }; - - element = std::move(el); // NOLINT - } - else { - element = std::move(el); // NOLINT - el.~T(); // NOLINT - block->ConcurrentQueue::Block::template set_empty(index); - } - - return true; - } - else { - // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent - this->dequeueOvercommit.fetch_add(1, std::memory_order_release); // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write - } - } - - return false; - } - - template - bool MOODYCAMEL_NO_TSAN enqueue_bulk(It itemFirst, size_t count) - { - // First, we need to make sure we have enough room to enqueue all of the elements; - // this means pre-allocating blocks and putting them in the block index (but only if - // all the allocations succeeded). - index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); - auto startBlock = this->tailBlock; - auto originalBlockIndexFront = pr_blockIndexFront; - auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; - - Block* firstAllocatedBlock = nullptr; - - // Figure out how many blocks we'll need to allocate, and do so - size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); - index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); - if (blockBaseDiff > 0) { - // Allocate as many blocks as possible from ahead - while (blockBaseDiff > 0 && this->tailBlock != nullptr && this->tailBlock->next != firstAllocatedBlock && this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { - blockBaseDiff -= static_cast(BLOCK_SIZE); - currentTailIndex += static_cast(BLOCK_SIZE); - - this->tailBlock = this->tailBlock->next; - firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; - - auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; - entry.base = currentTailIndex; - entry.block = this->tailBlock; - pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); - } - - // Now allocate as many blocks as necessary from the block pool - while (blockBaseDiff > 0) { - blockBaseDiff -= static_cast(BLOCK_SIZE); - currentTailIndex += static_cast(BLOCK_SIZE); - - auto head = this->headIndex.load(std::memory_order_relaxed); - assert(!details::circular_less_than(currentTailIndex, head)); - bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); - if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize || full) { - MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) { - // Failed to allocate, undo changes (but keep injected blocks) - pr_blockIndexFront = originalBlockIndexFront; - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; - return false; - } - else if (full || !new_block_index(originalBlockIndexSlotsUsed)) { - // Failed to allocate, undo changes (but keep injected blocks) - pr_blockIndexFront = originalBlockIndexFront; - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; - return false; - } - - // pr_blockIndexFront is updated inside new_block_index, so we need to - // update our fallback value too (since we keep the new index even if we - // later fail) - originalBlockIndexFront = originalBlockIndexSlotsUsed; - } - - // Insert a new block in the circular linked list - auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); - if (newBlock == nullptr) { - pr_blockIndexFront = originalBlockIndexFront; - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; - return false; - } - -#ifdef MCDBGQ_TRACKMEM - newBlock->owner = this; -#endif - newBlock->ConcurrentQueue::Block::template set_all_empty(); - if (this->tailBlock == nullptr) { - newBlock->next = newBlock; - } - else { - newBlock->next = this->tailBlock->next; - this->tailBlock->next = newBlock; - } - this->tailBlock = newBlock; - firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; - - ++pr_blockIndexSlotsUsed; - - auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; - entry.base = currentTailIndex; - entry.block = this->tailBlock; - pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); - } - - // Excellent, all allocations succeeded. Reset each block's emptiness before we fill them up, and - // publish the new block index front - auto block = firstAllocatedBlock; - while (true) { - block->ConcurrentQueue::Block::template reset_empty(); - if (block == this->tailBlock) { - break; - } - block = block->next; - } - - MOODYCAMEL_CONSTEXPR_IF (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (static_cast(nullptr)) T(details::deref_noexcept(itemFirst)))) { - blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); - } - } - - // Enqueue, one block at a time - index_t newTailIndex = startTailIndex + static_cast(count); - currentTailIndex = startTailIndex; - auto endBlock = this->tailBlock; - this->tailBlock = startBlock; - assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0); - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { - this->tailBlock = firstAllocatedBlock; - } - while (true) { - index_t stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - if (details::circular_less_than(newTailIndex, stopIndex)) { - stopIndex = newTailIndex; - } - MOODYCAMEL_CONSTEXPR_IF (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (static_cast(nullptr)) T(details::deref_noexcept(itemFirst)))) { - while (currentTailIndex != stopIndex) { - new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); - } - } - else { - MOODYCAMEL_TRY { - while (currentTailIndex != stopIndex) { - // Must use copy constructor even if move constructor is available - // because we may have to revert if there's an exception. - // Sorry about the horrible templated next line, but it was the only way - // to disable moving *at compile time*, which is important because a type - // may only define a (noexcept) move constructor, and so calls to the - // cctor will not compile, even if they are in an if branch that will never - // be executed - new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if(nullptr)) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); - ++currentTailIndex; - ++itemFirst; - } - } - MOODYCAMEL_CATCH (...) { - // Oh dear, an exception's been thrown -- destroy the elements that - // were enqueued so far and revert the entire bulk operation (we'll keep - // any allocated blocks in our linked list for later, though). - auto constructedStopIndex = currentTailIndex; - auto lastBlockEnqueued = this->tailBlock; - - pr_blockIndexFront = originalBlockIndexFront; - pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; - this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; - - if (!details::is_trivially_destructible::value) { - auto block = startBlock; - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { - block = firstAllocatedBlock; - } - currentTailIndex = startTailIndex; - while (true) { - stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - if (details::circular_less_than(constructedStopIndex, stopIndex)) { - stopIndex = constructedStopIndex; - } - while (currentTailIndex != stopIndex) { - (*block)[currentTailIndex++]->~T(); - } - if (block == lastBlockEnqueued) { - break; - } - block = block->next; - } - } - MOODYCAMEL_RETHROW; - } - } - - if (this->tailBlock == endBlock) { - assert(currentTailIndex == newTailIndex); - break; - } - this->tailBlock = this->tailBlock->next; - } - - MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (static_cast(nullptr)) T(details::deref_noexcept(itemFirst)))) { - if (firstAllocatedBlock != nullptr) - blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); - } - - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - - template - size_t dequeue_bulk(It& itemFirst, size_t max) - { - auto tail = this->tailIndex.load(std::memory_order_relaxed); - auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); - auto desiredCount = static_cast(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit)); - if (details::circular_less_than(0, desiredCount)) { - desiredCount = desiredCount < max ? desiredCount : max; - std::atomic_thread_fence(std::memory_order_acquire); - - auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); - - tail = this->tailIndex.load(std::memory_order_acquire); - auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); - if (details::circular_less_than(0, actualCount)) { - actualCount = desiredCount < actualCount ? desiredCount : actualCount; - if (actualCount < desiredCount) { - this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); - } - - // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this - // will never exceed tail. - auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); - - // Determine which block the first element is in - auto localBlockIndex = blockIndex.load(std::memory_order_acquire); - auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); - - auto headBase = localBlockIndex->entries[localBlockIndexHead].base; - auto firstBlockBaseIndex = firstIndex & ~static_cast(BLOCK_SIZE - 1); - auto offset = static_cast(static_cast::type>(firstBlockBaseIndex - headBase) / static_cast::type>(BLOCK_SIZE)); - auto indexIndex = (localBlockIndexHead + offset) & (localBlockIndex->size - 1); - - // Iterate the blocks and dequeue - auto index = firstIndex; - do { - auto firstIndexInBlock = index; - index_t endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; - auto block = localBlockIndex->entries[indexIndex].block; - if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) { - while (index != endIndex) { - auto& el = *((*block)[index]); - *itemFirst++ = std::move(el); - el.~T(); - ++index; - } - } - else { - MOODYCAMEL_TRY { - while (index != endIndex) { - auto& el = *((*block)[index]); - *itemFirst = std::move(el); - ++itemFirst; - el.~T(); - ++index; - } - } - MOODYCAMEL_CATCH (...) { - // It's too late to revert the dequeue, but we can make sure that all - // the dequeued objects are properly destroyed and the block index - // (and empty count) are properly updated before we propagate the exception - do { - block = localBlockIndex->entries[indexIndex].block; - while (index != endIndex) { - (*block)[index++]->~T(); - } - block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); - indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); - - firstIndexInBlock = index; - endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; - } while (index != firstIndex + actualCount); - - MOODYCAMEL_RETHROW; - } - } - block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); - indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); - } while (index != firstIndex + actualCount); - - return actualCount; - } - else { - // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent - this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); - } - } - - return 0; - } - - private: - struct BlockIndexEntry - { - index_t base; - Block* block; - }; - - struct BlockIndexHeader - { - size_t size; - std::atomic front; // Current slot (not next, like pr_blockIndexFront) - BlockIndexEntry* entries; - void* prev; - }; - - - bool new_block_index(size_t numberOfFilledSlotsToExpose) - { - auto prevBlockSizeMask = pr_blockIndexSize - 1; - - // Create the new block - pr_blockIndexSize <<= 1; - auto newRawPtr = static_cast((Traits::malloc)(sizeof(BlockIndexHeader) + std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * pr_blockIndexSize)); - if (newRawPtr == nullptr) { - pr_blockIndexSize >>= 1; // Reset to allow graceful retry - return false; - } - - auto newBlockIndexEntries = reinterpret_cast(details::align_for(newRawPtr + sizeof(BlockIndexHeader))); - - // Copy in all the old indices, if any - size_t j = 0; - if (pr_blockIndexSlotsUsed != 0) { - auto i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & prevBlockSizeMask; - do { - newBlockIndexEntries[j++] = pr_blockIndexEntries[i]; - i = (i + 1) & prevBlockSizeMask; - } while (i != pr_blockIndexFront); - } - - // Update everything - auto header = new (newRawPtr) BlockIndexHeader; - header->size = pr_blockIndexSize; - header->front.store(numberOfFilledSlotsToExpose - 1, std::memory_order_relaxed); - header->entries = newBlockIndexEntries; - header->prev = pr_blockIndexRaw; // we link the new block to the old one so we can free it later - - pr_blockIndexFront = j; - pr_blockIndexEntries = newBlockIndexEntries; - pr_blockIndexRaw = newRawPtr; - blockIndex.store(header, std::memory_order_release); - - return true; - } - - private: - std::atomic blockIndex; - - // To be used by producer only -- consumer must use the ones in referenced by blockIndex - size_t pr_blockIndexSlotsUsed; - size_t pr_blockIndexSize; - size_t pr_blockIndexFront; // Next slot (not current) - BlockIndexEntry* pr_blockIndexEntries; - void* pr_blockIndexRaw; - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - public: - ExplicitProducer* nextExplicitProducer; - private: -#endif - -#ifdef MCDBGQ_TRACKMEM - friend struct MemStats; -#endif - }; - - - ////////////////////////////////// - // Implicit queue - ////////////////////////////////// - - struct ImplicitProducer : public ProducerBase - { - ImplicitProducer(ConcurrentQueue* parent_) : - ProducerBase(parent_, false), - nextBlockIndexCapacity(IMPLICIT_INITIAL_INDEX_SIZE), - blockIndex(nullptr) - { - new_block_index(); - } - - ~ImplicitProducer() - { - // Note that since we're in the destructor we can assume that all enqueue/dequeue operations - // completed already; this means that all undequeued elements are placed contiguously across - // contiguous blocks, and that only the first and last remaining blocks can be only partially - // empty (all other remaining blocks must be completely full). - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - // Unregister ourselves for thread termination notification - if (!this->inactive.load(std::memory_order_relaxed)) { - details::ThreadExitNotifier::unsubscribe(&threadExitListener); - } -#endif - - // Destroy all remaining elements! - auto tail = this->tailIndex.load(std::memory_order_relaxed); - auto index = this->headIndex.load(std::memory_order_relaxed); - Block* block = nullptr; - assert(index == tail || details::circular_less_than(index, tail)); - bool forceFreeLastBlock = index != tail; // If we enter the loop, then the last (tail) block will not be freed - while (index != tail) { - if ((index & static_cast(BLOCK_SIZE - 1)) == 0 || block == nullptr) { - if (block != nullptr) { - // Free the old block - this->parent->add_block_to_free_list(block); - } - - block = get_block_index_entry_for_index(index)->value.load(std::memory_order_relaxed); - } - - ((*block)[index])->~T(); - ++index; - } - // Even if the queue is empty, there's still one block that's not on the free list - // (unless the head index reached the end of it, in which case the tail will be poised - // to create a new block). - if (this->tailBlock != nullptr && (forceFreeLastBlock || (tail & static_cast(BLOCK_SIZE - 1)) != 0)) { - this->parent->add_block_to_free_list(this->tailBlock); - } - - // Destroy block index - auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); - if (localBlockIndex != nullptr) { - for (size_t i = 0; i != localBlockIndex->capacity; ++i) { - localBlockIndex->index[i]->~BlockIndexEntry(); - } - do { - auto prev = localBlockIndex->prev; - localBlockIndex->~BlockIndexHeader(); - (Traits::free)(localBlockIndex); - localBlockIndex = prev; - } while (localBlockIndex != nullptr); - } - } - - template - inline bool enqueue(U&& element) - { - index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); - index_t newTailIndex = 1 + currentTailIndex; - if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { - // We reached the end of a block, start a new one - auto head = this->headIndex.load(std::memory_order_relaxed); - assert(!details::circular_less_than(currentTailIndex, head)); - if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { - return false; - } -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - // Find out where we'll be inserting this block in the block index - BlockIndexEntry* idxEntry; - if (!insert_block_index_entry(idxEntry, currentTailIndex)) { - return false; - } - - // Get ahold of a new block - auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); - if (newBlock == nullptr) { - rewind_block_index_tail(); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - return false; - } -#ifdef MCDBGQ_TRACKMEM - newBlock->owner = this; -#endif - newBlock->ConcurrentQueue::Block::template reset_empty(); - - MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (static_cast(nullptr)) T(std::forward(element)))) { - // May throw, try to insert now before we publish the fact that we have this new block - MOODYCAMEL_TRY { - new ((*newBlock)[currentTailIndex]) T(std::forward(element)); - } - MOODYCAMEL_CATCH (...) { - rewind_block_index_tail(); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - this->parent->add_block_to_free_list(newBlock); - MOODYCAMEL_RETHROW; - } - } - - // Insert the new block into the index - idxEntry->value.store(newBlock, std::memory_order_relaxed); - - this->tailBlock = newBlock; - - MOODYCAMEL_CONSTEXPR_IF (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (static_cast(nullptr)) T(std::forward(element)))) { - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - } - - // Enqueue - new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); - - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } - - template - bool dequeue(U& element) - { - // See ExplicitProducer::dequeue for rationale and explanation - index_t tail = this->tailIndex.load(std::memory_order_relaxed); - index_t overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); - if (details::circular_less_than(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { - std::atomic_thread_fence(std::memory_order_acquire); - - index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); - tail = this->tailIndex.load(std::memory_order_acquire); - if ((details::likely)(details::circular_less_than(myDequeueCount - overcommit, tail))) { - index_t index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); - - // Determine which block the element is in - auto entry = get_block_index_entry_for_index(index); - - // Dequeue - auto block = entry->value.load(std::memory_order_relaxed); - auto& el = *((*block)[index]); - - if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - // Note: Acquiring the mutex with every dequeue instead of only when a block - // is released is very sub-optimal, but it is, after all, purely debug code. - debug::DebugLock lock(producer->mutex); -#endif - struct Guard { - Block* block; - index_t index; - BlockIndexEntry* entry; - ConcurrentQueue* parent; - - ~Guard() - { - (*block)[index]->~T(); - if (block->ConcurrentQueue::Block::template set_empty(index)) { - entry->value.store(nullptr, std::memory_order_relaxed); - parent->add_block_to_free_list(block); - } - } - } guard = { block, index, entry, this->parent }; - - element = std::move(el); // NOLINT - } - else { - element = std::move(el); // NOLINT - el.~T(); // NOLINT - - if (block->ConcurrentQueue::Block::template set_empty(index)) { - { -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - // Add the block back into the global free pool (and remove from block index) - entry->value.store(nullptr, std::memory_order_relaxed); - } - this->parent->add_block_to_free_list(block); // releases the above store - } - } - - return true; - } - else { - this->dequeueOvercommit.fetch_add(1, std::memory_order_release); - } - } - - return false; - } - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4706) // assignment within conditional expression -#endif - template - bool enqueue_bulk(It itemFirst, size_t count) - { - // First, we need to make sure we have enough room to enqueue all of the elements; - // this means pre-allocating blocks and putting them in the block index (but only if - // all the allocations succeeded). - - // Note that the tailBlock we start off with may not be owned by us any more; - // this happens if it was filled up exactly to the top (setting tailIndex to - // the first index of the next block which is not yet allocated), then dequeued - // completely (putting it on the free list) before we enqueue again. - - index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); - auto startBlock = this->tailBlock; - Block* firstAllocatedBlock = nullptr; - auto endBlock = this->tailBlock; - - // Figure out how many blocks we'll need to allocate, and do so - size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); - index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); - if (blockBaseDiff > 0) { -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - do { - blockBaseDiff -= static_cast(BLOCK_SIZE); - currentTailIndex += static_cast(BLOCK_SIZE); - - // Find out where we'll be inserting this block in the block index - BlockIndexEntry* idxEntry = nullptr; // initialization here unnecessary but compiler can't always tell - Block* newBlock; - bool indexInserted = false; - auto head = this->headIndex.load(std::memory_order_relaxed); - assert(!details::circular_less_than(currentTailIndex, head)); - bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); - - if (full || !(indexInserted = insert_block_index_entry(idxEntry, currentTailIndex)) || (newBlock = this->parent->ConcurrentQueue::template requisition_block()) == nullptr) { - // Index allocation or block allocation failed; revert any other allocations - // and index insertions done so far for this operation - if (indexInserted) { - rewind_block_index_tail(); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - } - currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); - for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { - currentTailIndex += static_cast(BLOCK_SIZE); - idxEntry = get_block_index_entry_for_index(currentTailIndex); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - rewind_block_index_tail(); - } - this->parent->add_blocks_to_free_list(firstAllocatedBlock); - this->tailBlock = startBlock; - - return false; - } - -#ifdef MCDBGQ_TRACKMEM - newBlock->owner = this; -#endif - newBlock->ConcurrentQueue::Block::template reset_empty(); - newBlock->next = nullptr; - - // Insert the new block into the index - idxEntry->value.store(newBlock, std::memory_order_relaxed); - - // Store the chain of blocks so that we can undo if later allocations fail, - // and so that we can find the blocks when we do the actual enqueueing - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr) { - assert(this->tailBlock != nullptr); - this->tailBlock->next = newBlock; - } - this->tailBlock = newBlock; - endBlock = newBlock; - firstAllocatedBlock = firstAllocatedBlock == nullptr ? newBlock : firstAllocatedBlock; - } while (blockBaseDiff > 0); - } - - // Enqueue, one block at a time - index_t newTailIndex = startTailIndex + static_cast(count); - currentTailIndex = startTailIndex; - this->tailBlock = startBlock; - assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0); - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { - this->tailBlock = firstAllocatedBlock; - } - while (true) { - index_t stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - if (details::circular_less_than(newTailIndex, stopIndex)) { - stopIndex = newTailIndex; - } - MOODYCAMEL_CONSTEXPR_IF (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (static_cast(nullptr)) T(details::deref_noexcept(itemFirst)))) { - while (currentTailIndex != stopIndex) { - new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); - } - } - else { - MOODYCAMEL_TRY { - while (currentTailIndex != stopIndex) { - new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if(nullptr)) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); - ++currentTailIndex; - ++itemFirst; - } - } - MOODYCAMEL_CATCH (...) { - auto constructedStopIndex = currentTailIndex; - auto lastBlockEnqueued = this->tailBlock; - - if (!details::is_trivially_destructible::value) { - auto block = startBlock; - if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { - block = firstAllocatedBlock; - } - currentTailIndex = startTailIndex; - while (true) { - stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - if (details::circular_less_than(constructedStopIndex, stopIndex)) { - stopIndex = constructedStopIndex; - } - while (currentTailIndex != stopIndex) { - (*block)[currentTailIndex++]->~T(); - } - if (block == lastBlockEnqueued) { - break; - } - block = block->next; - } - } - - currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); - for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { - currentTailIndex += static_cast(BLOCK_SIZE); - auto idxEntry = get_block_index_entry_for_index(currentTailIndex); - idxEntry->value.store(nullptr, std::memory_order_relaxed); - rewind_block_index_tail(); - } - this->parent->add_blocks_to_free_list(firstAllocatedBlock); - this->tailBlock = startBlock; - MOODYCAMEL_RETHROW; - } - } - - if (this->tailBlock == endBlock) { - assert(currentTailIndex == newTailIndex); - break; - } - this->tailBlock = this->tailBlock->next; - } - this->tailIndex.store(newTailIndex, std::memory_order_release); - return true; - } -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - template - size_t dequeue_bulk(It& itemFirst, size_t max) - { - auto tail = this->tailIndex.load(std::memory_order_relaxed); - auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); - auto desiredCount = static_cast(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit)); - if (details::circular_less_than(0, desiredCount)) { - desiredCount = desiredCount < max ? desiredCount : max; - std::atomic_thread_fence(std::memory_order_acquire); - - auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); - - tail = this->tailIndex.load(std::memory_order_acquire); - auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); - if (details::circular_less_than(0, actualCount)) { - actualCount = desiredCount < actualCount ? desiredCount : actualCount; - if (actualCount < desiredCount) { - this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); - } - - // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this - // will never exceed tail. - auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); - - // Iterate the blocks and dequeue - auto index = firstIndex; - BlockIndexHeader* localBlockIndex; - auto indexIndex = get_block_index_index_for_index(index, localBlockIndex); - do { - auto blockStartIndex = index; - index_t endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; - - auto entry = localBlockIndex->index[indexIndex]; - auto block = entry->value.load(std::memory_order_relaxed); - if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) { - while (index != endIndex) { - auto& el = *((*block)[index]); - *itemFirst++ = std::move(el); - el.~T(); - ++index; - } - } - else { - MOODYCAMEL_TRY { - while (index != endIndex) { - auto& el = *((*block)[index]); - *itemFirst = std::move(el); - ++itemFirst; - el.~T(); - ++index; - } - } - MOODYCAMEL_CATCH (...) { - do { - entry = localBlockIndex->index[indexIndex]; - block = entry->value.load(std::memory_order_relaxed); - while (index != endIndex) { - (*block)[index++]->~T(); - } - - if (block->ConcurrentQueue::Block::template set_many_empty(blockStartIndex, static_cast(endIndex - blockStartIndex))) { -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - entry->value.store(nullptr, std::memory_order_relaxed); - this->parent->add_block_to_free_list(block); - } - indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); - - blockStartIndex = index; - endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); - endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; - } while (index != firstIndex + actualCount); - - MOODYCAMEL_RETHROW; - } - } - if (block->ConcurrentQueue::Block::template set_many_empty(blockStartIndex, static_cast(endIndex - blockStartIndex))) { - { -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - // Note that the set_many_empty above did a release, meaning that anybody who acquires the block - // we're about to free can use it safely since our writes (and reads!) will have happened-before then. - entry->value.store(nullptr, std::memory_order_relaxed); - } - this->parent->add_block_to_free_list(block); // releases the above store - } - indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); - } while (index != firstIndex + actualCount); - - return actualCount; - } - else { - this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); - } - } - - return 0; - } - - private: - // The block size must be > 1, so any number with the low bit set is an invalid block base index - static const index_t INVALID_BLOCK_BASE = 1; - - struct BlockIndexEntry - { - std::atomic key; - std::atomic value; - }; - - struct BlockIndexHeader - { - size_t capacity; - std::atomic tail; - BlockIndexEntry* entries; - BlockIndexEntry** index; - BlockIndexHeader* prev; - }; - - template - inline bool insert_block_index_entry(BlockIndexEntry*& idxEntry, index_t blockStartIndex) - { - auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); // We're the only writer thread, relaxed is OK - if (localBlockIndex == nullptr) { - return false; // this can happen if new_block_index failed in the constructor - } - size_t newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); - idxEntry = localBlockIndex->index[newTail]; - if (idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE || - idxEntry->value.load(std::memory_order_relaxed) == nullptr) { - - idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); - localBlockIndex->tail.store(newTail, std::memory_order_release); - return true; - } - - // No room in the old block index, try to allocate another one! - MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) { - return false; - } - else if (!new_block_index()) { - return false; - } - else { - localBlockIndex = blockIndex.load(std::memory_order_relaxed); - newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); - idxEntry = localBlockIndex->index[newTail]; - assert(idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE); - idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); - localBlockIndex->tail.store(newTail, std::memory_order_release); - return true; - } - } - - inline void rewind_block_index_tail() - { - auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); - localBlockIndex->tail.store((localBlockIndex->tail.load(std::memory_order_relaxed) - 1) & (localBlockIndex->capacity - 1), std::memory_order_relaxed); - } - - inline BlockIndexEntry* get_block_index_entry_for_index(index_t index) const - { - BlockIndexHeader* localBlockIndex; - auto idx = get_block_index_index_for_index(index, localBlockIndex); - return localBlockIndex->index[idx]; - } - - inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader*& localBlockIndex) const - { -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - debug::DebugLock lock(mutex); -#endif - index &= ~static_cast(BLOCK_SIZE - 1); - localBlockIndex = blockIndex.load(std::memory_order_acquire); - auto tail = localBlockIndex->tail.load(std::memory_order_acquire); - auto tailBase = localBlockIndex->index[tail]->key.load(std::memory_order_relaxed); - assert(tailBase != INVALID_BLOCK_BASE); - // Note: Must use division instead of shift because the index may wrap around, causing a negative - // offset, whose negativity we want to preserve - auto offset = static_cast(static_cast::type>(index - tailBase) / static_cast::type>(BLOCK_SIZE)); - size_t idx = (tail + offset) & (localBlockIndex->capacity - 1); - assert(localBlockIndex->index[idx]->key.load(std::memory_order_relaxed) == index && localBlockIndex->index[idx]->value.load(std::memory_order_relaxed) != nullptr); - return idx; - } - - bool new_block_index() - { - auto prev = blockIndex.load(std::memory_order_relaxed); - size_t prevCapacity = prev == nullptr ? 0 : prev->capacity; - auto entryCount = prev == nullptr ? nextBlockIndexCapacity : prevCapacity; - auto raw = static_cast((Traits::malloc)( - sizeof(BlockIndexHeader) + - std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * entryCount + - std::alignment_of::value - 1 + sizeof(BlockIndexEntry*) * nextBlockIndexCapacity)); - if (raw == nullptr) { - return false; - } - - auto header = new (raw) BlockIndexHeader; - auto entries = reinterpret_cast(details::align_for(raw + sizeof(BlockIndexHeader))); - auto index = reinterpret_cast(details::align_for(reinterpret_cast(entries) + sizeof(BlockIndexEntry) * entryCount)); - if (prev != nullptr) { - auto prevTail = prev->tail.load(std::memory_order_relaxed); - auto prevPos = prevTail; - size_t i = 0; - do { - prevPos = (prevPos + 1) & (prev->capacity - 1); - index[i++] = prev->index[prevPos]; - } while (prevPos != prevTail); - assert(i == prevCapacity); - } - for (size_t i = 0; i != entryCount; ++i) { - new (entries + i) BlockIndexEntry; - entries[i].key.store(INVALID_BLOCK_BASE, std::memory_order_relaxed); - index[prevCapacity + i] = entries + i; - } - header->prev = prev; - header->entries = entries; - header->index = index; - header->capacity = nextBlockIndexCapacity; - header->tail.store((prevCapacity - 1) & (nextBlockIndexCapacity - 1), std::memory_order_relaxed); - - blockIndex.store(header, std::memory_order_release); - - nextBlockIndexCapacity <<= 1; - - return true; - } - - private: - size_t nextBlockIndexCapacity; - std::atomic blockIndex; - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - public: - details::ThreadExitListener threadExitListener; - private: -#endif - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - public: - ImplicitProducer* nextImplicitProducer; - private: -#endif - -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX - mutable debug::DebugMutex mutex; -#endif -#ifdef MCDBGQ_TRACKMEM - friend struct MemStats; -#endif - }; - - - ////////////////////////////////// - // Block pool manipulation - ////////////////////////////////// - - void populate_initial_block_list(size_t blockCount) - { - initialBlockPoolSize = blockCount; - if (initialBlockPoolSize == 0) { - initialBlockPool = nullptr; - return; - } - - initialBlockPool = create_array(blockCount); - if (initialBlockPool == nullptr) { - initialBlockPoolSize = 0; - } - for (size_t i = 0; i < initialBlockPoolSize; ++i) { - initialBlockPool[i].dynamicallyAllocated = false; - } - } - - inline Block* try_get_block_from_initial_pool() - { - if (initialBlockPoolIndex.load(std::memory_order_relaxed) >= initialBlockPoolSize) { - return nullptr; - } - - auto index = initialBlockPoolIndex.fetch_add(1, std::memory_order_relaxed); - - return index < initialBlockPoolSize ? (initialBlockPool + index) : nullptr; - } - - inline void add_block_to_free_list(Block* block) - { -#ifdef MCDBGQ_TRACKMEM - block->owner = nullptr; -#endif - if (!Traits::RECYCLE_ALLOCATED_BLOCKS && block->dynamicallyAllocated) { - destroy(block); - } - else { - freeList.add(block); - } - } - - inline void add_blocks_to_free_list(Block* block) - { - while (block != nullptr) { - auto next = block->next; - add_block_to_free_list(block); - block = next; - } - } - - inline Block* try_get_block_from_free_list() - { - return freeList.try_get(); - } - - // Gets a free block from one of the memory pools, or allocates a new one (if applicable) - template - Block* requisition_block() - { - auto block = try_get_block_from_initial_pool(); - if (block != nullptr) { - return block; - } - - block = try_get_block_from_free_list(); - if (block != nullptr) { - return block; - } - - MOODYCAMEL_CONSTEXPR_IF (canAlloc == CanAlloc) { - return create(); - } - else { - return nullptr; - } - } - - -#ifdef MCDBGQ_TRACKMEM - public: - struct MemStats { - size_t allocatedBlocks; - size_t usedBlocks; - size_t freeBlocks; - size_t ownedBlocksExplicit; - size_t ownedBlocksImplicit; - size_t implicitProducers; - size_t explicitProducers; - size_t elementsEnqueued; - size_t blockClassBytes; - size_t queueClassBytes; - size_t implicitBlockIndexBytes; - size_t explicitBlockIndexBytes; - - friend class ConcurrentQueue; - - private: - static MemStats getFor(ConcurrentQueue* q) - { - MemStats stats = { 0 }; - - stats.elementsEnqueued = q->size_approx(); - - auto block = q->freeList.head_unsafe(); - while (block != nullptr) { - ++stats.allocatedBlocks; - ++stats.freeBlocks; - block = block->freeListNext.load(std::memory_order_relaxed); - } - - for (auto ptr = q->producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - bool implicit = dynamic_cast(ptr) != nullptr; - stats.implicitProducers += implicit ? 1 : 0; - stats.explicitProducers += implicit ? 0 : 1; - - if (implicit) { - auto prod = static_cast(ptr); - stats.queueClassBytes += sizeof(ImplicitProducer); - auto head = prod->headIndex.load(std::memory_order_relaxed); - auto tail = prod->tailIndex.load(std::memory_order_relaxed); - auto hash = prod->blockIndex.load(std::memory_order_relaxed); - if (hash != nullptr) { - for (size_t i = 0; i != hash->capacity; ++i) { - if (hash->index[i]->key.load(std::memory_order_relaxed) != ImplicitProducer::INVALID_BLOCK_BASE && hash->index[i]->value.load(std::memory_order_relaxed) != nullptr) { - ++stats.allocatedBlocks; - ++stats.ownedBlocksImplicit; - } - } - stats.implicitBlockIndexBytes += hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry); - for (; hash != nullptr; hash = hash->prev) { - stats.implicitBlockIndexBytes += sizeof(typename ImplicitProducer::BlockIndexHeader) + hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry*); - } - } - for (; details::circular_less_than(head, tail); head += BLOCK_SIZE) { - //auto block = prod->get_block_index_entry_for_index(head); - ++stats.usedBlocks; - } - } - else { - auto prod = static_cast(ptr); - stats.queueClassBytes += sizeof(ExplicitProducer); - auto tailBlock = prod->tailBlock; - bool wasNonEmpty = false; - if (tailBlock != nullptr) { - auto block = tailBlock; - do { - ++stats.allocatedBlocks; - if (!block->ConcurrentQueue::Block::template is_empty() || wasNonEmpty) { - ++stats.usedBlocks; - wasNonEmpty = wasNonEmpty || block != tailBlock; - } - ++stats.ownedBlocksExplicit; - block = block->next; - } while (block != tailBlock); - } - auto index = prod->blockIndex.load(std::memory_order_relaxed); - while (index != nullptr) { - stats.explicitBlockIndexBytes += sizeof(typename ExplicitProducer::BlockIndexHeader) + index->size * sizeof(typename ExplicitProducer::BlockIndexEntry); - index = static_cast(index->prev); - } - } - } - - auto freeOnInitialPool = q->initialBlockPoolIndex.load(std::memory_order_relaxed) >= q->initialBlockPoolSize ? 0 : q->initialBlockPoolSize - q->initialBlockPoolIndex.load(std::memory_order_relaxed); - stats.allocatedBlocks += freeOnInitialPool; - stats.freeBlocks += freeOnInitialPool; - - stats.blockClassBytes = sizeof(Block) * stats.allocatedBlocks; - stats.queueClassBytes += sizeof(ConcurrentQueue); - - return stats; - } - }; - - // For debugging only. Not thread-safe. - MemStats getMemStats() - { - return MemStats::getFor(this); - } - private: - friend struct MemStats; -#endif - - - ////////////////////////////////// - // Producer list manipulation - ////////////////////////////////// - - ProducerBase* recycle_or_create_producer(bool isExplicit) - { -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH - debug::DebugLock lock(implicitProdMutex); -#endif - // Try to re-use one first - for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { - if (ptr->inactive.load(std::memory_order_relaxed) && ptr->isExplicit == isExplicit) { - bool expected = true; - if (ptr->inactive.compare_exchange_strong(expected, /* desired */ false, std::memory_order_acquire, std::memory_order_relaxed)) { - // We caught one! It's been marked as activated, the caller can have it - return ptr; - } - } - } - - return add_producer(isExplicit ? static_cast(create(this)) : create(this)); - } - - ProducerBase* add_producer(ProducerBase* producer) - { - // Handle failed memory allocation - if (producer == nullptr) { - return nullptr; - } - - producerCount.fetch_add(1, std::memory_order_relaxed); - - // Add it to the lock-free list - auto prevTail = producerListTail.load(std::memory_order_relaxed); - do { - producer->next = prevTail; - } while (!producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, std::memory_order_relaxed)); - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - if (producer->isExplicit) { - auto prevTailExplicit = explicitProducers.load(std::memory_order_relaxed); - do { - static_cast(producer)->nextExplicitProducer = prevTailExplicit; - } while (!explicitProducers.compare_exchange_weak(prevTailExplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); - } - else { - auto prevTailImplicit = implicitProducers.load(std::memory_order_relaxed); - do { - static_cast(producer)->nextImplicitProducer = prevTailImplicit; - } while (!implicitProducers.compare_exchange_weak(prevTailImplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); - } -#endif - - return producer; - } - - void reown_producers() - { - // After another instance is moved-into/swapped-with this one, all the - // producers we stole still think their parents are the other queue. - // So fix them up! - for (auto ptr = producerListTail.load(std::memory_order_relaxed); ptr != nullptr; ptr = ptr->next_prod()) { - ptr->parent = this; - } - } - - - ////////////////////////////////// - // Implicit producer hash - ////////////////////////////////// - - struct ImplicitProducerKVP - { - std::atomic key; - ImplicitProducer* value; // No need for atomicity since it's only read by the thread that sets it in the first place - - ImplicitProducerKVP() : value(nullptr) { } - - ImplicitProducerKVP(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT - { - key.store(other.key.load(std::memory_order_relaxed), std::memory_order_relaxed); - value = other.value; - } - - inline ImplicitProducerKVP& operator=(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT - { - swap(other); - return *this; - } - - inline void swap(ImplicitProducerKVP& other) MOODYCAMEL_NOEXCEPT - { - if (this != &other) { - details::swap_relaxed(key, other.key); - std::swap(value, other.value); - } - } - }; - - template - friend void moodycamel::swap(typename ConcurrentQueue::ImplicitProducerKVP&, typename ConcurrentQueue::ImplicitProducerKVP&) MOODYCAMEL_NOEXCEPT; - - struct ImplicitProducerHash - { - size_t capacity; - ImplicitProducerKVP* entries; - ImplicitProducerHash* prev; - }; - - inline void populate_initial_implicit_producer_hash() - { - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) { - return; - } - else { - implicitProducerHashCount.store(0, std::memory_order_relaxed); - auto hash = &initialImplicitProducerHash; - hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; - hash->entries = &initialImplicitProducerHashEntries[0]; - for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) { - initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); - } - hash->prev = nullptr; - implicitProducerHash.store(hash, std::memory_order_relaxed); - } - } - - void swap_implicit_producer_hashes(ConcurrentQueue& other) - { - MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) { - return; - } - else { - // Swap (assumes our implicit producer hash is initialized) - initialImplicitProducerHashEntries.swap(other.initialImplicitProducerHashEntries); - initialImplicitProducerHash.entries = &initialImplicitProducerHashEntries[0]; - other.initialImplicitProducerHash.entries = &other.initialImplicitProducerHashEntries[0]; - - details::swap_relaxed(implicitProducerHashCount, other.implicitProducerHashCount); - - details::swap_relaxed(implicitProducerHash, other.implicitProducerHash); - if (implicitProducerHash.load(std::memory_order_relaxed) == &other.initialImplicitProducerHash) { - implicitProducerHash.store(&initialImplicitProducerHash, std::memory_order_relaxed); - } - else { - ImplicitProducerHash* hash; - for (hash = implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &other.initialImplicitProducerHash; hash = hash->prev) { - continue; - } - hash->prev = &initialImplicitProducerHash; - } - if (other.implicitProducerHash.load(std::memory_order_relaxed) == &initialImplicitProducerHash) { - other.implicitProducerHash.store(&other.initialImplicitProducerHash, std::memory_order_relaxed); - } - else { - ImplicitProducerHash* hash; - for (hash = other.implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &initialImplicitProducerHash; hash = hash->prev) { - continue; - } - hash->prev = &other.initialImplicitProducerHash; - } - } - } - - // Only fails (returns nullptr) if memory allocation fails - ImplicitProducer* get_or_add_implicit_producer() - { - // Note that since the data is essentially thread-local (key is thread ID), - // there's a reduced need for fences (memory ordering is already consistent - // for any individual thread), except for the current table itself. - - // Start by looking for the thread ID in the current and all previous hash tables. - // If it's not found, it must not be in there yet, since this same thread would - // have added it previously to one of the tables that we traversed. - - // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table - -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH - debug::DebugLock lock(implicitProdMutex); -#endif - - auto id = details::thread_id(); - auto hashedId = details::hash_thread_id(id); - - auto mainHash = implicitProducerHash.load(std::memory_order_acquire); - assert(mainHash != nullptr); // silence clang-tidy and MSVC warnings (hash cannot be null) - for (auto hash = mainHash; hash != nullptr; hash = hash->prev) { - // Look for the id in this hash - auto index = hashedId; - while (true) { // Not an infinite loop because at least one slot is free in the hash table - index &= hash->capacity - 1u; - - auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed); - if (probedKey == id) { - // Found it! If we had to search several hashes deep, though, we should lazily add it - // to the current main hash table to avoid the extended search next time. - // Note there's guaranteed to be room in the current hash table since every subsequent - // table implicitly reserves space for all previous tables (there's only one - // implicitProducerHashCount). - auto value = hash->entries[index].value; - if (hash != mainHash) { - index = hashedId; - while (true) { - index &= mainHash->capacity - 1u; - auto empty = details::invalid_thread_id; -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - auto reusable = details::invalid_thread_id2; - if (mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_seq_cst, std::memory_order_relaxed) || - mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_seq_cst, std::memory_order_relaxed)) { -#else - if (mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_seq_cst, std::memory_order_relaxed)) { -#endif - mainHash->entries[index].value = value; - break; - } - ++index; - } - } - - return value; - } - if (probedKey == details::invalid_thread_id) { - break; // Not in this hash table - } - ++index; - } - } - - // Insert! - auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed); - while (true) { - // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) - if (newCount >= (mainHash->capacity >> 1) && !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) { - // We've acquired the resize lock, try to allocate a bigger hash table. - // Note the acquire fence synchronizes with the release fence at the end of this block, and hence when - // we reload implicitProducerHash it must be the most recent version (it only gets changed within this - // locked block). - mainHash = implicitProducerHash.load(std::memory_order_acquire); - if (newCount >= (mainHash->capacity >> 1)) { - size_t newCapacity = mainHash->capacity << 1; - while (newCount >= (newCapacity >> 1)) { - newCapacity <<= 1; - } - auto raw = static_cast((Traits::malloc)(sizeof(ImplicitProducerHash) + std::alignment_of::value - 1 + sizeof(ImplicitProducerKVP) * newCapacity)); - if (raw == nullptr) { - // Allocation failed - implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); - implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); - return nullptr; - } - - auto newHash = new (raw) ImplicitProducerHash; - newHash->capacity = static_cast(newCapacity); - newHash->entries = reinterpret_cast(details::align_for(raw + sizeof(ImplicitProducerHash))); - for (size_t i = 0; i != newCapacity; ++i) { - new (newHash->entries + i) ImplicitProducerKVP; - newHash->entries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); - } - newHash->prev = mainHash; - implicitProducerHash.store(newHash, std::memory_order_release); - implicitProducerHashResizeInProgress.clear(std::memory_order_release); - mainHash = newHash; - } - else { - implicitProducerHashResizeInProgress.clear(std::memory_order_release); - } - } - - // If it's < three-quarters full, add to the old one anyway so that we don't have to wait for the next table - // to finish being allocated by another thread (and if we just finished allocating above, the condition will - // always be true) - if (newCount < (mainHash->capacity >> 1) + (mainHash->capacity >> 2)) { - auto producer = static_cast(recycle_or_create_producer(false)); - if (producer == nullptr) { - implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); - return nullptr; - } - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - producer->threadExitListener.callback = &ConcurrentQueue::implicit_producer_thread_exited_callback; - producer->threadExitListener.userData = producer; - details::ThreadExitNotifier::subscribe(&producer->threadExitListener); -#endif - - auto index = hashedId; - while (true) { - index &= mainHash->capacity - 1u; - auto empty = details::invalid_thread_id; -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - auto reusable = details::invalid_thread_id2; - if (mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_seq_cst, std::memory_order_relaxed)) { - implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); // already counted as a used slot - mainHash->entries[index].value = producer; - break; - } -#endif - if (mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_seq_cst, std::memory_order_relaxed)) { - mainHash->entries[index].value = producer; - break; - } - ++index; - } - return producer; - } - - // Hmm, the old hash is quite full and somebody else is busy allocating a new one. - // We need to wait for the allocating thread to finish (if it succeeds, we add, if not, - // we try to allocate ourselves). - mainHash = implicitProducerHash.load(std::memory_order_acquire); - } - } - -#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED - void implicit_producer_thread_exited(ImplicitProducer* producer) - { - // Remove from hash -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH - debug::DebugLock lock(implicitProdMutex); -#endif - auto hash = implicitProducerHash.load(std::memory_order_acquire); - assert(hash != nullptr); // The thread exit listener is only registered if we were added to a hash in the first place - auto id = details::thread_id(); - auto hashedId = details::hash_thread_id(id); - details::thread_id_t probedKey; - - // We need to traverse all the hashes just in case other threads aren't on the current one yet and are - // trying to add an entry thinking there's a free slot (because they reused a producer) - for (; hash != nullptr; hash = hash->prev) { - auto index = hashedId; - do { - index &= hash->capacity - 1u; - probedKey = id; - if (hash->entries[index].key.compare_exchange_strong(probedKey, details::invalid_thread_id2, std::memory_order_seq_cst, std::memory_order_relaxed)) { - break; - } - ++index; - } while (probedKey != details::invalid_thread_id); // Can happen if the hash has changed but we weren't put back in it yet, or if we weren't added to this hash in the first place - } - - // Mark the queue as being recyclable - producer->inactive.store(true, std::memory_order_release); - } - - static void implicit_producer_thread_exited_callback(void* userData) - { - auto producer = static_cast(userData); - auto queue = producer->parent; - queue->implicit_producer_thread_exited(producer); - } -#endif - - ////////////////////////////////// - // Utility functions - ////////////////////////////////// - - template - static inline void* aligned_malloc(size_t size) - { - MOODYCAMEL_CONSTEXPR_IF (std::alignment_of::value <= std::alignment_of::value) - return (Traits::malloc)(size); - else { - size_t alignment = std::alignment_of::value; - void* raw = (Traits::malloc)(size + alignment - 1 + sizeof(void*)); - if (!raw) - return nullptr; - char* ptr = details::align_for(reinterpret_cast(raw) + sizeof(void*)); - *(reinterpret_cast(ptr) - 1) = raw; - return ptr; - } - } - - template - static inline void aligned_free(void* ptr) - { - MOODYCAMEL_CONSTEXPR_IF (std::alignment_of::value <= std::alignment_of::value) - return (Traits::free)(ptr); - else - (Traits::free)(ptr ? *(reinterpret_cast(ptr) - 1) : nullptr); - } - - template - static inline U* create_array(size_t count) - { - assert(count > 0); - U* p = static_cast(aligned_malloc(sizeof(U) * count)); - if (p == nullptr) - return nullptr; - - for (size_t i = 0; i != count; ++i) - new (p + i) U(); - return p; - } - - template - static inline void destroy_array(U* p, size_t count) - { - if (p != nullptr) { - assert(count > 0); - for (size_t i = count; i != 0; ) - (p + --i)->~U(); - } - aligned_free(p); - } - - template - static inline U* create() - { - void* p = aligned_malloc(sizeof(U)); - return p != nullptr ? new (p) U : nullptr; - } - - template - static inline U* create(A1&& a1) - { - void* p = aligned_malloc(sizeof(U)); - return p != nullptr ? new (p) U(std::forward(a1)) : nullptr; - } - - template - static inline void destroy(U* p) - { - if (p != nullptr) - p->~U(); - aligned_free(p); - } - -private: - std::atomic producerListTail; - std::atomic producerCount; - - std::atomic initialBlockPoolIndex; - Block* initialBlockPool; - size_t initialBlockPoolSize; - -#ifndef MCDBGQ_USEDEBUGFREELIST - FreeList freeList; -#else - debug::DebugFreeList freeList; -#endif - - std::atomic implicitProducerHash; - std::atomic implicitProducerHashCount; // Number of slots logically used - ImplicitProducerHash initialImplicitProducerHash; - std::array initialImplicitProducerHashEntries; - std::atomic_flag implicitProducerHashResizeInProgress; - - std::atomic nextExplicitConsumerId; - std::atomic globalExplicitConsumerOffset; - -#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH - debug::DebugMutex implicitProdMutex; -#endif - -#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG - std::atomic explicitProducers; - std::atomic implicitProducers; -#endif -}; - - -template -ProducerToken::ProducerToken(ConcurrentQueue& queue) - : producer(queue.recycle_or_create_producer(true)) -{ - if (producer != nullptr) { - producer->token = this; - } -} - -template -ProducerToken::ProducerToken(BlockingConcurrentQueue& queue) - : producer(reinterpret_cast*>(&queue)->recycle_or_create_producer(true)) -{ - if (producer != nullptr) { - producer->token = this; - } -} - -template -ConsumerToken::ConsumerToken(ConcurrentQueue& queue) - : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) -{ - initialOffset = queue.nextExplicitConsumerId.fetch_add(1, std::memory_order_release); - lastKnownGlobalOffset = static_cast(-1); -} - -template -ConsumerToken::ConsumerToken(BlockingConcurrentQueue& queue) - : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) -{ - initialOffset = reinterpret_cast*>(&queue)->nextExplicitConsumerId.fetch_add(1, std::memory_order_release); - lastKnownGlobalOffset = static_cast(-1); -} - -template -inline void swap(ConcurrentQueue& a, ConcurrentQueue& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -inline void swap(ProducerToken& a, ProducerToken& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -inline void swap(ConsumerToken& a, ConsumerToken& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -template -inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT -{ - a.swap(b); -} - -} - -#if defined(_MSC_VER) && (!defined(_HAS_CXX17) || !_HAS_CXX17) -#pragma warning(pop) -#endif - -#if defined(__GNUC__) && !defined(__INTEL_COMPILER) -#pragma GCC diagnostic pop -#endif diff --git a/test/thirdparty/lightweightsemaphore.h b/test/thirdparty/lightweightsemaphore.h deleted file mode 100644 index 41ba094..0000000 --- a/test/thirdparty/lightweightsemaphore.h +++ /dev/null @@ -1,425 +0,0 @@ -// Provides an efficient implementation of a semaphore (LightweightSemaphore). -// This is an extension of Jeff Preshing's sempahore implementation (licensed -// under the terms of its separate zlib license) that has been adapted and -// extended by Cameron Desrochers. - -#pragma once - -#include // For std::size_t -#include -#include // For std::make_signed - -#if defined(_WIN32) -// Avoid including windows.h in a header; we only need a handful of -// items, so we'll redeclare them here (this is relatively safe since -// the API generally has to remain stable between Windows versions). -// I know this is an ugly hack but it still beats polluting the global -// namespace with thousands of generic names or adding a .cpp for nothing. -extern "C" { - struct _SECURITY_ATTRIBUTES; - __declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName); - __declspec(dllimport) int __stdcall CloseHandle(void* hObject); - __declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds); - __declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount); -} -#elif defined(__MACH__) -#include -#elif defined(__unix__) -#include - -#if defined(__GLIBC_PREREQ) && defined(_GNU_SOURCE) -#if __GLIBC_PREREQ(2,30) -#define MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC -#endif -#endif -#endif - -namespace moodycamel -{ -namespace details -{ - -// Code in the mpmc_sema namespace below is an adaptation of Jeff Preshing's -// portable + lightweight semaphore implementations, originally from -// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h -// LICENSE: -// Copyright (c) 2015 Jeff Preshing -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgement in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. -#if defined(_WIN32) -class Semaphore -{ -private: - void* m_hSema; - - Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION; - Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION; - -public: - Semaphore(int initialCount = 0) - { - assert(initialCount >= 0); - const long maxLong = 0x7fffffff; - m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr); - assert(m_hSema); - } - - ~Semaphore() - { - CloseHandle(m_hSema); - } - - bool wait() - { - const unsigned long infinite = 0xffffffff; - return WaitForSingleObject(m_hSema, infinite) == 0; - } - - bool try_wait() - { - return WaitForSingleObject(m_hSema, 0) == 0; - } - - bool timed_wait(std::uint64_t usecs) - { - return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0; - } - - void signal(int count = 1) - { - while (!ReleaseSemaphore(m_hSema, count, nullptr)); - } -}; -#elif defined(__MACH__) -//--------------------------------------------------------- -// Semaphore (Apple iOS and OSX) -// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html -//--------------------------------------------------------- -class Semaphore -{ -private: - semaphore_t m_sema; - - Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION; - Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION; - -public: - Semaphore(int initialCount = 0) - { - assert(initialCount >= 0); - kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount); - assert(rc == KERN_SUCCESS); - (void)rc; - } - - ~Semaphore() - { - semaphore_destroy(mach_task_self(), m_sema); - } - - bool wait() - { - return semaphore_wait(m_sema) == KERN_SUCCESS; - } - - bool try_wait() - { - return timed_wait(0); - } - - bool timed_wait(std::uint64_t timeout_usecs) - { - mach_timespec_t ts; - ts.tv_sec = static_cast(timeout_usecs / 1000000); - ts.tv_nsec = static_cast((timeout_usecs % 1000000) * 1000); - - // added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html - kern_return_t rc = semaphore_timedwait(m_sema, ts); - return rc == KERN_SUCCESS; - } - - void signal() - { - while (semaphore_signal(m_sema) != KERN_SUCCESS); - } - - void signal(int count) - { - while (count-- > 0) - { - while (semaphore_signal(m_sema) != KERN_SUCCESS); - } - } -}; -#elif defined(__unix__) -//--------------------------------------------------------- -// Semaphore (POSIX, Linux) -//--------------------------------------------------------- -class Semaphore -{ -private: - sem_t m_sema; - - Semaphore(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION; - Semaphore& operator=(const Semaphore& other) MOODYCAMEL_DELETE_FUNCTION; - -public: - Semaphore(int initialCount = 0) - { - assert(initialCount >= 0); - int rc = sem_init(&m_sema, 0, static_cast(initialCount)); - assert(rc == 0); - (void)rc; - } - - ~Semaphore() - { - sem_destroy(&m_sema); - } - - bool wait() - { - // http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error - int rc; - do { - rc = sem_wait(&m_sema); - } while (rc == -1 && errno == EINTR); - return rc == 0; - } - - bool try_wait() - { - int rc; - do { - rc = sem_trywait(&m_sema); - } while (rc == -1 && errno == EINTR); - return rc == 0; - } - - bool timed_wait(std::uint64_t usecs) - { - struct timespec ts; - const int usecs_in_1_sec = 1000000; - const int nsecs_in_1_sec = 1000000000; -#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC - clock_gettime(CLOCK_MONOTONIC, &ts); -#else - clock_gettime(CLOCK_REALTIME, &ts); -#endif - ts.tv_sec += (time_t)(usecs / usecs_in_1_sec); - ts.tv_nsec += (long)(usecs % usecs_in_1_sec) * 1000; - // sem_timedwait bombs if you have more than 1e9 in tv_nsec - // so we have to clean things up before passing it in - if (ts.tv_nsec >= nsecs_in_1_sec) { - ts.tv_nsec -= nsecs_in_1_sec; - ++ts.tv_sec; - } - - int rc; - do { -#ifdef MOODYCAMEL_LIGHTWEIGHTSEMAPHORE_MONOTONIC - rc = sem_clockwait(&m_sema, CLOCK_MONOTONIC, &ts); -#else - rc = sem_timedwait(&m_sema, &ts); -#endif - } while (rc == -1 && errno == EINTR); - return rc == 0; - } - - void signal() - { - while (sem_post(&m_sema) == -1); - } - - void signal(int count) - { - while (count-- > 0) - { - while (sem_post(&m_sema) == -1); - } - } -}; -#else -#error Unsupported platform! (No semaphore wrapper available) -#endif - -} // end namespace details - - -//--------------------------------------------------------- -// LightweightSemaphore -//--------------------------------------------------------- -class LightweightSemaphore -{ -public: - typedef std::make_signed::type ssize_t; - -private: - std::atomic m_count; - details::Semaphore m_sema; - int m_maxSpins; - - bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1) - { - ssize_t oldCount; - int spin = m_maxSpins; - while (--spin >= 0) - { - oldCount = m_count.load(std::memory_order_relaxed); - if ((oldCount > 0) && m_count.compare_exchange_strong(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed)) - return true; - std::atomic_signal_fence(std::memory_order_acquire); // Prevent the compiler from collapsing the loop. - } - oldCount = m_count.fetch_sub(1, std::memory_order_acquire); - if (oldCount > 0) - return true; - if (timeout_usecs < 0) - { - if (m_sema.wait()) - return true; - } - if (timeout_usecs > 0 && m_sema.timed_wait((std::uint64_t)timeout_usecs)) - return true; - // At this point, we've timed out waiting for the semaphore, but the - // count is still decremented indicating we may still be waiting on - // it. So we have to re-adjust the count, but only if the semaphore - // wasn't signaled enough times for us too since then. If it was, we - // need to release the semaphore too. - while (true) - { - oldCount = m_count.load(std::memory_order_acquire); - if (oldCount >= 0 && m_sema.try_wait()) - return true; - if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed)) - return false; - } - } - - ssize_t waitManyWithPartialSpinning(ssize_t max, std::int64_t timeout_usecs = -1) - { - assert(max > 0); - ssize_t oldCount; - int spin = m_maxSpins; - while (--spin >= 0) - { - oldCount = m_count.load(std::memory_order_relaxed); - if (oldCount > 0) - { - ssize_t newCount = oldCount > max ? oldCount - max : 0; - if (m_count.compare_exchange_strong(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed)) - return oldCount - newCount; - } - std::atomic_signal_fence(std::memory_order_acquire); - } - oldCount = m_count.fetch_sub(1, std::memory_order_acquire); - if (oldCount <= 0) - { - if ((timeout_usecs == 0) || (timeout_usecs < 0 && !m_sema.wait()) || (timeout_usecs > 0 && !m_sema.timed_wait((std::uint64_t)timeout_usecs))) - { - while (true) - { - oldCount = m_count.load(std::memory_order_acquire); - if (oldCount >= 0 && m_sema.try_wait()) - break; - if (oldCount < 0 && m_count.compare_exchange_strong(oldCount, oldCount + 1, std::memory_order_relaxed, std::memory_order_relaxed)) - return 0; - } - } - } - if (max > 1) - return 1 + tryWaitMany(max - 1); - return 1; - } - -public: - LightweightSemaphore(ssize_t initialCount = 0, int maxSpins = 10000) : m_count(initialCount), m_maxSpins(maxSpins) - { - assert(initialCount >= 0); - assert(maxSpins >= 0); - } - - bool tryWait() - { - ssize_t oldCount = m_count.load(std::memory_order_relaxed); - while (oldCount > 0) - { - if (m_count.compare_exchange_weak(oldCount, oldCount - 1, std::memory_order_acquire, std::memory_order_relaxed)) - return true; - } - return false; - } - - bool wait() - { - return tryWait() || waitWithPartialSpinning(); - } - - bool wait(std::int64_t timeout_usecs) - { - return tryWait() || waitWithPartialSpinning(timeout_usecs); - } - - // Acquires between 0 and (greedily) max, inclusive - ssize_t tryWaitMany(ssize_t max) - { - assert(max >= 0); - ssize_t oldCount = m_count.load(std::memory_order_relaxed); - while (oldCount > 0) - { - ssize_t newCount = oldCount > max ? oldCount - max : 0; - if (m_count.compare_exchange_weak(oldCount, newCount, std::memory_order_acquire, std::memory_order_relaxed)) - return oldCount - newCount; - } - return 0; - } - - // Acquires at least one, and (greedily) at most max - ssize_t waitMany(ssize_t max, std::int64_t timeout_usecs) - { - assert(max >= 0); - ssize_t result = tryWaitMany(max); - if (result == 0 && max > 0) - result = waitManyWithPartialSpinning(max, timeout_usecs); - return result; - } - - ssize_t waitMany(ssize_t max) - { - ssize_t result = waitMany(max, -1); - assert(result > 0); - return result; - } - - void signal(ssize_t count = 1) - { - assert(count >= 0); - ssize_t oldCount = m_count.fetch_add(count, std::memory_order_release); - ssize_t toRelease = -oldCount < count ? -oldCount : count; - if (toRelease > 0) - { - m_sema.signal((int)toRelease); - } - } - - std::size_t availableApprox() const - { - ssize_t count = m_count.load(std::memory_order_relaxed); - return count > 0 ? static_cast(count) : 0; - } -}; - -} // end namespace moodycamel