From bec699f0bda2c67bd5ffb777b2fd8c307681c0ae Mon Sep 17 00:00:00 2001 From: David Chavez Date: Wed, 5 Jun 2024 01:12:43 +0200 Subject: [PATCH] Migrate to N64ModernRuntime (#354) --- .github/workflows/validate.yml | 4 +- .gitignore | 2 + .gitmodules | 3 + CMakeLists.txt | 60 +- include/ovl_patches.hpp | 9 + include/recomp.h | 327 --- include/recomp_config.h | 54 - include/recomp_game.h | 40 - include/recomp_helpers.h | 50 - include/recomp_input.h | 42 +- include/recomp_overlays.h | 10 - include/recomp_ui.h | 4 +- include/rsp.h | 94 - include/rsp_vu.h | 203 -- include/rsp_vu_impl.h | 1537 ------------ include/rt64_layer.h | 50 - include/sections.h | 23 - include/zelda_config.h | 91 + include/{recomp_debug.h => zelda_debug.h} | 6 +- include/zelda_game.h | 9 + include/{recomp_sound.h => zelda_sound.h} | 6 +- lib/N64ModernRuntime | 1 + patches/Makefile | 2 +- patches/patch_helpers.h | 2 +- src/game/config.cpp | 77 +- src/game/controls.cpp | 4 +- src/game/debug.cpp | 10 +- src/game/input.cpp | 26 +- src/game/quicksaving.cpp | 10 +- src/game/recomp_api.cpp | 40 +- src/game/scene_table.cpp | 4 +- src/main/main.cpp | 82 +- src/main/register_overlays.cpp | 19 + src/main/register_patches.cpp | 12 + src/recomp/ai.cpp | 29 - src/recomp/cont.cpp | 155 -- src/recomp/dp.cpp | 44 - src/recomp/eep.cpp | 49 - src/recomp/euc-jp.cpp | 2587 --------------------- src/recomp/euc-jp.h | 11 - src/recomp/files.cpp | 51 - src/recomp/flash.cpp | 141 -- src/recomp/math_routines.cpp | 80 - src/recomp/overlays.cpp | 165 -- src/recomp/pak.cpp | 35 - src/recomp/patch_loading.cpp | 11 - src/recomp/pi.cpp | 302 --- src/recomp/print.cpp | 72 - src/recomp/recomp.cpp | 445 ---- src/recomp/sp.cpp | 50 - src/recomp/ultra_stubs.cpp | 45 - src/recomp/ultra_translation.cpp | 163 -- src/recomp/vi.cpp | 47 - src/ui/ui_color_hack.cpp | 2 +- src/ui/ui_config.cpp | 146 +- src/ui/ui_launcher.cpp | 94 +- src/ui/ui_renderer.cpp | 105 +- ultramodern/audio.cpp | 67 - ultramodern/config.hpp | 86 - ultramodern/events.cpp | 620 ----- ultramodern/mesgqueue.cpp | 188 -- ultramodern/misc_ultra.cpp | 22 - ultramodern/port_main.c | 83 - ultramodern/rt64_layer.cpp | 319 --- ultramodern/scheduling.cpp | 38 - ultramodern/task_win32.cpp | 13 - ultramodern/threadqueue.cpp | 66 - ultramodern/threads.cpp | 346 --- ultramodern/timer.cpp | 224 -- ultramodern/ultra64.h | 260 --- ultramodern/ultrainit.cpp | 14 - ultramodern/ultramodern.hpp | 166 -- 72 files changed, 513 insertions(+), 9741 deletions(-) create mode 100644 include/ovl_patches.hpp delete mode 100644 include/recomp.h delete mode 100644 include/recomp_config.h delete mode 100644 include/recomp_game.h delete mode 100644 include/recomp_helpers.h delete mode 100644 include/recomp_overlays.h delete mode 100644 include/rsp.h delete mode 100644 include/rsp_vu.h delete mode 100644 include/rsp_vu_impl.h delete mode 100644 include/rt64_layer.h delete mode 100644 include/sections.h create mode 100644 include/zelda_config.h rename include/{recomp_debug.h => zelda_debug.h} (85%) create mode 100644 include/zelda_game.h rename include/{recomp_sound.h => zelda_sound.h} (78%) create mode 160000 lib/N64ModernRuntime create mode 100644 src/main/register_overlays.cpp create mode 100644 src/main/register_patches.cpp delete mode 100644 src/recomp/ai.cpp delete mode 100644 src/recomp/cont.cpp delete mode 100644 src/recomp/dp.cpp delete mode 100644 src/recomp/eep.cpp delete mode 100644 src/recomp/euc-jp.cpp delete mode 100644 src/recomp/euc-jp.h delete mode 100644 src/recomp/files.cpp delete mode 100644 src/recomp/flash.cpp delete mode 100644 src/recomp/math_routines.cpp delete mode 100644 src/recomp/overlays.cpp delete mode 100644 src/recomp/pak.cpp delete mode 100644 src/recomp/patch_loading.cpp delete mode 100644 src/recomp/pi.cpp delete mode 100644 src/recomp/print.cpp delete mode 100644 src/recomp/recomp.cpp delete mode 100644 src/recomp/sp.cpp delete mode 100644 src/recomp/ultra_stubs.cpp delete mode 100644 src/recomp/ultra_translation.cpp delete mode 100644 src/recomp/vi.cpp delete mode 100644 ultramodern/audio.cpp delete mode 100644 ultramodern/config.hpp delete mode 100644 ultramodern/events.cpp delete mode 100644 ultramodern/mesgqueue.cpp delete mode 100644 ultramodern/misc_ultra.cpp delete mode 100644 ultramodern/port_main.c delete mode 100644 ultramodern/rt64_layer.cpp delete mode 100644 ultramodern/scheduling.cpp delete mode 100644 ultramodern/task_win32.cpp delete mode 100644 ultramodern/threadqueue.cpp delete mode 100644 ultramodern/threads.cpp delete mode 100644 ultramodern/timer.cpp delete mode 100644 ultramodern/ultra64.h delete mode 100644 ultramodern/ultrainit.cpp delete mode 100644 ultramodern/ultramodern.hpp diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index b4a571d..7194331 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -56,7 +56,7 @@ jobs: run: | git clone https://github.com/Mr-Wiseguy/N64Recomp.git --recurse-submodules N64RecompSource cd N64RecompSource - git checkout 6eb7d5bd3ee7f0b79f3fd7adbe931dccbacf7e1b + git checkout 8dfed04919b7bfdd0fd34ff049eed7020dea0d71 git submodule update --init --recursive # enable ccache @@ -130,7 +130,7 @@ jobs: run: | git clone https://github.com/Mr-Wiseguy/N64Recomp.git --recurse-submodules N64RecompSource cd N64RecompSource - git checkout 6eb7d5bd3ee7f0b79f3fd7adbe931dccbacf7e1b + git checkout 8dfed04919b7bfdd0fd34ff049eed7020dea0d71 git submodule update --init --recursive # enable ccache diff --git a/.gitignore b/.gitignore index 079b415..a718c25 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # VSCode file settings .vscode/settings.json .vscode/c_cpp_properties.json +.vscode/launch.json # Input elf and rom files *.elf @@ -56,3 +57,4 @@ node_modules/ # Recompiler Linux binary N64Recomp +.DS_Store diff --git a/.gitmodules b/.gitmodules index 34f6676..383eb4e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "lib/sse2neon"] path = lib/sse2neon url = https://github.com/DLTcollab/sse2neon.git +[submodule "lib/N64ModernRuntime"] + path = lib/N64ModernRuntime + url = git@github.com:N64Recomp/N64ModernRuntime.git diff --git a/CMakeLists.txt b/CMakeLists.txt index f2be1d7..6e281c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,10 +13,6 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") cmake_policy(SET CMP0135 NEW) endif() -if(UNIX AND NOT APPLE) - set(LINUX TRUE) -endif() - set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) @@ -35,6 +31,8 @@ add_subdirectory(${CMAKE_SOURCE_DIR}/lib/lunasvg) SET(ENABLE_SVG_PLUGIN ON CACHE BOOL "" FORCE) add_subdirectory(${CMAKE_SOURCE_DIR}/lib/RmlUi) +add_subdirectory(${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime) + target_include_directories(rt64 PRIVATE ${CMAKE_BINARY_DIR}/rt64/src) # RecompiledFuncs - Library containing the primary recompiler output @@ -48,6 +46,8 @@ target_compile_options(RecompiledFuncs PRIVATE target_include_directories(RecompiledFuncs PRIVATE ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/ultramodern/include + ${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/librecomp/include ) file(GLOB FUNC_C_SOURCES ${CMAKE_SOURCE_DIR}/RecompiledFuncs/*.c) @@ -65,6 +65,8 @@ target_compile_options(PatchesLib PRIVATE target_include_directories(PatchesLib PRIVATE ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/ultramodern/include + ${CMAKE_SOURCE_DIR}/lib/N64ModernRuntime/librecomp/include ) target_sources(PatchesLib PRIVATE @@ -92,8 +94,8 @@ add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/RecompiledPatches/patches.c ${CMAKE_SOURCE_DIR}/RecompiledPatches/recomp_overlays.inl ${CMAKE_SOURCE_DIR}/RecompiledPatches/funcs.h - ${CMAKE_SOURCE_DIR}/src/recomp/patch_loading.cpp - COMMAND ./N64Recomp patches.toml && ${CMAKE_COMMAND} -E touch ${CMAKE_SOURCE_DIR}/src/recomp/patch_loading.cpp + # TODO: Look into why modifying patches requires two builds to take + COMMAND ./N64Recomp patches.toml WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} DEPENDS ${CMAKE_SOURCE_DIR}/patches/patches.bin ) @@ -112,40 +114,10 @@ if (EXISTS ${CMAKE_SOURCE_DIR}/shadercache/mm_shader_cache.bin) endif() set (SOURCES - ${CMAKE_SOURCE_DIR}/ultramodern/audio.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/events.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/mesgqueue.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/misc_ultra.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/port_main.c - ${CMAKE_SOURCE_DIR}/ultramodern/scheduling.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/threadqueue.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/task_win32.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/threads.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/timer.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/ultrainit.cpp - ${CMAKE_SOURCE_DIR}/ultramodern/rt64_layer.cpp - - ${CMAKE_SOURCE_DIR}/src/recomp/ai.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/cont.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/dp.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/eep.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/euc-jp.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/files.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/flash.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/math_routines.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/overlays.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/patch_loading.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/pak.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/pi.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/ultra_stubs.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/ultra_translation.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/print.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/recomp.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/sp.cpp - ${CMAKE_SOURCE_DIR}/src/recomp/vi.cpp - ${CMAKE_SOURCE_DIR}/src/main/main.cpp - + ${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp + ${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp + ${CMAKE_SOURCE_DIR}/src/game/input.cpp ${CMAKE_SOURCE_DIR}/src/game/controls.cpp ${CMAKE_SOURCE_DIR}/src/game/config.cpp @@ -225,7 +197,7 @@ if (WIN32) "${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/x64/dxil.dll" "${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/dxc/bin/x64/dxcompiler.dll" $) - + set_target_properties( Zelda64Recompiled PROPERTIES @@ -238,10 +210,10 @@ if (WIN32) target_sources(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/icons/app.rc) endif() -if (LINUX) +if (CMAKE_SYSTEM_NAME MATCHES "Linux") find_package(SDL2 REQUIRED) find_package(X11 REQUIRED) - + # Generate icon_bytes.c from the app icon PNG. add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h COMMAND file_to_c ${CMAKE_SOURCE_DIR}/icons/512.png icon_bytes ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.c ${CMAKE_CURRENT_BINARY_DIR}/icon_bytes.h @@ -274,7 +246,7 @@ if (LINUX) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED) - + target_link_libraries(Zelda64Recompiled PRIVATE "-latomic -static-libstdc++" ${CMAKE_DL_LIBS} Threads::Threads) endif() @@ -282,6 +254,8 @@ target_link_libraries(Zelda64Recompiled PRIVATE PatchesLib RecompiledFuncs SDL2 + librecomp + ultramodern rt64 RmlCore RmlDebugger diff --git a/include/ovl_patches.hpp b/include/ovl_patches.hpp new file mode 100644 index 0000000..d863c10 --- /dev/null +++ b/include/ovl_patches.hpp @@ -0,0 +1,9 @@ +#ifndef __OVL_PATCHES_HPP__ +#define __OVL_PATCHES_HPP__ + +namespace zelda64 { + void register_overlays(); + void register_patches(); +} + +#endif diff --git a/include/recomp.h b/include/recomp.h deleted file mode 100644 index bb40c0a..0000000 --- a/include/recomp.h +++ /dev/null @@ -1,327 +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)((gpr)(val) >> 0); \ - *(uint32_t*)(rdram + ((((reg) + (offset) + 0)) - 0xFFFFFFFF80000000)) = (uint32_t)((gpr)(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) - -static inline gpr do_lwl(uint8_t* rdram, gpr initial_value, gpr offset, gpr reg) { - // Calculate the overall address - gpr address = (offset + reg); - - // Load the aligned word - gpr word_address = address & ~0x3; - uint32_t loaded_value = MEM_W(0, word_address); - - // Mask the existing value and shift the loaded value appropriately - gpr misalignment = address & 0x3; - gpr masked_value = initial_value & ~(0xFFFFFFFFu << (misalignment * 8)); - loaded_value <<= (misalignment * 8); - - // Cast to int32_t to sign extend first - return (gpr)(int32_t)(masked_value | loaded_value); -} - -static inline gpr do_lwr(uint8_t* rdram, gpr initial_value, gpr offset, gpr reg) { - // Calculate the overall address - gpr address = (offset + reg); - - // Load the aligned word - gpr word_address = address & ~0x3; - uint32_t loaded_value = MEM_W(0, word_address); - - // Mask the existing value and shift the loaded value appropriately - gpr misalignment = address & 0x3; - gpr masked_value = initial_value & ~(0xFFFFFFFFu >> (24 - misalignment * 8)); - loaded_value >>= (24 - misalignment * 8); - - // Cast to int32_t to sign extend first - return (gpr)(int32_t)(masked_value | loaded_value); -} - -static inline void do_swl(uint8_t* rdram, gpr offset, gpr reg, gpr val) { - // Calculate the overall address - gpr address = (offset + reg); - - // Get the initial value of the aligned word - gpr word_address = address & ~0x3; - uint32_t initial_value = MEM_W(0, word_address); - - // Mask the initial value and shift the input value appropriately - gpr misalignment = address & 0x3; - uint32_t masked_initial_value = initial_value & ~(0xFFFFFFFFu >> (misalignment * 8)); - uint32_t shifted_input_value = ((uint32_t)val) >> (misalignment * 8); - MEM_W(0, word_address) = masked_initial_value | shifted_input_value; -} - -static inline void do_swr(uint8_t* rdram, gpr offset, gpr reg, gpr val) { - // Calculate the overall address - gpr address = (offset + reg); - - // Get the initial value of the aligned word - gpr word_address = address & ~0x3; - uint32_t initial_value = MEM_W(0, word_address); - - // Mask the initial value and shift the input value appropriately - gpr misalignment = address & 0x3; - uint32_t masked_initial_value = initial_value & ~(0xFFFFFFFFu << (24 - misalignment * 8)); - uint32_t shifted_input_value = ((uint32_t)val) << (24 - misalignment * 8); - MEM_W(0, word_address) = masked_initial_value | shifted_input_value; -} - -#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)) - -#define DEFAULT_ROUNDING_MODE 0 - -static inline int32_t do_cvt_w_s(float val, unsigned int rounding_mode) { - switch (rounding_mode) { - case 0: // round to nearest value - return (int32_t)lroundf(val); - case 1: // round to zero (truncate) - return (int32_t)val; - case 2: // round to positive infinity (ceil) - return (int32_t)ceilf(val); - case 3: // round to negative infinity (floor) - return (int32_t)floorf(val); - } - assert(0); - return 0; -} - -#define CVT_W_S(val) \ - do_cvt_w_s(val, rounding_mode) - -static inline int32_t do_cvt_w_d(double val, unsigned int rounding_mode) { - switch (rounding_mode) { - case 0: // round to nearest value - return (int32_t)lround(val); - case 1: // round to zero (truncate) - return (int32_t)val; - case 2: // round to positive infinity (ceil) - return (int32_t)ceil(val); - case 3: // round to negative infinity (floor) - return (int32_t)floor(val); - } - assert(0); - return 0; -} - -#define CVT_W_D(val) \ - do_cvt_w_d(val, rounding_mode) - -#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, f1, f2, f3, f4, f5, f6, f7, - f8, f9, f10, f11, f12, f13, f14, f15, - f16, f17, f18, f19, f20, f21, f22, f23, - f24, f25, f26, f27, f28, f29, f30, f31; - uint64_t hi, lo; - uint32_t* f_odd; - uint32_t status_reg; - uint8_t mips3_float_mode; -} recomp_context; - -// Checks if the target is an even float register or that mips3 float mode is enabled -#define CHECK_FR(ctx, idx) \ - assert(((idx) & 1) == 0 || (ctx)->mips3_float_mode) - -#ifdef __cplusplus -extern "C" { -#endif - -void cop0_status_write(recomp_context* ctx, gpr value); -gpr cop0_status_read(recomp_context* ctx); -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 Banjo-Tooie -void recomp_syscall_handler(uint8_t* rdram, recomp_context* ctx, int32_t instruction_vram); - -// 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); \ -//} - -void pause_self(uint8_t *rdram); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/include/recomp_config.h b/include/recomp_config.h deleted file mode 100644 index 51e0424..0000000 --- a/include/recomp_config.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef __RECOMP_CONFIG_H__ -#define __RECOMP_CONFIG_H__ - -#include -#include -#include "../ultramodern/config.hpp" - -namespace recomp { - constexpr std::u8string_view program_id = u8"Zelda64Recompiled"; - constexpr std::u8string_view mm_game_id = u8"mm.n64.us.1.0"; - constexpr std::string_view program_name = "Zelda 64: Recompiled"; - - void load_config(); - void save_config(); - - void reset_input_bindings(); - void reset_cont_input_bindings(); - void reset_kb_input_bindings(); - - std::filesystem::path get_app_folder_path(); - - bool get_debug_mode_enabled(); - void set_debug_mode_enabled(bool enabled); - - enum class AutosaveMode { - On, - Off, - OptionCount - }; - - enum class AnalogCamMode { - On, - Off, - OptionCount - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(recomp::AutosaveMode, { - {recomp::AutosaveMode::On, "On"}, - {recomp::AutosaveMode::Off, "Off"} - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(recomp::AnalogCamMode, { - {recomp::AnalogCamMode::On, "On"}, - {recomp::AnalogCamMode::Off, "Off"} - }); - - AutosaveMode get_autosave_mode(); - void set_autosave_mode(AutosaveMode mode); - - AnalogCamMode get_analog_cam_mode(); - void set_analog_cam_mode(AnalogCamMode mode); -}; - -#endif diff --git a/include/recomp_game.h b/include/recomp_game.h deleted file mode 100644 index dd1da12..0000000 --- a/include/recomp_game.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef __RECOMP_GAME__ -#define __RECOMP_GAME__ - -#include -#include - -#include "recomp.h" -#include "../ultramodern/ultramodern.hpp" -#include "rt64_layer.h" - -namespace recomp { - enum class Game { - OoT, - MM, - None, - Quit - }; - enum class RomValidationError { - Good, - FailedToOpen, - NotARom, - IncorrectRom, - NotYet, - IncorrectVersion, - OtherError - }; - void check_all_stored_roms(); - bool load_stored_rom(Game game); - RomValidationError select_rom(const std::filesystem::path& rom_path, Game game); - bool is_rom_valid(Game game); - bool is_rom_loaded(); - void set_rom_contents(std::vector&& new_rom); - void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes); - void do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr); - void start(ultramodern::WindowHandle window_handle, const ultramodern::audio_callbacks_t& audio_callbacks, const ultramodern::input_callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks); - void start_game(Game game); - void message_box(const char* message); -} - -#endif diff --git a/include/recomp_helpers.h b/include/recomp_helpers.h deleted file mode 100644 index cfadf81..0000000 --- a/include/recomp_helpers.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef __RECOMP_HELPERS__ -#define __RECOMP_HELPERS__ - -#include "recomp.h" -#include "../ultramodern/ultra64.h" - -template -T _arg(uint8_t* rdram, recomp_context* ctx) { - static_assert(index < 4, "Only args 0 through 3 supported"); - gpr raw_arg = (&ctx->r4)[index]; - if constexpr (std::is_same_v) { - if constexpr (index < 2) { - static_assert(index != 1, "Floats in arg 1 not supported"); - return ctx->f12.fl; - } - else { - // static_assert in else workaround - [] () { - static_assert(flag, "Floats in a2/a3 not supported"); - }(); - } - } - else if constexpr (std::is_pointer_v) { - static_assert (!std::is_pointer_v>, "Double pointers not supported"); - return TO_PTR(std::remove_pointer_t, raw_arg); - } - else if constexpr (std::is_integral_v) { - static_assert(sizeof(T) <= 4, "64-bit args not supported"); - return static_cast(raw_arg); - } - else { - // static_assert in else workaround - [] () { - static_assert(flag, "Unsupported type"); - }(); - } -} - -template -void _return(recomp_context* ctx, T val) { - static_assert(sizeof(T) <= 4 && "Only 32-bit value returns supported currently"); - if (std::is_same_v) { - ctx->f0.fl = val; - } - else if (std::is_integral_v && sizeof(T) <= 4) { - ctx->r2 = int32_t(val); - } -} - -#endif \ No newline at end of file diff --git a/include/recomp_input.h b/include/recomp_input.h index 7243862..785294f 100644 --- a/include/recomp_input.h +++ b/include/recomp_input.h @@ -14,6 +14,7 @@ namespace recomp { // x-macros to build input enums and arrays. // First parameter is the enum name, second parameter is the bit field for the input (or 0 if there is no associated one), third is the readable name. + // TODO refactor this to allow projects to rename these, or get rid of the readable name and leave that up to individual projects to map. #define DEFINE_N64_BUTTON_INPUTS() \ DEFINE_INPUT(A, 0x8000, "Action") \ DEFINE_INPUT(B, 0x4000, "Attack/Cancel") \ @@ -168,20 +169,6 @@ namespace recomp { void apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out); void set_right_analog_suppressed(bool suppressed); - enum class TargetingMode { - Switch, - Hold, - OptionCount - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(recomp::TargetingMode, { - {recomp::TargetingMode::Switch, "Switch"}, - {recomp::TargetingMode::Hold, "Hold"} - }); - - TargetingMode get_targeting_mode(); - void set_targeting_mode(TargetingMode mode); - enum class BackgroundInputMode { On, Off, @@ -196,35 +183,8 @@ namespace recomp { BackgroundInputMode get_background_input_mode(); void set_background_input_mode(BackgroundInputMode mode); - enum class CameraInvertMode { - InvertNone, - InvertX, - InvertY, - InvertBoth, - OptionCount - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(recomp::CameraInvertMode, { - {recomp::CameraInvertMode::InvertNone, "InvertNone"}, - {recomp::CameraInvertMode::InvertX, "InvertX"}, - {recomp::CameraInvertMode::InvertY, "InvertY"}, - {recomp::CameraInvertMode::InvertBoth, "InvertBoth"} - }); - - CameraInvertMode get_camera_invert_mode(); - void set_camera_invert_mode(CameraInvertMode mode); - - CameraInvertMode get_analog_camera_invert_mode(); - void set_analog_camera_invert_mode(CameraInvertMode mode); - bool game_input_disabled(); bool all_input_disabled(); - - // TODO move these - void quicksave_save(); - void quicksave_load(); - - void open_quit_game_prompt(); } #endif diff --git a/include/recomp_overlays.h b/include/recomp_overlays.h deleted file mode 100644 index cf1f8a1..0000000 --- a/include/recomp_overlays.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __RECOMP_OVERLAYS_H__ -#define __RECOMP_OVERLAYS_H__ - -#include - -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); -void init_overlays(); - -#endif \ No newline at end of file diff --git a/include/recomp_ui.h b/include/recomp_ui.h index 6139421..a8d42a4 100644 --- a/include/recomp_ui.h +++ b/include/recomp_ui.h @@ -14,7 +14,7 @@ namespace Rml { class Event; } -namespace recomp { +namespace recompui { class UiEventListenerInstancer; class MenuController { @@ -118,6 +118,8 @@ namespace recomp { bool get_cont_active(void); void set_cont_active(bool active); void activate_mouse(); + + void message_box(const char* msg); } #endif diff --git a/include/rsp.h b/include/rsp.h deleted file mode 100644 index 17f8f90..0000000 --- a/include/rsp.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef __RSP_H__ -#define __RSP_H__ - -#include "rsp_vu.h" -#include "recomp.h" -#include - -enum class RspExitReason { - Invalid, - Broke, - ImemOverrun, - UnhandledJumpTarget, - Unsupported -}; - -extern uint8_t dmem[]; -extern uint16_t rspReciprocals[512]; -extern uint16_t rspInverseSquareRoots[512]; - -#define RSP_MEM_B(offset, addr) \ - (*reinterpret_cast(dmem + (0xFFF & (((offset) + (addr)) ^ 3)))) - -#define RSP_MEM_BU(offset, addr) \ - (*reinterpret_cast(dmem + (0xFFF & (((offset) + (addr)) ^ 3)))) - -static inline uint32_t RSP_MEM_W_LOAD(uint32_t offset, uint32_t addr) { - uint32_t out; - for (int i = 0; i < 4; i++) { - reinterpret_cast(&out)[i ^ 3] = RSP_MEM_BU(offset + i, addr); - } - return out; -} - -static inline void RSP_MEM_W_STORE(uint32_t offset, uint32_t addr, uint32_t val) { - for (int i = 0; i < 4; i++) { - RSP_MEM_BU(offset + i, addr) = reinterpret_cast(&val)[i ^ 3]; - } -} - -static inline uint32_t RSP_MEM_HU_LOAD(uint32_t offset, uint32_t addr) { - uint16_t out; - for (int i = 0; i < 2; i++) { - reinterpret_cast(&out)[(i + 2) ^ 3] = RSP_MEM_BU(offset + i, addr); - } - return out; -} - -static inline uint32_t RSP_MEM_H_LOAD(uint32_t offset, uint32_t addr) { - int16_t out; - for (int i = 0; i < 2; i++) { - reinterpret_cast(&out)[(i + 2) ^ 3] = RSP_MEM_BU(offset + i, addr); - } - return out; -} - -static inline void RSP_MEM_H_STORE(uint32_t offset, uint32_t addr, uint32_t val) { - for (int i = 0; i < 2; i++) { - RSP_MEM_BU(offset + i, addr) = reinterpret_cast(&val)[(i + 2) ^ 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/include/rsp_vu.h b/include/rsp_vu.h deleted file mode 100644 index f98fdc7..0000000 --- a/include/rsp_vu.h +++ /dev/null @@ -1,203 +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 - -#if defined(__x86_64__) || defined(_M_X64) -#define ARCHITECTURE_SUPPORTS_SSE4_1 1 -#include -using v128 = __m128i; -#elif defined(__aarch64__) || defined(_M_ARM64) -#define ARCHITECTURE_SUPPORTS_SSE4_1 1 -#include "sse2neon.h" -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; -} - -template inline auto sclip(s64 x) -> s64 { - enum : u64 { b = 1ull << (bits - 1), m = b * 2 - 1 }; - return ((x & m) ^ b) - b; -} - -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/include/rsp_vu_impl.h b/include/rsp_vu_impl.h deleted file mode 100644 index dbf46b2..0000000 --- a/include/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/include/rt64_layer.h b/include/rt64_layer.h deleted file mode 100644 index 04725e3..0000000 --- a/include/rt64_layer.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef __RT64_LAYER_H__ -#define __RT64_LAYER_H__ - -#include "../ultramodern/ultramodern.hpp" -#include "../ultramodern/config.hpp" - -namespace RT64 { - struct Application; -} - -namespace ultramodern { - enum class RT64SetupResult { - Success, - DynamicLibrariesNotFound, - InvalidGraphicsAPI, - GraphicsAPINotFound, - GraphicsDeviceNotFound - }; - - struct WindowHandle; - struct RT64Context { - public: - ~RT64Context(); - RT64Context(uint8_t* rdram, WindowHandle window_handle, bool developer_mode); - bool valid() { return static_cast(app); } - RT64SetupResult get_setup_result() { return setup_result; } - - void update_config(const GraphicsConfig& old_config, const GraphicsConfig& new_config); - void enable_instant_present(); - void send_dl(const OSTask* task); - void update_screen(uint32_t vi_origin); - void shutdown(); - void set_dummy_vi(); - uint32_t get_display_framerate(); - float get_resolution_scale(); - void load_shader_cache(std::span cache_binary); - private: - RT64SetupResult setup_result; - std::unique_ptr app; - }; - - RT64::UserConfiguration::Antialiasing RT64MaxMSAA(); - bool RT64SamplePositionsSupported(); - bool RT64HighPrecisionFBEnabled(); -} - -void set_rt64_hooks(); - -#endif - diff --git a/include/sections.h b/include/sections.h deleted file mode 100644 index 1d1b228..0000000 --- a/include/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/include/zelda_config.h b/include/zelda_config.h new file mode 100644 index 0000000..f5b206a --- /dev/null +++ b/include/zelda_config.h @@ -0,0 +1,91 @@ +#ifndef __ZELDA_CONFIG_H__ +#define __ZELDA_CONFIG_H__ + +#include +#include +#include "ultramodern/config.hpp" + +namespace zelda64 { + constexpr std::u8string_view program_id = u8"Zelda64Recompiled"; + constexpr std::string_view program_name = "Zelda 64: Recompiled"; + + // TODO: Move loading configs to the runtime once we have a way to allow per-project customization. + void load_config(); + void save_config(); + + void reset_input_bindings(); + void reset_cont_input_bindings(); + void reset_kb_input_bindings(); + + std::filesystem::path get_app_folder_path(); + + bool get_debug_mode_enabled(); + void set_debug_mode_enabled(bool enabled); + + enum class AutosaveMode { + On, + Off, + OptionCount + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(zelda64::AutosaveMode, { + {zelda64::AutosaveMode::On, "On"}, + {zelda64::AutosaveMode::Off, "Off"} + }); + + enum class TargetingMode { + Switch, + Hold, + OptionCount + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(zelda64::TargetingMode, { + {zelda64::TargetingMode::Switch, "Switch"}, + {zelda64::TargetingMode::Hold, "Hold"} + }); + + TargetingMode get_targeting_mode(); + void set_targeting_mode(TargetingMode mode); + + enum class CameraInvertMode { + InvertNone, + InvertX, + InvertY, + InvertBoth, + OptionCount + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(zelda64::CameraInvertMode, { + {zelda64::CameraInvertMode::InvertNone, "InvertNone"}, + {zelda64::CameraInvertMode::InvertX, "InvertX"}, + {zelda64::CameraInvertMode::InvertY, "InvertY"}, + {zelda64::CameraInvertMode::InvertBoth, "InvertBoth"} + }); + + CameraInvertMode get_camera_invert_mode(); + void set_camera_invert_mode(CameraInvertMode mode); + + CameraInvertMode get_analog_camera_invert_mode(); + void set_analog_camera_invert_mode(CameraInvertMode mode); + + enum class AnalogCamMode { + On, + Off, + OptionCount + }; + + NLOHMANN_JSON_SERIALIZE_ENUM(zelda64::AnalogCamMode, { + {zelda64::AnalogCamMode::On, "On"}, + {zelda64::AnalogCamMode::Off, "Off"} + }); + + AutosaveMode get_autosave_mode(); + void set_autosave_mode(AutosaveMode mode); + + AnalogCamMode get_analog_cam_mode(); + void set_analog_cam_mode(AnalogCamMode mode); + + void open_quit_game_prompt(); +}; + +#endif diff --git a/include/recomp_debug.h b/include/zelda_debug.h similarity index 85% rename from include/recomp_debug.h rename to include/zelda_debug.h index c8e6538..4c0743e 100644 --- a/include/recomp_debug.h +++ b/include/zelda_debug.h @@ -1,10 +1,10 @@ -#ifndef __RECOMP_DEBUG_H__ -#define __RECOMP_DEBUG_H__ +#ifndef __ZELDA_DEBUG_H__ +#define __ZELDA_DEBUG_H__ #include #include -namespace recomp { +namespace zelda64 { struct SceneWarps { int index; std::string name; diff --git a/include/zelda_game.h b/include/zelda_game.h new file mode 100644 index 0000000..ac63979 --- /dev/null +++ b/include/zelda_game.h @@ -0,0 +1,9 @@ +#ifndef __ZELDA_GAME_H__ +#define __ZELDA_GAME_H__ + +namespace zelda64 { + void quicksave_save(); + void quicksave_load(); +}; + +#endif diff --git a/include/recomp_sound.h b/include/zelda_sound.h similarity index 78% rename from include/recomp_sound.h rename to include/zelda_sound.h index f33ed4a..33bec01 100644 --- a/include/recomp_sound.h +++ b/include/zelda_sound.h @@ -1,7 +1,7 @@ -#ifndef __RECOMP_SOUND_H__ -#define __RECOMP_SOUND_H__ +#ifndef __ZELDA_SOUND_H__ +#define __ZELDA_SOUND_H__ -namespace recomp { +namespace zelda64 { void reset_sound_settings(); void set_main_volume(int volume); int get_main_volume(); diff --git a/lib/N64ModernRuntime b/lib/N64ModernRuntime new file mode 160000 index 0000000..ec7e81b --- /dev/null +++ b/lib/N64ModernRuntime @@ -0,0 +1 @@ +Subproject commit ec7e81b45d9a622cb3e45865ce5d7d3536b26534 diff --git a/patches/Makefile b/patches/Makefile index 8e59105..0416f19 100644 --- a/patches/Makefile +++ b/patches/Makefile @@ -27,7 +27,7 @@ $(C_OBJS): %.o : %.c $(CC) $(CFLAGS) $(CPPFLAGS) $< -MMD -MF $(@:.o=.d) -c -o $@ clean: - rm -rf $(C_OBJS) $(TARGET) $(DATABIN) + rm -rf $(C_OBJS) $(TARGET) $(DATABIN) $(C_DEPS) -include $(C_DEPS) diff --git a/patches/patch_helpers.h b/patches/patch_helpers.h index f6b88d3..371e8b4 100644 --- a/patches/patch_helpers.h +++ b/patches/patch_helpers.h @@ -4,7 +4,7 @@ #ifdef MIPS #include "ultra64.h" #else -#include "recomp.h" +#include "librecomp/recomp.h" #endif #ifdef __cplusplus diff --git a/src/game/config.cpp b/src/game/config.cpp index e90e20b..c32c379 100644 --- a/src/game/config.cpp +++ b/src/game/config.cpp @@ -1,8 +1,8 @@ -#include "recomp_config.h" +#include "zelda_config.h" #include "recomp_input.h" -#include "recomp_sound.h" -#include "recomp_files.h" -#include "../../ultramodern/config.hpp" +#include "zelda_sound.h" +#include "ultramodern/config.hpp" +#include "librecomp/files.hpp" #include #include #include @@ -18,6 +18,7 @@ constexpr std::u8string_view general_filename = u8"general.json"; constexpr std::u8string_view graphics_filename = u8"graphics.json"; constexpr std::u8string_view controls_filename = u8"controls.json"; constexpr std::u8string_view sound_filename = u8"sound.json"; +constexpr std::u8string_view program_id = u8"Zelda64Recompiled"; constexpr auto res_default = ultramodern::Resolution::Auto; constexpr auto hr_default = ultramodern::HUDRatioMode::Clamp16x9; @@ -127,7 +128,7 @@ namespace recomp { } } -std::filesystem::path recomp::get_app_folder_path() { +std::filesystem::path zelda64::get_app_folder_path() { std::filesystem::path recomp_dir{}; #if defined(_WIN32) @@ -135,7 +136,7 @@ std::filesystem::path recomp::get_app_folder_path() { PWSTR known_path = NULL; HRESULT result = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &known_path); if (result == S_OK) { - recomp_dir = std::filesystem::path{known_path} / recomp::program_id; + recomp_dir = std::filesystem::path{known_path} / zelda64::program_id; } CoTaskMemFree(known_path); @@ -147,7 +148,7 @@ std::filesystem::path recomp::get_app_folder_path() { } if (homedir != nullptr) { - recomp_dir = std::filesystem::path{homedir} / (std::u8string{u8".config/"} + std::u8string{recomp::program_id}); + recomp_dir = std::filesystem::path{homedir} / (std::u8string{u8".config/"} + std::u8string{zelda64::program_id}); } #endif @@ -198,33 +199,33 @@ bool save_json_with_backups(const std::filesystem::path& path, const nlohmann::j bool save_general_config(const std::filesystem::path& path) { nlohmann::json config_json{}; - recomp::to_json(config_json["targeting_mode"], recomp::get_targeting_mode()); + zelda64::to_json(config_json["targeting_mode"], zelda64::get_targeting_mode()); recomp::to_json(config_json["background_input_mode"], recomp::get_background_input_mode()); config_json["rumble_strength"] = recomp::get_rumble_strength(); config_json["gyro_sensitivity"] = recomp::get_gyro_sensitivity(); config_json["mouse_sensitivity"] = recomp::get_mouse_sensitivity(); config_json["joystick_deadzone"] = recomp::get_joystick_deadzone(); - config_json["autosave_mode"] = recomp::get_autosave_mode(); - config_json["camera_invert_mode"] = recomp::get_camera_invert_mode(); - config_json["analog_cam_mode"] = recomp::get_analog_cam_mode(); - config_json["analog_camera_invert_mode"] = recomp::get_analog_camera_invert_mode(); - config_json["debug_mode"] = recomp::get_debug_mode_enabled(); + config_json["autosave_mode"] = zelda64::get_autosave_mode(); + config_json["camera_invert_mode"] = zelda64::get_camera_invert_mode(); + config_json["analog_cam_mode"] = zelda64::get_analog_cam_mode(); + config_json["analog_camera_invert_mode"] = zelda64::get_analog_camera_invert_mode(); + config_json["debug_mode"] = zelda64::get_debug_mode_enabled(); return save_json_with_backups(path, config_json); } void set_general_settings_from_json(const nlohmann::json& config_json) { - recomp::set_targeting_mode(from_or_default(config_json, "targeting_mode", recomp::TargetingMode::Switch)); + zelda64::set_targeting_mode(from_or_default(config_json, "targeting_mode", zelda64::TargetingMode::Switch)); recomp::set_background_input_mode(from_or_default(config_json, "background_input_mode", recomp::BackgroundInputMode::On)); recomp::set_rumble_strength(from_or_default(config_json, "rumble_strength", 25)); recomp::set_gyro_sensitivity(from_or_default(config_json, "gyro_sensitivity", 50)); recomp::set_mouse_sensitivity(from_or_default(config_json, "mouse_sensitivity", is_steam_deck ? 50 : 0)); recomp::set_joystick_deadzone(from_or_default(config_json, "joystick_deadzone", 5)); - recomp::set_autosave_mode(from_or_default(config_json, "autosave_mode", recomp::AutosaveMode::On)); - recomp::set_camera_invert_mode(from_or_default(config_json, "camera_invert_mode", recomp::CameraInvertMode::InvertY)); - recomp::set_analog_cam_mode(from_or_default(config_json, "analog_cam_mode", recomp::AnalogCamMode::Off)); - recomp::set_analog_camera_invert_mode(from_or_default(config_json, "analog_camera_invert_mode", recomp::CameraInvertMode::InvertNone)); - recomp::set_debug_mode_enabled(from_or_default(config_json, "debug_mode", false)); + zelda64::set_autosave_mode(from_or_default(config_json, "autosave_mode", zelda64::AutosaveMode::On)); + zelda64::set_camera_invert_mode(from_or_default(config_json, "camera_invert_mode", zelda64::CameraInvertMode::InvertY)); + zelda64::set_analog_cam_mode(from_or_default(config_json, "analog_cam_mode", zelda64::AnalogCamMode::Off)); + zelda64::set_analog_camera_invert_mode(from_or_default(config_json, "analog_camera_invert_mode", zelda64::CameraInvertMode::InvertNone)); + zelda64::set_debug_mode_enabled(from_or_default(config_json, "debug_mode", false)); } bool load_general_config(const std::filesystem::path& path) { @@ -277,16 +278,16 @@ void assign_all_mappings(recomp::InputDevice device, const recomp::DefaultN64Map assign_mapping_complete(device, recomp::GameInput::TOGGLE_MENU, values.toggle_menu); }; -void recomp::reset_input_bindings() { +void zelda64::reset_input_bindings() { assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); } -void recomp::reset_cont_input_bindings() { +void zelda64::reset_cont_input_bindings() { assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings); } -void recomp::reset_kb_input_bindings() { +void zelda64::reset_kb_input_bindings() { assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings); } @@ -369,8 +370,8 @@ bool load_input_device_from_json(const nlohmann::json& config_json, recomp::Inpu cur_input, recomp::get_default_mapping_for_input( device == recomp::InputDevice::Keyboard ? - recomp::default_n64_keyboard_mappings : - recomp::default_n64_controller_mappings, + recomp::default_n64_keyboard_mappings : + recomp::default_n64_controller_mappings, cur_input ) ); @@ -408,10 +409,10 @@ bool load_controls_config(const std::filesystem::path& path) { bool save_sound_config(const std::filesystem::path& path) { nlohmann::json config_json{}; - config_json["main_volume"] = recomp::get_main_volume(); - config_json["bgm_volume"] = recomp::get_bgm_volume(); - config_json["low_health_beeps"] = recomp::get_low_health_beeps_enabled(); - + config_json["main_volume"] = zelda64::get_main_volume(); + config_json["bgm_volume"] = zelda64::get_bgm_volume(); + config_json["low_health_beeps"] = zelda64::get_low_health_beeps_enabled(); + return save_json_with_backups(path, config_json); } @@ -421,17 +422,17 @@ bool load_sound_config(const std::filesystem::path& path) { return false; } - recomp::reset_sound_settings(); - call_if_key_exists(recomp::set_main_volume, config_json, "main_volume"); - call_if_key_exists(recomp::set_bgm_volume, config_json, "bgm_volume"); - call_if_key_exists(recomp::set_low_health_beeps_enabled, config_json, "low_health_beeps"); + zelda64::reset_sound_settings(); + call_if_key_exists(zelda64::set_main_volume, config_json, "main_volume"); + call_if_key_exists(zelda64::set_bgm_volume, config_json, "bgm_volume"); + call_if_key_exists(zelda64::set_low_health_beeps_enabled, config_json, "low_health_beeps"); return true; } -void recomp::load_config() { +void zelda64::load_config() { detect_steam_deck(); - std::filesystem::path recomp_dir = recomp::get_app_folder_path(); + std::filesystem::path recomp_dir = zelda64::get_app_folder_path(); std::filesystem::path general_path = recomp_dir / general_filename; std::filesystem::path graphics_path = recomp_dir / graphics_filename; std::filesystem::path controls_path = recomp_dir / controls_filename; @@ -455,18 +456,18 @@ void recomp::load_config() { } if (!load_controls_config(controls_path)) { - recomp::reset_input_bindings(); + zelda64::reset_input_bindings(); save_controls_config(controls_path); } if (!load_sound_config(sound_path)) { - recomp::reset_sound_settings(); + zelda64::reset_sound_settings(); save_sound_config(sound_path); } } -void recomp::save_config() { - std::filesystem::path recomp_dir = recomp::get_app_folder_path(); +void zelda64::save_config() { + std::filesystem::path recomp_dir = zelda64::get_app_folder_path(); if (recomp_dir.empty()) { return; diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 35db400..0a6f9f3 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -1,8 +1,8 @@ #include -#include "recomp_helpers.h" +#include "librecomp/helpers.hpp" #include "recomp_input.h" -#include "../ultramodern/ultramodern.hpp" +#include "ultramodern/ultramodern.hpp" // Arrays that hold the mappings for every input for keyboard and controller respectively. using input_mapping = std::array; diff --git a/src/game/debug.cpp b/src/game/debug.cpp index 5dae119..03e2d5f 100644 --- a/src/game/debug.cpp +++ b/src/game/debug.cpp @@ -1,13 +1,13 @@ #include -#include "recomp_debug.h" -#include "recomp_helpers.h" +#include "zelda_debug.h" +#include "librecomp/helpers.hpp" #include "../patches/input.h" std::atomic pending_warp = 0xFFFF; std::atomic pending_set_time = 0xFFFF; -void recomp::do_warp(int area, int scene, int entrance) { - const recomp::SceneWarps game_scene = recomp::game_warps[area].scenes[scene]; +void zelda64::do_warp(int area, int scene, int entrance) { + const zelda64::SceneWarps game_scene = zelda64::game_warps[area].scenes[scene]; int game_scene_index = game_scene.index; pending_warp.store(((game_scene_index & 0xFF) << 8) | ((entrance & 0x0F) << 4)); } @@ -17,7 +17,7 @@ extern "C" void recomp_get_pending_warp(uint8_t* rdram, recomp_context* ctx) { _return(ctx, pending_warp.exchange(0xFFFF)); } -void recomp::set_time(uint8_t day, uint8_t hour, uint8_t minute) { +void zelda64::set_time(uint8_t day, uint8_t hour, uint8_t minute) { pending_set_time.store((day << 16) | (uint16_t(hour) << 8) | minute); } diff --git a/src/game/input.cpp b/src/game/input.cpp index b06d8b8..09da9b5 100644 --- a/src/game/input.cpp +++ b/src/game/input.cpp @@ -1,12 +1,12 @@ #include #include -#include "../ultramodern/ultramodern.hpp" -#include "recomp.h" +#include "ultramodern/ultramodern.hpp" +#include "librecomp/recomp.h" #include "recomp_input.h" +#include "zelda_config.h" #include "recomp_ui.h" #include "SDL.h" -#include "rt64_layer.h" #include "promptfont.h" #include "GamepadMotion.hpp" @@ -75,13 +75,13 @@ void recomp::stop_scanning_input() { void queue_if_enabled(SDL_Event* event) { if (!recomp::all_input_disabled()) { - recomp::queue_event(*event); + recompui::queue_event(*event); } } static std::atomic_bool cursor_enabled = true; -void recomp::set_cursor_visible(bool visible) { +void recompui::set_cursor_visible(bool visible) { cursor_enabled.store(visible); } @@ -110,7 +110,7 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) { if ((keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_RETURN && (keyevent->keysym.mod & SDL_Keymod::KMOD_ALT)) || keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_F11 ) { - recomp::toggle_fullscreen(); + recompui::toggle_fullscreen(); } if (scanning_device != recomp::InputDevice::COUNT) { if (keyevent->keysym.scancode == SDL_Scancode::SDL_SCANCODE_ESCAPE) { @@ -155,12 +155,12 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) { return true; } - if (recomp::get_current_menu() != recomp::Menu::Config) { - recomp::set_current_menu(recomp::Menu::Config); + if (recompui::get_current_menu() != recompui::Menu::Config) { + recompui::set_current_menu(recompui::Menu::Config); } - recomp::open_quit_game_prompt(); - recomp::activate_mouse(); + zelda64::open_quit_game_prompt(); + recompui::activate_mouse(); break; } case SDL_EventType::SDL_MOUSEWHEEL: @@ -435,10 +435,10 @@ void recomp::poll_inputs() { bool save_is_held = InputState.keys[SDL_SCANCODE_F5] != 0; bool load_is_held = InputState.keys[SDL_SCANCODE_F7] != 0; if (save_is_held && !save_was_held) { - recomp::quicksave_save(); + zelda64::quicksave_save(); } else if (load_is_held && !load_was_held) { - recomp::quicksave_load(); + zelda64::quicksave_load(); } save_was_held = save_is_held; } @@ -649,7 +649,7 @@ void recomp::set_right_analog_suppressed(bool suppressed) { bool recomp::game_input_disabled() { // Disable input if any menu is open. - return recomp::get_current_menu() != recomp::Menu::None; + return recompui::get_current_menu() != recompui::Menu::None; } bool recomp::all_input_disabled() { diff --git a/src/game/quicksaving.cpp b/src/game/quicksaving.cpp index 08975f4..05c36a5 100644 --- a/src/game/quicksaving.cpp +++ b/src/game/quicksaving.cpp @@ -2,9 +2,9 @@ #if 0 -#include "recomp_helpers.h" -#include "recomp_input.h" -#include "../ultramodern/ultramodern.hpp" +#include "librecomp/helpers.hpp" +#include "librecomp/input.hpp" +#include "ultramodern/ultramodern.hpp" enum class QuicksaveAction { None, @@ -14,11 +14,11 @@ enum class QuicksaveAction { std::atomic cur_quicksave_action = QuicksaveAction::None; -void recomp::quicksave_save() { +void zelda64::quicksave_save() { cur_quicksave_action.store(QuicksaveAction::Save); } -void recomp::quicksave_load() { +void zelda64::quicksave_load() { cur_quicksave_action.store(QuicksaveAction::Load); } diff --git a/src/game/recomp_api.cpp b/src/game/recomp_api.cpp index d310ffe..042788d 100644 --- a/src/game/recomp_api.cpp +++ b/src/game/recomp_api.cpp @@ -1,18 +1,18 @@ #include -#include "recomp.h" -#include "recomp_overlays.h" -#include "recomp_config.h" +#include "librecomp/recomp.h" +#include "librecomp/overlays.hpp" +#include "zelda_config.h" #include "recomp_input.h" #include "recomp_ui.h" -#include "recomp_sound.h" -#include "recomp_helpers.h" -#include "rt64_layer.h" +#include "zelda_sound.h" +#include "librecomp/helpers.hpp" #include "../patches/input.h" #include "../patches/graphics.h" #include "../patches/sound.h" -#include "../ultramodern/ultramodern.hpp" -#include "../ultramodern/config.hpp" +#include "ultramodern/ultramodern.hpp" +#include "ultramodern/config.hpp" +#include "ultramodern/rt64_layer.hpp" extern "C" void recomp_update_inputs(uint8_t* rdram, recomp_context* ctx) { recomp::poll_inputs(); @@ -62,7 +62,7 @@ extern "C" void recomp_get_aspect_ratio(uint8_t* rdram, recomp_context* ctx) { ultramodern::GraphicsConfig graphics_config = ultramodern::get_graphics_config(); float original = _arg<0, float>(rdram, ctx); int width, height; - recomp::get_window_size(width, height); + recompui::get_window_size(width, height); switch (graphics_config.ar_option) { case RT64::UserConfiguration::AspectRatio::Original: @@ -76,15 +76,15 @@ extern "C" void recomp_get_aspect_ratio(uint8_t* rdram, recomp_context* ctx) { } extern "C" void recomp_get_targeting_mode(uint8_t* rdram, recomp_context* ctx) { - _return(ctx, static_cast(recomp::get_targeting_mode())); + _return(ctx, static_cast(zelda64::get_targeting_mode())); } extern "C" void recomp_get_bgm_volume(uint8_t* rdram, recomp_context* ctx) { - _return(ctx, recomp::get_bgm_volume() / 100.0f); + _return(ctx, zelda64::get_bgm_volume() / 100.0f); } extern "C" void recomp_get_low_health_beeps_enabled(uint8_t* rdram, recomp_context* ctx) { - _return(ctx, static_cast(recomp::get_low_health_beeps_enabled())); + _return(ctx, static_cast(zelda64::get_low_health_beeps_enabled())); } extern "C" void recomp_time_us(uint8_t* rdram, recomp_context* ctx) { @@ -92,7 +92,7 @@ extern "C" void recomp_time_us(uint8_t* rdram, recomp_context* ctx) { } extern "C" void recomp_autosave_enabled(uint8_t* rdram, recomp_context* ctx) { - _return(ctx, static_cast(recomp::get_autosave_mode() == recomp::AutosaveMode::On)); + _return(ctx, static_cast(zelda64::get_autosave_mode() == zelda64::AutosaveMode::On)); } extern "C" void recomp_load_overlays(uint8_t * rdram, recomp_context * ctx) { @@ -115,24 +115,24 @@ extern "C" void recomp_get_inverted_axes(uint8_t* rdram, recomp_context* ctx) { s32* x_out = _arg<0, s32*>(rdram, ctx); s32* y_out = _arg<1, s32*>(rdram, ctx); - recomp::CameraInvertMode mode = recomp::get_camera_invert_mode(); + zelda64::CameraInvertMode mode = zelda64::get_camera_invert_mode(); - *x_out = (mode == recomp::CameraInvertMode::InvertX || mode == recomp::CameraInvertMode::InvertBoth); - *y_out = (mode == recomp::CameraInvertMode::InvertY || mode == recomp::CameraInvertMode::InvertBoth); + *x_out = (mode == zelda64::CameraInvertMode::InvertX || mode == zelda64::CameraInvertMode::InvertBoth); + *y_out = (mode == zelda64::CameraInvertMode::InvertY || mode == zelda64::CameraInvertMode::InvertBoth); } extern "C" void recomp_get_analog_inverted_axes(uint8_t* rdram, recomp_context* ctx) { s32* x_out = _arg<0, s32*>(rdram, ctx); s32* y_out = _arg<1, s32*>(rdram, ctx); - recomp::CameraInvertMode mode = recomp::get_analog_camera_invert_mode(); + zelda64::CameraInvertMode mode = zelda64::get_analog_camera_invert_mode(); - *x_out = (mode == recomp::CameraInvertMode::InvertX || mode == recomp::CameraInvertMode::InvertBoth); - *y_out = (mode == recomp::CameraInvertMode::InvertY || mode == recomp::CameraInvertMode::InvertBoth); + *x_out = (mode == zelda64::CameraInvertMode::InvertX || mode == zelda64::CameraInvertMode::InvertBoth); + *y_out = (mode == zelda64::CameraInvertMode::InvertY || mode == zelda64::CameraInvertMode::InvertBoth); } extern "C" void recomp_analog_cam_enabled(uint8_t* rdram, recomp_context* ctx) { - _return(ctx, recomp::get_analog_cam_mode() == recomp::AnalogCamMode::On); + _return(ctx, zelda64::get_analog_cam_mode() == zelda64::AnalogCamMode::On); } extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) { diff --git a/src/game/scene_table.cpp b/src/game/scene_table.cpp index 4301788..af24c7b 100644 --- a/src/game/scene_table.cpp +++ b/src/game/scene_table.cpp @@ -1,9 +1,9 @@ #include #include #include -#include "recomp_debug.h" +#include "zelda_debug.h" -std::vector recomp::game_warps { +std::vector zelda64::game_warps { { "Clock Town", { { 0, "Mayor's Residence", { diff --git a/src/main/main.cpp b/src/main/main.cpp index 3cc2893..b28f19c 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -6,11 +6,12 @@ #include #include #include +#include #include "nfd.h" -#include "../../ultramodern/ultra64.h" -#include "../../ultramodern/ultramodern.hpp" +#include "ultramodern/ultra64.h" +#include "ultramodern/ultramodern.hpp" #define SDL_MAIN_HANDLED #ifdef _WIN32 #include "SDL.h" @@ -21,9 +22,14 @@ #include "recomp_ui.h" #include "recomp_input.h" -#include "recomp_config.h" -#include "recomp_game.h" -#include "recomp_sound.h" +#include "zelda_config.h" +#include "zelda_sound.h" +#include "ovl_patches.hpp" +#include "librecomp/game.hpp" + +#ifdef HAS_MM_SHADER_CACHE +#include "mm_shader_cache.h" +#endif #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -192,7 +198,7 @@ void queue_samples(int16_t* audio_data, size_t sample_count) { // Convert the audio from 16-bit values to floats and swap the audio channels into the // swap buffer to correct for the address xor caused by endianness handling. - float cur_main_volume = recomp::get_main_volume() / 100.0f; // Get the current main volume, normalized to 0.0-1.0. + float cur_main_volume = zelda64::get_main_volume() / 100.0f; // Get the current main volume, normalized to 0.0-1.0. for (size_t i = 0; i < sample_count; i += input_channels) { swap_buffer[i + 0 + duplicated_input_frames * input_channels] = audio_data[i + 1] * (0.5f / 32768.0f) * cur_main_volume; swap_buffer[i + 1 + duplicated_input_frames * input_channels] = audio_data[i + 0] * (0.5f / 32768.0f) * cur_main_volume; @@ -302,6 +308,42 @@ void reset_audio(uint32_t output_freq) { update_audio_converter(); } +extern RspUcodeFunc njpgdspMain; +extern RspUcodeFunc aspMain; + +RspUcodeFunc* get_rsp_microcode(const OSTask* task) { + switch (task->t.type) { + case M_AUDTASK: + return aspMain; + + case M_NJPEGTASK: + return njpgdspMain; + + default: + fprintf(stderr, "Unknown task: %" PRIu32 "\n", task->t.type); + return nullptr; + } +} + +extern "C" void recomp_entrypoint(uint8_t * rdram, recomp_context * ctx); +gpr get_entrypoint_address(); + +// array of supported GameEntry objects +std::vector supported_games = { + { + .rom_hash = 0xEF18B4A9E2386169ULL, + .internal_name = "ZELDA MAJORA'S MASK", + .game_id = u8"mm.n64.us.1.0", +#ifdef HAS_MM_SHADER_CACHE + .cache_data = {mm_shader_cache_bytes, sizeof(mm_shader_cache_bytes)}, +#endif + .is_enabled = true, + .entrypoint_address = get_entrypoint_address(), + .entrypoint = recomp_entrypoint, + }, +}; + + int main(int argc, char** argv) { #ifdef _WIN32 @@ -333,7 +375,20 @@ int main(int argc, char** argv) { SDL_InitSubSystem(SDL_INIT_AUDIO); reset_audio(48000); - recomp::load_config(); + // Register supported games and patches + for (const auto& game : supported_games) { + recomp::register_game(game); + } + + zelda64::register_overlays(); + zelda64::register_patches(); + + recomp::register_config_path(zelda64::get_app_folder_path()); + zelda64::load_config(); + + recomp::rsp::callbacks_t rsp_callbacks{ + .get_rsp_microcode = get_rsp_microcode, + }; ultramodern::gfx_callbacks_t gfx_callbacks{ .create_gfx = create_gfx, @@ -353,8 +408,17 @@ int main(int argc, char** argv) { .set_rumble = recomp::set_rumble, }; - recomp::start({}, audio_callbacks, input_callbacks, gfx_callbacks); - + ultramodern::events::callbacks_t thread_callbacks{ + .vi_callback = recomp::update_rumble, + .gfx_init_callback = recompui::update_supported_options, + }; + + ultramodern::error_handling::callbacks_t error_handling_callbacks{ + .message_box = recompui::message_box, + }; + + recomp::start({}, rsp_callbacks, audio_callbacks, input_callbacks, gfx_callbacks, thread_callbacks, error_handling_callbacks); + NFD_Quit(); return EXIT_SUCCESS; diff --git a/src/main/register_overlays.cpp b/src/main/register_overlays.cpp new file mode 100644 index 0000000..034f64f --- /dev/null +++ b/src/main/register_overlays.cpp @@ -0,0 +1,19 @@ +#include "ovl_patches.hpp" +#include "../../RecompiledFuncs/recomp_overlays.inl" + +#include "librecomp/overlays.hpp" + +void zelda64::register_overlays() { + recomp::overlay_section_table_data_t sections { + .code_sections = section_table, + .num_code_sections = ARRLEN(section_table), + .total_num_sections = num_sections, + }; + + recomp::overlays_by_index_t overlays { + .table = overlay_sections_by_index, + .len = ARRLEN(overlay_sections_by_index), + }; + + recomp::register_overlays(sections, overlays); +} diff --git a/src/main/register_patches.cpp b/src/main/register_patches.cpp new file mode 100644 index 0000000..5076868 --- /dev/null +++ b/src/main/register_patches.cpp @@ -0,0 +1,12 @@ +#include "ovl_patches.hpp" +#include "../../RecompiledPatches/patches_bin.h" +#include "../../RecompiledPatches/recomp_overlays.inl" + +#include "librecomp/overlays.hpp" +#include "librecomp/game.hpp" + +void zelda64::register_patches() { + // TODO: This is a bit awful, maybe provide only one functions that does both in librecomp? + recomp::register_patch(mm_patches_bin, sizeof(mm_patches_bin)); + recomp::register_patch_section(section_table); +} diff --git a/src/recomp/ai.cpp b/src/recomp/ai.cpp deleted file mode 100644 index 44dbea2..0000000 --- a/src/recomp/ai.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "recomp.h" -#include -#include -#include "../ultramodern/ultra64.h" -#include "../ultramodern/ultramodern.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; - ultramodern::set_audio_frequency(freq); -} - -extern "C" void osAiSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) { - ultramodern::queue_audio_buffer(rdram, ctx->r4, ctx->r5); - ctx->r2 = 0; -} - -extern "C" void osAiGetLength_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = ultramodern::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/src/recomp/cont.cpp b/src/recomp/cont.cpp deleted file mode 100644 index 42492c3..0000000 --- a/src/recomp/cont.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "../ultramodern/ultramodern.hpp" -#include "recomp_helpers.h" - -static ultramodern::input_callbacks_t input_callbacks; - -std::chrono::high_resolution_clock::time_point input_poll_time; - -void update_poll_time() { - input_poll_time = std::chrono::high_resolution_clock::now(); -} - -extern "C" void recomp_set_current_frame_poll_id(uint8_t* rdram, recomp_context* ctx) { - // TODO reimplement the system for tagging polls with IDs to handle games with multithreaded input polling. -} - -extern "C" void recomp_measure_latency(uint8_t* rdram, recomp_context* ctx) { - ultramodern::measure_input_latency(); -} - -void ultramodern::measure_input_latency() { - // printf("Delta: %ld micros\n", std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - input_poll_time)); -} - -void set_input_callbacks(const ultramodern::input_callbacks_t& callbacks) { - input_callbacks = callbacks; -} - -static int max_controllers = 0; - -extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) { - PTR(void) bitpattern = _arg<1, PTR(void)>(rdram, ctx); - PTR(void) status = _arg<2, PTR(void)>(rdram, ctx); - - // 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 - } - - _return(ctx, 0); -} - -extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) { - if (input_callbacks.poll_input) { - input_callbacks.poll_input(); - } - update_poll_time(); - - ultramodern::send_si_message(rdram); -} - -extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) { - PTR(void) pad = _arg<0, PTR(void)>(rdram, ctx); - - uint16_t buttons = 0; - float x = 0.0f; - float y = 0.0f; - - if (input_callbacks.get_input) { - input_callbacks.get_input(&buttons, &x, &y); - } - - if (max_controllers > 0) { - // button - MEM_H(0, pad) = buttons; - // stick_x - MEM_B(2, pad) = (int8_t)(127 * x); - // stick_y - MEM_B(3, pad) = (int8_t)(127 * 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) { - ultramodern::send_si_message(rdram); -} - -extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) { - PTR(void) status = _arg<0, PTR(void)>(rdram, ctx); - - // Mark controller 0 as present - MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus) - MEM_B(2, status) = 0x01; // status: 0x01 (from joybus, indicates that a pak is plugged into the controller) - 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(_arg<0, u8>(rdram, ctx), u8(4)); - _return(ctx, 0); -} - -extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) { - PTR(void) pfs = _arg<0, PTR(void)>(rdram, ctx); - s32 flag = _arg<1, s32>(rdram, ctx); - s32 channel = MEM_W(8, pfs); - - // Only respect accesses to controller 0. - if (channel == 0) { - input_callbacks.set_rumble(flag); - } - - _return(ctx, 0); -} - -extern "C" void osMotorInit_recomp(uint8_t* rdram, recomp_context* ctx) { - PTR(void) pfs = _arg<1, PTR(void)>(rdram, ctx); - s32 channel = _arg<2, s32>(rdram, ctx); - MEM_W(8, pfs) = channel; - - _return(ctx, 0); -} - -extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) { - PTR(void) pfs = _arg<0, PTR(void)>(rdram, ctx); - s32 channel = MEM_W(8, pfs); - - // Only respect accesses to controller 0. - if (channel == 0) { - input_callbacks.set_rumble(true); - } - - _return(ctx, 0); -} - -extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) { - PTR(void) pfs = _arg<0, PTR(void)>(rdram, ctx); - s32 channel = MEM_W(8, pfs); - - // Only respect accesses to controller 0. - if (channel == 0) { - input_callbacks.set_rumble(false); - } - - _return(ctx, 0); -} diff --git a/src/recomp/dp.cpp b/src/recomp/dp.cpp deleted file mode 100644 index 3ad5327..0000000 --- a/src/recomp/dp.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "recomp.h" - -enum class RDPStatusBit { - XbusDmem = 0, - Freeze = 1, - Flush = 2, - CommandBusy = 6, - BufferReady = 7, - DmaBusy = 8, - EndValid = 9, - StartValid = 10, -}; - -constexpr void update_bit(uint32_t& state, uint32_t flags, RDPStatusBit bit) { - int set_bit_pos = (int)bit * 2 + 0; - int reset_bit_pos = (int)bit * 2 + 1; - bool set = (flags & (1U << set_bit_pos)) != 0; - bool reset = (flags & (1U << reset_bit_pos)) != 0; - - if (set ^ reset) { - if (set) { - state |= (1U << (int)bit); - } - else { - state &= ~(1U << (int)bit); - } - } -} - -uint32_t rdp_state = 1 << (int)RDPStatusBit::BufferReady; - -extern "C" void osDpSetNextBuffer_recomp(uint8_t* rdram, recomp_context* ctx) { - assert(false); -} - -extern "C" void osDpGetStatus_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = rdp_state; -} - -extern "C" void osDpSetStatus_recomp(uint8_t* rdram, recomp_context* ctx) { - update_bit(rdp_state, ctx->r4, RDPStatusBit::XbusDmem); - update_bit(rdp_state, ctx->r4, RDPStatusBit::Freeze); - update_bit(rdp_state, ctx->r4, RDPStatusBit::Flush); -} diff --git a/src/recomp/eep.cpp b/src/recomp/eep.cpp deleted file mode 100644 index ddc612d..0000000 --- a/src/recomp/eep.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "recomp.h" -#include "../ultramodern/ultra64.h" - -void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count); -void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count); - -constexpr int eeprom_block_size = 8; -constexpr int eep4_size = 4096; -constexpr int eep4_block_count = eep4_size / eeprom_block_size; -constexpr int eep16_size = 16384; -constexpr int eep16_block_count = eep16_size / eeprom_block_size; - -extern "C" void osEepromProbe_recomp(uint8_t* rdram, recomp_context* ctx) { - ctx->r2 = 0x02; // EEP16K -} - -extern "C" void osEepromWrite_recomp(uint8_t* rdram, recomp_context* ctx) { - assert(false);// ctx->r2 = 8; // CONT_NO_RESPONSE_ERROR -} - -extern "C" void osEepromLongWrite_recomp(uint8_t* rdram, recomp_context* ctx) { - uint8_t eep_address = ctx->r5; - gpr buffer = ctx->r6; - int32_t nbytes = ctx->r7; - - assert(!(nbytes & 7)); - assert(eep_address * eeprom_block_size + nbytes <= eep16_size); - - save_write(rdram, buffer, eep_address * eeprom_block_size, nbytes); - - ctx->r2 = 0; -} - -extern "C" void osEepromRead_recomp(uint8_t* rdram, recomp_context* ctx) { - assert(false);// ctx->r2 = 8; // CONT_NO_RESPONSE_ERROR -} - -extern "C" void osEepromLongRead_recomp(uint8_t* rdram, recomp_context* ctx) { - uint8_t eep_address = ctx->r5; - gpr buffer = ctx->r6; - int32_t nbytes = ctx->r7; - - assert(!(nbytes & 7)); - assert(eep_address * eeprom_block_size + nbytes <= eep16_size); - - save_read(rdram, buffer, eep_address * eeprom_block_size, nbytes); - - ctx->r2 = 0; -} diff --git a/src/recomp/euc-jp.cpp b/src/recomp/euc-jp.cpp deleted file mode 100644 index dbeb2fe..0000000 --- a/src/recomp/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/src/recomp/euc-jp.h b/src/recomp/euc-jp.h deleted file mode 100644 index 9ee246f..0000000 --- a/src/recomp/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/src/recomp/files.cpp b/src/recomp/files.cpp deleted file mode 100644 index fa4ed70..0000000 --- a/src/recomp/files.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "recomp_files.h" - -constexpr std::u8string_view backup_suffix = u8".bak"; -constexpr std::u8string_view temp_suffix = u8".temp"; - -std::ifstream recomp::open_input_backup_file(const std::filesystem::path& filepath, std::ios_base::openmode mode) { - std::filesystem::path backup_path{filepath}; - backup_path += backup_suffix; - return std::ifstream{backup_path, mode}; -} - -std::ifstream recomp::open_input_file_with_backup(const std::filesystem::path& filepath, std::ios_base::openmode mode) { - std::ifstream ret{filepath, mode}; - - // Check if the file failed to open and open the corresponding backup file instead if so. - if (!ret.good()) { - return open_input_backup_file(filepath, mode); - } - - return ret; -} - -std::ofstream recomp::open_output_file_with_backup(const std::filesystem::path& filepath, std::ios_base::openmode mode) { - std::filesystem::path temp_path{filepath}; - temp_path += temp_suffix; - std::ofstream temp_file_out{ temp_path, mode }; - - return temp_file_out; -} - -bool recomp::finalize_output_file_with_backup(const std::filesystem::path& filepath) { - std::filesystem::path backup_path{filepath}; - backup_path += backup_suffix; - - std::filesystem::path temp_path{filepath}; - temp_path += temp_suffix; - - std::error_code ec; - if (std::filesystem::exists(filepath, ec)) { - std::filesystem::copy_file(filepath, backup_path, std::filesystem::copy_options::overwrite_existing, ec); - if (ec) { - return false; - } - } - std::filesystem::copy_file(temp_path, filepath, std::filesystem::copy_options::overwrite_existing, ec); - if (ec) { - return false; - } - std::filesystem::remove(temp_path, ec); - return true; -} diff --git a/src/recomp/flash.cpp b/src/recomp/flash.cpp deleted file mode 100644 index 519eaa3..0000000 --- a/src/recomp/flash.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include -#include "../ultramodern/ultra64.h" -#include "../ultramodern/ultramodern.hpp" -#include "recomp.h" - -// TODO move this out into ultramodern code - -constexpr uint32_t flash_size = 1024 * 1024 / 8; // 1Mbit -constexpr uint32_t page_size = 128; -constexpr uint32_t pages_per_sector = 128; -constexpr uint32_t page_count = flash_size / page_size; -constexpr uint32_t sector_size = page_size * pages_per_sector; -constexpr uint32_t sector_count = flash_size / sector_size; - -void save_write_ptr(const void* in, uint32_t offset, uint32_t count); -void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count); -void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count); -void save_clear(uint32_t start, uint32_t size, char value); - -std::array write_buffer; - -extern "C" void osFlashInit_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = ultramodern::flash_handle; -} - -extern "C" void osFlashReadStatus_recomp(uint8_t * rdram, recomp_context * ctx) { - PTR(u8) flash_status = ctx->r4; - - MEM_B(0, flash_status) = 0; -} - -extern "C" void osFlashReadId_recomp(uint8_t * rdram, recomp_context * ctx) { - PTR(u32) flash_type = ctx->r4; - PTR(u32) flash_maker = ctx->r5; - - // Mimic a real flash chip's type and maker, as some games actually check if one is present. - MEM_W(0, flash_type) = 0x11118001; - MEM_W(0, flash_maker) = 0x00C2001E; -} - -extern "C" void osFlashClearStatus_recomp(uint8_t * rdram, recomp_context * ctx) { - -} - -extern "C" void osFlashAllErase_recomp(uint8_t * rdram, recomp_context * ctx) { - save_clear(0, ultramodern::save_size, 0xFF); - - ctx->r2 = 0; -} - -extern "C" void osFlashAllEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) { - save_clear(0, ultramodern::save_size, 0xFF); - - ctx->r2 = 0; -} - -// This function is named sector but really means page. -extern "C" void osFlashSectorErase_recomp(uint8_t * rdram, recomp_context * ctx) { - uint32_t page_num = (uint32_t)ctx->r4; - - // Prevent out of bounds erase - if (page_num >= page_count) { - ctx->r2 = -1; - return; - } - - save_clear(page_num * page_size, page_size, 0xFF); - - ctx->r2 = 0; -} - -// Same naming issue as above. -extern "C" void osFlashSectorEraseThrough_recomp(uint8_t * rdram, recomp_context * ctx) { - uint32_t page_num = (uint32_t)ctx->r4; - - // Prevent out of bounds erase - if (page_num >= page_count) { - ctx->r2 = -1; - return; - } - - save_clear(page_num * page_size, page_size, 0xFF); - - ctx->r2 = 0; -} - -extern "C" void osFlashCheckEraseEnd_recomp(uint8_t * rdram, recomp_context * ctx) { - // All erases are blocking in this implementation, so this should always return OK. - ctx->r2 = 0; // FLASH_STATUS_ERASE_OK -} - -extern "C" void osFlashWriteBuffer_recomp(uint8_t * rdram, recomp_context * ctx) { - OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r4); - int32_t pri = ctx->r5; - PTR(void) dramAddr = ctx->r6; - PTR(OSMesgQueue) mq = ctx->r7; - - // Copy the input data into the write buffer - for (size_t i = 0; i < page_size; i++) { - write_buffer[i] = MEM_B(i, dramAddr); - } - - // Send the message indicating write completion - osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK); - - ctx->r2 = 0; -} - -extern "C" void osFlashWriteArray_recomp(uint8_t * rdram, recomp_context * ctx) { - uint32_t page_num = ctx->r4; - - // Copy the write buffer into the save file - save_write_ptr(write_buffer.data(), page_num * page_size, page_size); - - ctx->r2 = 0; -} - -extern "C" void osFlashReadArray_recomp(uint8_t * rdram, recomp_context * ctx) { - OSIoMesg* mb = TO_PTR(OSIoMesg, ctx->r4); - int32_t pri = ctx->r5; - uint32_t page_num = ctx->r6; - PTR(void) dramAddr = ctx->r7; - uint32_t n_pages = MEM_W(0x10, ctx->r29); - PTR(OSMesgQueue) mq = MEM_W(0x14, ctx->r29); - - uint32_t offset = page_num * page_size; - uint32_t count = n_pages * page_size; - - // Read from the save file into the provided buffer - save_read(PASS_RDRAM dramAddr, offset, count); - - // Send the message indicating read completion - osSendMesg(PASS_RDRAM mq, 0, OS_MESG_NOBLOCK); - - ctx->r2 = 0; -} - -extern "C" void osFlashChange_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} diff --git a/src/recomp/math_routines.cpp b/src/recomp/math_routines.cpp deleted file mode 100644 index f5117c5..0000000 --- a/src/recomp/math_routines.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "../ultramodern/ultramodern.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/src/recomp/overlays.cpp b/src/recomp/overlays.cpp deleted file mode 100644 index 3295552..0000000 --- a/src/recomp/overlays.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include -#include -#include -#include "recomp.h" -#include "recomp_overlays.h" -#include "../RecompiledFuncs/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; - - LoadedSection(int32_t loaded_ram_addr_, size_t section_table_index_) { - loaded_ram_addr = loaded_ram_addr_; - section_table_index = 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; -} - -void load_special_overlay(const SectionTableEntry& section, int32_t ram) { - 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; - } -} - - -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); - -extern "C" void unload_overlay_by_id(uint32_t id) { - uint32_t section_table_index = overlay_sections_by_index[id]; - const SectionTableEntry& section = section_table[section_table_index]; - - auto find_it = std::find_if(loaded_sections.begin(), loaded_sections.end(), [section_table_index](const LoadedSection& s) { return s.section_table_index == section_table_index; }); - - if (find_it != loaded_sections.end()) { - // 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 + find_it->loaded_ram_addr; - func_map.erase(func_address); - } - // Reset the section's address in the address table - section_addresses[section.index] = section.ram_addr; - // Remove the section from the loaded section map - loaded_sections.erase(find_it); - } -} - -extern "C" void load_overlay_by_id(uint32_t id, uint32_t ram_addr) { - uint32_t section_table_index = overlay_sections_by_index[id]; - const SectionTableEntry& section = section_table[section_table_index]; - int32_t prev_address = section_addresses[section.index]; - if (/*ram_addr >= 0x80000000 && ram_addr < 0x81000000) {*/ prev_address == section.ram_addr) { - load_overlay(section_table_index, ram_addr); - } - else { - int32_t new_address = prev_address + ram_addr; - unload_overlay_by_id(id); - load_overlay(section_table_index, new_address); - } -} - -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); - assert(false); - 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] = section.ram_addr; - // Remove the section from the loaded section map - it = loaded_sections.erase(it); - // Skip incrementing the iterator - continue; - } - ++it; - } -} - -void load_patch_functions(); - -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; - } - ); - - load_patch_functions(); -} - -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); - assert(false); - std::exit(EXIT_FAILURE); - } - return func_find->second; -} - diff --git a/src/recomp/pak.cpp b/src/recomp/pak.cpp deleted file mode 100644 index bf50132..0000000 --- a/src/recomp/pak.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "recomp.h" -#include "../ultramodern/ultra64.h" -#include "../ultramodern/ultramodern.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 -} - -extern "C" void osPfsChecker_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 1; // PFS_ERR_NOPACK -} diff --git a/src/recomp/patch_loading.cpp b/src/recomp/patch_loading.cpp deleted file mode 100644 index 225bef9..0000000 --- a/src/recomp/patch_loading.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include -#include -#include "recomp.h" -#include "../../RecompiledPatches/recomp_overlays.inl" - -void load_special_overlay(const SectionTableEntry& section, int32_t ram); - -void load_patch_functions() { - load_special_overlay(section_table[0], section_table[0].ram_addr); -} diff --git a/src/recomp/pi.cpp b/src/recomp/pi.cpp deleted file mode 100644 index e4e2599..0000000 --- a/src/recomp/pi.cpp +++ /dev/null @@ -1,302 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "recomp.h" -#include "recomp_game.h" -#include "recomp_config.h" -#include "recomp_files.h" -#include "../ultramodern/ultra64.h" -#include "../ultramodern/ultramodern.hpp" - -static std::vector rom; - -bool recomp::is_rom_loaded() { - return !rom.empty(); -} - -void recomp::set_rom_contents(std::vector&& new_rom) { - rom = std::move(new_rom); -} - -// 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 drive_base = 0x06000000; - -constexpr uint32_t k1_to_phys(uint32_t addr) { - return addr & 0x1FFFFFFF; -} - -constexpr uint32_t phys_to_k1(uint32_t addr) { - return addr | 0xA0000000; -} - -extern "C" void __osPiGetAccess_recomp(uint8_t* rdram, recomp_context* ctx) { -} - -extern "C" void __osPiRelAccess_recomp(uint8_t* rdram, recomp_context* ctx) { -} - -extern "C" void osCartRomInit_recomp(uint8_t* rdram, recomp_context* ctx) { - OSPiHandle* handle = TO_PTR(OSPiHandle, ultramodern::cart_handle); - handle->type = 0; // cart - handle->baseAddress = phys_to_k1(rom_base); - handle->domain = 0; - - ctx->r2 = (gpr)ultramodern::cart_handle; -} - -extern "C" void osDriveRomInit_recomp(uint8_t * rdram, recomp_context * ctx) { - OSPiHandle* handle = TO_PTR(OSPiHandle, ultramodern::drive_handle); - handle->type = 1; // bulk - handle->baseAddress = phys_to_k1(drive_base); - handle->domain = 0; - - ctx->r2 = (gpr)ultramodern::drive_handle; -} - -extern "C" void osCreatePiManager_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -void recomp::do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes) { - // TODO use word copies when possible - - // TODO handle misaligned DMA - assert((physical_addr & 0x1) == 0 && "Only PI DMA from aligned ROM addresses is currently supported"); - assert((ram_address & 0x7) == 0 && "Only PI DMA to aligned RDRAM addresses is currently supported"); - assert((num_bytes & 0x1) == 0 && "Only PI DMA with aligned sizes is currently supported"); - uint8_t* rom_addr = rom.data() + physical_addr - rom_base; - for (size_t i = 0; i < num_bytes; i++) { - MEM_B(i, ram_address) = *rom_addr; - rom_addr++; - } -} - -void recomp::do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr) { - assert((physical_addr & 0x3) == 0 && "PIO not 4-byte aligned in device, currently unsupported"); - assert((ram_address & 0x3) == 0 && "PIO not 4-byte aligned in RDRAM, currently unsupported"); - uint8_t* rom_addr = rom.data() + physical_addr - rom_base; - MEM_B(0, ram_address) = *rom_addr++; - MEM_B(1, ram_address) = *rom_addr++; - MEM_B(2, ram_address) = *rom_addr++; - MEM_B(3, ram_address) = *rom_addr++; -} - -struct { - std::array save_buffer; - std::thread saving_thread; - moodycamel::LightweightSemaphore write_sempahore; - std::mutex save_buffer_mutex; -} save_context; - -const std::u8string save_folder = u8"saves"; -const std::u8string save_filename = std::u8string{recomp::mm_game_id} + u8".bin"; - -std::filesystem::path get_save_file_path() { - return recomp::get_app_folder_path() / save_folder / save_filename; -} - -void update_save_file() { - bool saving_failed = false; - { - std::ofstream save_file = recomp::open_output_file_with_backup(get_save_file_path(), std::ios_base::binary); - - if (save_file.good()) { - std::lock_guard lock{ save_context.save_buffer_mutex }; - save_file.write(save_context.save_buffer.data(), save_context.save_buffer.size()); - } - else { - saving_failed = false; - } - } - if (!saving_failed) { - saving_failed = !recomp::finalize_output_file_with_backup(get_save_file_path()); - } - if (saving_failed) { - recomp::message_box("Failed to write to the save file. Check your file permissions and whether the save folder has been moved to Dropbox or similar, as this can cause issues."); - } -} - -extern std::atomic_bool exited; - -void saving_thread_func(RDRAM_ARG1) { - while (!exited) { - bool save_buffer_updated = false; - // Repeatedly wait for a new action to be sent. - constexpr int64_t wait_time_microseconds = 10000; - constexpr int max_actions = 128; - int num_actions = 0; - - // Wait up to the given timeout for a write to come in. Allow multiple writes to coalesce together into a single save. - // Cap the number of coalesced writes to guarantee that the save buffer eventually gets written out to the file even if the game - // is constantly sending writes. - while (save_context.write_sempahore.wait(wait_time_microseconds) && num_actions < max_actions) { - save_buffer_updated = true; - num_actions++; - } - - // If an action came through that affected the save file, save the updated contents. - if (save_buffer_updated) { - update_save_file(); - } - } -} - -void save_write_ptr(const void* in, uint32_t offset, uint32_t count) { - { - std::lock_guard lock { save_context.save_buffer_mutex }; - memcpy(&save_context.save_buffer[offset], in, count); - } - - save_context.write_sempahore.signal(); -} - -void save_write(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { - { - std::lock_guard lock { save_context.save_buffer_mutex }; - for (uint32_t i = 0; i < count; i++) { - save_context.save_buffer[offset + i] = MEM_B(i, rdram_address); - } - } - - save_context.write_sempahore.signal(); -} - -void save_read(RDRAM_ARG PTR(void) rdram_address, uint32_t offset, uint32_t count) { - std::lock_guard lock { save_context.save_buffer_mutex }; - for (size_t i = 0; i < count; i++) { - MEM_B(i, rdram_address) = save_context.save_buffer[offset + i]; - } -} - -void save_clear(uint32_t start, uint32_t size, char value) { - { - std::lock_guard lock { save_context.save_buffer_mutex }; - std::fill_n(save_context.save_buffer.begin() + start, size, value); - } - - save_context.write_sempahore.signal(); -} - -void ultramodern::init_saving(RDRAM_ARG1) { - std::filesystem::path save_file_path = get_save_file_path(); - - // Ensure the save file directory exists. - std::filesystem::create_directories(save_file_path.parent_path()); - - // Read the save file if it exists. - std::ifstream save_file = recomp::open_input_file_with_backup(save_file_path, std::ios_base::binary); - if (save_file.good()) { - save_file.read(save_context.save_buffer.data(), save_context.save_buffer.size()); - } - else { - // Otherwise clear the save file to all zeroes. - save_context.save_buffer.fill(0); - } - - save_context.saving_thread = std::thread{saving_thread_func, PASS_RDRAM}; -} - -void ultramodern::join_saving_thread() { - save_context.saving_thread.join(); -} - -void do_dma(RDRAM_ARG 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 - recomp::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(RDRAM_ARG recomp_context* ctx) { - uint32_t mb = ctx->r4; - uint32_t pri = ctx->r5; - uint32_t direction = ctx->r6; - uint32_t devAddr = ctx->r7 | rom_base; - 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(PASS_RDRAM mq, dramAddr, physical_addr, size, direction); - - ctx->r2 = 0; -} - -extern "C" void osEPiStartDma_recomp(RDRAM_ARG 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(PASS_RDRAM mq, dramAddr, physical_addr, size, direction); - - ctx->r2 = 0; -} - -extern "C" void osEPiReadIo_recomp(RDRAM_ARG 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 - recomp::do_rom_pio(PASS_RDRAM dramAddr, physical_addr); - } else { - // sram - assert(false && "SRAM ReadIo unimplemented"); - } - - ctx->r2 = 0; -} - -extern "C" void osPiGetStatus_recomp(RDRAM_ARG recomp_context * ctx) { - ctx->r2 = 0; -} - -extern "C" void osPiRawStartDma_recomp(RDRAM_ARG recomp_context * ctx) { - ctx->r2 = 0; -} diff --git a/src/recomp/print.cpp b/src/recomp/print.cpp deleted file mode 100644 index b0ea771..0000000 --- a/src/recomp/print.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include - -#include "../ultramodern/ultra64.h" -#include "../ultramodern/ultramodern.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/src/recomp/recomp.cpp b/src/recomp/recomp.cpp deleted file mode 100644 index 8135514..0000000 --- a/src/recomp/recomp.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#ifdef _WIN32 -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "recomp.h" -#include "recomp_overlays.h" -#include "recomp_game.h" -#include "recomp_config.h" -#include "xxHash/xxh3.h" -#include "../ultramodern/ultramodern.hpp" -#include "../../RecompiledPatches/patches_bin.h" -#ifdef HAS_MM_SHADER_CACHE -#include "mm_shader_cache.h" -#endif - -#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 - -struct RomEntry { - uint64_t xxhash3_value; - std::u8string stored_filename; - std::string internal_rom_name; -}; - -const std::unordered_map game_roms { - { recomp::Game::MM, { 0xEF18B4A9E2386169ULL, std::u8string{recomp::mm_game_id} + u8".z64", "ZELDA MAJORA'S MASK" }}, -}; - -bool check_hash(const std::vector& rom_data, uint64_t expected_hash) { - uint64_t calculated_hash = XXH3_64bits(rom_data.data(), rom_data.size()); - - return calculated_hash == expected_hash; -} - -static std::vector read_file(const std::filesystem::path& path) { - std::vector ret; - - std::ifstream file{ path, std::ios::binary}; - - if (file.good()) { - file.seekg(0, std::ios::end); - ret.resize(file.tellg()); - file.seekg(0, std::ios::beg); - - file.read(reinterpret_cast(ret.data()), ret.size()); - } - - return ret; -} - -bool write_file(const std::filesystem::path& path, const std::vector& data) { - std::ofstream out_file{ path, std::ios::binary }; - - if (!out_file.good()) { - return false; - } - - out_file.write(reinterpret_cast(data.data()), data.size()); - - return true; -} - -bool check_stored_rom(const RomEntry& game_entry) { - - std::vector stored_rom_data = read_file(recomp::get_app_folder_path() / game_entry.stored_filename); - - if (!check_hash(stored_rom_data, game_entry.xxhash3_value)) { - // Incorrect hash, remove the stored ROM file if it exists. - std::filesystem::remove(recomp::get_app_folder_path() / game_entry.stored_filename); - return false; - } - - return true; -} - -static std::unordered_set valid_game_roms; - -bool recomp::is_rom_valid(recomp::Game game) { - return valid_game_roms.contains(game); -} - -void recomp::check_all_stored_roms() { - for (const auto& cur_rom_entry: game_roms) { - if (check_stored_rom(cur_rom_entry.second)) { - valid_game_roms.insert(cur_rom_entry.first); - } - } -} - -bool recomp::load_stored_rom(recomp::Game game) { - auto find_it = game_roms.find(game); - - if (find_it == game_roms.end()) { - return false; - } - - std::vector stored_rom_data = read_file(recomp::get_app_folder_path() / find_it->second.stored_filename); - - if (!check_hash(stored_rom_data, find_it->second.xxhash3_value)) { - // The ROM no longer has the right hash, delete it. - std::filesystem::remove(recomp::get_app_folder_path() / find_it->second.stored_filename); - return false; - } - - recomp::set_rom_contents(std::move(stored_rom_data)); - return true; -} - -const std::array first_rom_bytes { 0x80, 0x37, 0x12, 0x40 }; - -enum class ByteswapType { - NotByteswapped, - Byteswapped4, - Byteswapped2, - Invalid -}; - -ByteswapType check_rom_start(const std::vector& rom_data) { - if (rom_data.size() < 4) { - return ByteswapType::Invalid; - } - - bool matched_all = true; - - auto check_match = [&](uint8_t index0, uint8_t index1, uint8_t index2, uint8_t index3) { - return - rom_data[0] == first_rom_bytes[index0] && - rom_data[1] == first_rom_bytes[index1] && - rom_data[2] == first_rom_bytes[index2] && - rom_data[3] == first_rom_bytes[index3]; - }; - - // Check if the ROM is already in the correct byte order. - if (check_match(0,1,2,3)) { - return ByteswapType::NotByteswapped; - } - - // Check if the ROM has been byteswapped in groups of 4 bytes. - if (check_match(3,2,1,0)) { - return ByteswapType::Byteswapped4; - } - - // Check if the ROM has been byteswapped in groups of 2 bytes. - if (check_match(1,0,3,2)) { - return ByteswapType::Byteswapped2; - } - - // No match found. - return ByteswapType::Invalid; -} - -void byteswap_data(std::vector& rom_data, size_t index_xor) { - for (size_t rom_pos = 0; rom_pos < rom_data.size(); rom_pos += 4) { - uint8_t temp0 = rom_data[rom_pos + 0]; - uint8_t temp1 = rom_data[rom_pos + 1]; - uint8_t temp2 = rom_data[rom_pos + 2]; - uint8_t temp3 = rom_data[rom_pos + 3]; - - rom_data[rom_pos + (0 ^ index_xor)] = temp0; - rom_data[rom_pos + (1 ^ index_xor)] = temp1; - rom_data[rom_pos + (2 ^ index_xor)] = temp2; - rom_data[rom_pos + (3 ^ index_xor)] = temp3; - } -} - -recomp::RomValidationError recomp::select_rom(const std::filesystem::path& rom_path, Game game) { - auto find_it = game_roms.find(game); - - if (find_it == game_roms.end()) { - return recomp::RomValidationError::OtherError; - } - - const RomEntry& game_entry = find_it->second; - - std::vector rom_data = read_file(rom_path); - - if (rom_data.empty()) { - return recomp::RomValidationError::FailedToOpen; - } - - // Pad the rom to the nearest multiple of 4 bytes. - rom_data.resize((rom_data.size() + 3) & ~3); - - ByteswapType byteswap_type = check_rom_start(rom_data); - - switch (byteswap_type) { - case ByteswapType::Invalid: - return recomp::RomValidationError::NotARom; - case ByteswapType::Byteswapped2: - byteswap_data(rom_data, 1); - break; - case ByteswapType::Byteswapped4: - byteswap_data(rom_data, 3); - break; - case ByteswapType::NotByteswapped: - break; - } - - if (!check_hash(rom_data, game_entry.xxhash3_value)) { - const std::string_view name{ reinterpret_cast(rom_data.data()) + 0x20, game_entry.internal_rom_name.size()}; - if (name == game_entry.internal_rom_name) { - return recomp::RomValidationError::IncorrectVersion; - } - else { - if (game == recomp::Game::MM && std::string_view{ reinterpret_cast(rom_data.data()) + 0x20, 19 } == "THE LEGEND OF ZELDA") { - return recomp::RomValidationError::NotYet; - } - else { - return recomp::RomValidationError::IncorrectRom; - } - } - } - - write_file(recomp::get_app_folder_path() / game_entry.stored_filename, rom_data); - - return recomp::RomValidationError::Good; -} - -extern "C" void osGetMemSize_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 8 * 1024 * 1024; -} - -enum class StatusReg { - FR = 0x04000000, -}; - -extern "C" void cop0_status_write(recomp_context* ctx, gpr value) { - uint32_t old_sr = ctx->status_reg; - uint32_t new_sr = (uint32_t)value; - uint32_t changed = old_sr ^ new_sr; - - // Check if the FR bit changed - if (changed & (uint32_t)StatusReg::FR) { - // Check if the FR bit was set - if (new_sr & (uint32_t)StatusReg::FR) { - // FR = 1, odd single floats point to their own registers - ctx->f_odd = &ctx->f1.u32l; - ctx->mips3_float_mode = true; - } - // Otherwise, it was cleared - else { - // FR = 0, odd single floats point to the upper half of the previous register - ctx->f_odd = &ctx->f0.u32h; - ctx->mips3_float_mode = false; - } - - // Remove the FR bit from the changed bits as it's been handled - changed &= ~(uint32_t)StatusReg::FR; - } - - // If any other bits were changed, assert false as they're not handled currently - if (changed) { - printf("Unhandled status register bits changed: 0x%08X\n", changed); - assert(false); - exit(EXIT_FAILURE); - } - - // Update the status register in the context - ctx->status_reg = new_sr; -} - -extern "C" gpr cop0_status_read(recomp_context* ctx) { - return (gpr)(int32_t)ctx->status_reg; -} - -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); - assert(false); - exit(EXIT_FAILURE); -} - -extern "C" void do_break(uint32_t vram) { - printf("Encountered break at original vram 0x%08X\n", vram); - assert(false); - 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; - ctx.mips3_float_mode = 0; - ctx.f_odd = &ctx.f0.u32h; - recomp_func_t* func = get_function(addr); - func(rdram, &ctx); -} - -// Recomp generation functions -extern "C" void recomp_entrypoint(uint8_t * rdram, recomp_context * ctx); -gpr get_entrypoint_address(); -const char* get_rom_name(); - -void read_patch_data(uint8_t* rdram, gpr patch_data_address) { - for (size_t i = 0; i < sizeof(mm_patches_bin); i++) { - MEM_B(i, patch_data_address) = mm_patches_bin[i]; - } -} - -void init(uint8_t* rdram, recomp_context* ctx) { - // 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); - - // Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000) - recomp::do_rom_read(rdram, entrypoint, 0x10001000, 0x100000); - - // Read in any extra data from patches - read_patch_data(rdram, (gpr)(s32)0x80801000); - - // Set up stack pointer - ctx->r29 = 0xFFFFFFFF803FFFF0u; - - // Set up context floats - ctx->f_odd = &ctx->f0.u32h; - ctx->mips3_float_mode = false; - - // 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; - 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 -} - -std::atomic game_started = recomp::Game::None; - -void recomp::start_game(recomp::Game game) { - game_started.store(game); - game_started.notify_all(); -} - -bool ultramodern::is_game_started() { - return game_started.load() != recomp::Game::None; -} - -void set_audio_callbacks(const ultramodern::audio_callbacks_t& callbacks); -void set_input_callbacks(const ultramodern::input_callbacks_t& callback); - -std::atomic_bool exited = false; - -void ultramodern::quit() { - exited.store(true); - recomp::Game desired = recomp::Game::None; - game_started.compare_exchange_strong(desired, recomp::Game::Quit); - game_started.notify_all(); -} - -void recomp::start(ultramodern::WindowHandle window_handle, const ultramodern::audio_callbacks_t& audio_callbacks, const ultramodern::input_callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks_) { - recomp::check_all_stored_roms(); - set_audio_callbacks(audio_callbacks); - set_input_callbacks(input_callbacks); - - ultramodern::gfx_callbacks_t gfx_callbacks = gfx_callbacks_; - - ultramodern::gfx_callbacks_t::gfx_data_t gfx_data{}; - - if (gfx_callbacks.create_gfx) { - gfx_data = gfx_callbacks.create_gfx(); - } - - if (window_handle == ultramodern::WindowHandle{}) { - if (gfx_callbacks.create_window) { - window_handle = gfx_callbacks.create_window(gfx_data); - } - else { - assert(false && "No create_window callback provided"); - } - } - - // Allocate rdram_buffer - std::unique_ptr rdram_buffer = std::make_unique(ultramodern::rdram_size); - std::memset(rdram_buffer.get(), 0, ultramodern::rdram_size); - - std::thread game_thread{[](ultramodern::WindowHandle window_handle, uint8_t* rdram) { - debug_printf("[Recomp] Starting\n"); - - ultramodern::set_native_thread_name("Game Start Thread"); - - ultramodern::preinit(rdram, window_handle); - - game_started.wait(recomp::Game::None); - recomp_context context{}; - - switch (game_started.load()) { - case recomp::Game::MM: - if (!recomp::load_stored_rom(recomp::Game::MM)) { - recomp::message_box("Error opening stored ROM! Please restart this program."); - } - - #ifdef HAS_MM_SHADER_CACHE - ultramodern::load_shader_cache({mm_shader_cache_bytes, sizeof(mm_shader_cache_bytes)}); - #endif - - init(rdram, &context); - try { - recomp_entrypoint(rdram, &context); - } catch (ultramodern::thread_terminated& terminated) { - - } - break; - case recomp::Game::Quit: - break; - } - - debug_printf("[Recomp] Quitting\n"); - }, window_handle, rdram_buffer.get()}; - - while (!exited) { - ultramodern::sleep_milliseconds(1); - if (gfx_callbacks.update_gfx != nullptr) { - gfx_callbacks.update_gfx(gfx_data); - } - } - game_thread.join(); - ultramodern::join_event_threads(); - ultramodern::join_thread_cleaner_thread(); - ultramodern::join_saving_thread(); -} diff --git a/src/recomp/sp.cpp b/src/recomp/sp.cpp deleted file mode 100644 index b3ee63f..0000000 --- a/src/recomp/sp.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include -#include -#include "../ultramodern/ultramodern.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); - snprintf(addr_str, sizeof(addr_str) - 1, "%08X", task->t.data_ptr); - addr_str[sizeof(addr_str) - 1] = '\0'; - 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; - } - ultramodern::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/src/recomp/ultra_stubs.cpp b/src/recomp/ultra_stubs.cpp deleted file mode 100644 index f2ac704..0000000 --- a/src/recomp/ultra_stubs.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "../ultramodern/ultra64.h" -#include "../ultramodern/ultramodern.hpp" -#include "recomp.h" - -// None of these functions need to be reimplemented, so stub them out -extern "C" void osUnmapTLBAll_recomp(uint8_t * rdram, recomp_context * ctx) { - // TODO this will need to be implemented in the future for any games that actually use the TLB -} - -extern "C" void osVoiceInit_recomp(uint8_t * rdram, recomp_context * ctx) { - ctx->r2 = 11; // CONT_ERR_DEVICE -} - -extern "C" void osVoiceSetWord_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} - -extern "C" void osVoiceCheckWord_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} - -extern "C" void osVoiceStopReadData_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} - -extern "C" void osVoiceMaskDictionary_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} - -extern "C" void osVoiceStartReadData_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} - -extern "C" void osVoiceControlGain_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} - -extern "C" void osVoiceGetReadData_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} - -extern "C" void osVoiceClearDictionary_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); -} - diff --git a/src/recomp/ultra_translation.cpp b/src/recomp/ultra_translation.cpp deleted file mode 100644 index 771758f..0000000 --- a/src/recomp/ultra_translation.cpp +++ /dev/null @@ -1,163 +0,0 @@ -#include -#include "../ultramodern/ultra64.h" -#include "../ultramodern/ultramodern.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 osYieldThread_recomp(uint8_t * rdram, recomp_context * ctx) { - assert(false); - // osYieldThread(rdram); -} - -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->r4); -} - -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 == ultramodern::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 ultramodern::this_thread(); -//} diff --git a/src/recomp/vi.cpp b/src/recomp/vi.cpp deleted file mode 100644 index 309e984..0000000 --- a/src/recomp/vi.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "../ultramodern/ultramodern.hpp" -#include "recomp.h" - -extern "C" void osViSetYScale_recomp(uint8_t* rdram, recomp_context * ctx) { - osViSetYScale(ctx->f12.fl); -} - -extern "C" void osViSetXScale_recomp(uint8_t* rdram, recomp_context * ctx) { - osViSetXScale(ctx->f12.fl); -} - -extern "C" void osCreateViManager_recomp(uint8_t* rdram, recomp_context* ctx) { - ; -} - -extern "C" void osViBlack_recomp(uint8_t* rdram, recomp_context* ctx) { - osViBlack((uint32_t)ctx->r4); -} - -extern "C" void osViSetSpecialFeatures_recomp(uint8_t* rdram, recomp_context* ctx) { - osViSetSpecialFeatures((uint32_t)ctx->r4); -} - -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) { - osViSetMode(rdram, (int32_t)ctx->r4); -} - -extern uint64_t total_vis; - -extern "C" void wait_one_frame(uint8_t* rdram, recomp_context* ctx) { - uint64_t cur_vis = total_vis; - while (cur_vis == total_vis) { - std::this_thread::yield(); - } -} diff --git a/src/ui/ui_color_hack.cpp b/src/ui/ui_color_hack.cpp index 5444aaa..a18b2fd 100644 --- a/src/ui/ui_color_hack.cpp +++ b/src/ui/ui_color_hack.cpp @@ -6,7 +6,7 @@ using ColourMap = Rml::UnorderedMap; -namespace recomp { +namespace recompui { class PropertyParserColorHack : public Rml::PropertyParser { public: PropertyParserColorHack(); diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp index ec25ab2..e6b9c1c 100644 --- a/src/ui/ui_config.cpp +++ b/src/ui/ui_config.cpp @@ -1,13 +1,13 @@ #include "recomp_ui.h" #include "recomp_input.h" -#include "recomp_sound.h" -#include "recomp_config.h" -#include "recomp_debug.h" +#include "zelda_sound.h" +#include "zelda_config.h" +#include "zelda_debug.h" #include "promptfont.h" -#include "../../ultramodern/config.hpp" -#include "../../ultramodern/ultramodern.hpp" +#include "ultramodern/config.hpp" +#include "ultramodern/ultramodern.hpp" #include "RmlUi/Core.h" -#include "rt64_layer.h" +#include "ultramodern/rt64_layer.hpp" ultramodern::GraphicsConfig new_options; Rml::DataModelHandle nav_help_model_handle; @@ -16,9 +16,9 @@ Rml::DataModelHandle controls_model_handle; Rml::DataModelHandle graphics_model_handle; Rml::DataModelHandle sound_options_model_handle; -recomp::PromptContext prompt_context; +recompui::PromptContext prompt_context; -namespace recomp { +namespace recompui { const std::unordered_map button_variants { {ButtonVariant::Primary, "primary"}, {ButtonVariant::Secondary, "secondary"}, @@ -164,7 +164,7 @@ static bool cont_active = true; static recomp::InputDevice cur_device = recomp::InputDevice::Controller; void recomp::finish_scanning_input(recomp::InputField scanned_field) { - recomp::set_input_binding(static_cast(scanned_input_index), scanned_binding_index, cur_device, scanned_field); + recomp::set_input_binding(static_cast(scanned_input_index), scanned_binding_index, cur_device, scanned_field); scanned_input_index = -1; scanned_binding_index = -1; controls_model_handle.DirtyVariable("inputs"); @@ -173,7 +173,7 @@ void recomp::finish_scanning_input(recomp::InputField scanned_field) { } void recomp::cancel_scanning_input() { - recomp::stop_scanning_input(); + recomp::stop_scanning_input(); scanned_input_index = -1; scanned_binding_index = -1; controls_model_handle.DirtyVariable("inputs"); @@ -198,13 +198,13 @@ void recomp::config_menu_set_cont_or_kb(bool cont_interacted) { } void close_config_menu_impl() { - recomp::save_config(); + zelda64::save_config(); if (ultramodern::is_game_started()) { - recomp::set_current_menu(recomp::Menu::None); + recompui::set_current_menu(recompui::Menu::None); } else { - recomp::set_current_menu(recomp::Menu::Launcher); + recompui::set_current_menu(recompui::Menu::Launcher); } } @@ -241,8 +241,8 @@ void close_config_menu() { graphics_model_handle.DirtyAllVariables(); close_config_menu_impl(); }, - recomp::ButtonVariant::Success, - recomp::ButtonVariant::Error, + recompui::ButtonVariant::Success, + recompui::ButtonVariant::Error, true, "config__close-menu-button" ); @@ -252,7 +252,7 @@ void close_config_menu() { close_config_menu_impl(); } -void recomp::open_quit_game_prompt() { +void zelda64::open_quit_game_prompt() { prompt_context.open_prompt( "Are you sure you want to quit?", "Any progress since your last save will be lost.", @@ -262,8 +262,8 @@ void recomp::open_quit_game_prompt() { ultramodern::quit(); }, []() {}, - recomp::ButtonVariant::Error, - recomp::ButtonVariant::Tertiary, + recompui::ButtonVariant::Error, + recompui::ButtonVariant::Tertiary, true, "config__quit-game-button" ); @@ -275,12 +275,12 @@ struct ControlOptionsContext { int gyro_sensitivity; // 0 to 100 int mouse_sensitivity; // 0 to 100 int joystick_deadzone; // 0 to 100 - recomp::TargetingMode targeting_mode; + zelda64::TargetingMode targeting_mode; recomp::BackgroundInputMode background_input_mode; - recomp::AutosaveMode autosave_mode; - recomp::CameraInvertMode camera_invert_mode; - recomp::AnalogCamMode analog_cam_mode; - recomp::CameraInvertMode analog_camera_invert_mode; + zelda64::AutosaveMode autosave_mode; + zelda64::CameraInvertMode camera_invert_mode; + zelda64::AnalogCamMode analog_cam_mode; + zelda64::CameraInvertMode analog_camera_invert_mode; }; ControlOptionsContext control_options_context; @@ -329,11 +329,11 @@ void recomp::set_joystick_deadzone(int deadzone) { } } -recomp::TargetingMode recomp::get_targeting_mode() { +zelda64::TargetingMode zelda64::get_targeting_mode() { return control_options_context.targeting_mode; } -void recomp::set_targeting_mode(recomp::TargetingMode mode) { +void zelda64::set_targeting_mode(zelda64::TargetingMode mode) { control_options_context.targeting_mode = mode; if (general_model_handle) { general_model_handle.DirtyVariable("targeting_mode"); @@ -357,44 +357,44 @@ void recomp::set_background_input_mode(recomp::BackgroundInputMode mode) { ); } -recomp::AutosaveMode recomp::get_autosave_mode() { +zelda64::AutosaveMode zelda64::get_autosave_mode() { return control_options_context.autosave_mode; } -void recomp::set_autosave_mode(recomp::AutosaveMode mode) { +void zelda64::set_autosave_mode(zelda64::AutosaveMode mode) { control_options_context.autosave_mode = mode; if (general_model_handle) { general_model_handle.DirtyVariable("autosave_mode"); } } -recomp::CameraInvertMode recomp::get_camera_invert_mode() { +zelda64::CameraInvertMode zelda64::get_camera_invert_mode() { return control_options_context.camera_invert_mode; } -void recomp::set_camera_invert_mode(recomp::CameraInvertMode mode) { +void zelda64::set_camera_invert_mode(zelda64::CameraInvertMode mode) { control_options_context.camera_invert_mode = mode; if (general_model_handle) { general_model_handle.DirtyVariable("camera_invert_mode"); } } -recomp::AnalogCamMode recomp::get_analog_cam_mode() { +zelda64::AnalogCamMode zelda64::get_analog_cam_mode() { return control_options_context.analog_cam_mode; } -void recomp::set_analog_cam_mode(recomp::AnalogCamMode mode) { +void zelda64::set_analog_cam_mode(zelda64::AnalogCamMode mode) { control_options_context.analog_cam_mode = mode; if (general_model_handle) { general_model_handle.DirtyVariable("analog_cam_mode"); } } -recomp::CameraInvertMode recomp::get_analog_camera_invert_mode() { +zelda64::CameraInvertMode zelda64::get_analog_camera_invert_mode() { return control_options_context.analog_camera_invert_mode; } -void recomp::set_analog_camera_invert_mode(recomp::CameraInvertMode mode) { +void zelda64::set_analog_camera_invert_mode(zelda64::CameraInvertMode mode) { control_options_context.analog_camera_invert_mode = mode; if (general_model_handle) { general_model_handle.DirtyVariable("analog_camera_invert_mode"); @@ -417,43 +417,43 @@ struct SoundOptionsContext { SoundOptionsContext sound_options_context; -void recomp::reset_sound_settings() { +void zelda64::reset_sound_settings() { sound_options_context.reset(); if (sound_options_model_handle) { sound_options_model_handle.DirtyAllVariables(); } } -void recomp::set_main_volume(int volume) { +void zelda64::set_main_volume(int volume) { sound_options_context.main_volume.store(volume); if (sound_options_model_handle) { sound_options_model_handle.DirtyVariable("main_volume"); } } -int recomp::get_main_volume() { +int zelda64::get_main_volume() { return sound_options_context.main_volume.load(); } -void recomp::set_bgm_volume(int volume) { +void zelda64::set_bgm_volume(int volume) { sound_options_context.bgm_volume.store(volume); if (sound_options_model_handle) { sound_options_model_handle.DirtyVariable("bgm_volume"); } } -int recomp::get_bgm_volume() { +int zelda64::get_bgm_volume() { return sound_options_context.bgm_volume.load(); } -void recomp::set_low_health_beeps_enabled(bool enabled) { +void zelda64::set_low_health_beeps_enabled(bool enabled) { sound_options_context.low_health_beeps_enabled.store((int)enabled); if (sound_options_model_handle) { sound_options_model_handle.DirtyVariable("low_health_beeps_enabled"); } } -bool recomp::get_low_health_beeps_enabled() { +bool zelda64::get_low_health_beeps_enabled() { return (bool)sound_options_context.low_health_beeps_enabled.load(); } @@ -471,7 +471,7 @@ struct DebugContext { bool debug_enabled = false; DebugContext() { - for (const auto& area : recomp::game_warps) { + for (const auto& area : zelda64::game_warps) { area_names.emplace_back(area.name); } update_warp_names(); @@ -479,15 +479,15 @@ struct DebugContext { void update_warp_names() { scene_names.clear(); - for (const auto& scene : recomp::game_warps[area_index].scenes) { + for (const auto& scene : zelda64::game_warps[area_index].scenes) { scene_names.emplace_back(scene.name); } - entrance_names = recomp::game_warps[area_index].scenes[scene_index].entrances; + entrance_names = zelda64::game_warps[area_index].scenes[scene_index].entrances; } }; -void recomp::update_rml_display_refresh_rate() { +void recompui::update_rml_display_refresh_rate() { static uint32_t lastRate = 0; if (!graphics_model_handle) return; @@ -500,7 +500,7 @@ void recomp::update_rml_display_refresh_rate() { DebugContext debug_context; -class ConfigMenu : public recomp::MenuController { +class ConfigMenu : public recompui::MenuController { public: ConfigMenu() { @@ -511,13 +511,13 @@ public: Rml::ElementDocument* load_document(Rml::Context* context) override { return context->LoadDocument("assets/config_menu.rml"); } - void register_events(recomp::UiEventListenerInstancer& listener) override { - recomp::register_event(listener, "apply_options", + void register_events(recompui::UiEventListenerInstancer& listener) override { + recompui::register_event(listener, "apply_options", [](const std::string& param, Rml::Event& event) { graphics_model_handle.DirtyVariable("options_changed"); apply_graphics_config(); }); - recomp::register_event(listener, "config_keydown", + recompui::register_event(listener, "config_keydown", [](const std::string& param, Rml::Event& event) { if (!prompt_context.open && event.GetId() == Rml::EventId::Keydown) { auto key = event.GetParameter("key_identifier", Rml::Input::KeyIdentifier::KI_UNKNOWN); @@ -533,23 +533,23 @@ public: } }); // This needs to be separate from `close_config_menu` so it ensures that the event is only on the target - recomp::register_event(listener, "close_config_menu_backdrop", + recompui::register_event(listener, "close_config_menu_backdrop", [](const std::string& param, Rml::Event& event) { if (event.GetPhase() == Rml::EventPhase::Target) { close_config_menu(); } }); - recomp::register_event(listener, "close_config_menu", + recompui::register_event(listener, "close_config_menu", [](const std::string& param, Rml::Event& event) { close_config_menu(); }); - recomp::register_event(listener, "open_quit_game_prompt", + recompui::register_event(listener, "open_quit_game_prompt", [](const std::string& param, Rml::Event& event) { - recomp::open_quit_game_prompt(); + zelda64::open_quit_game_prompt(); }); - recomp::register_event(listener, "toggle_input_device", + recompui::register_event(listener, "toggle_input_device", [](const std::string& param, Rml::Event& event) { cur_device = cur_device == recomp::InputDevice::Controller ? recomp::InputDevice::Keyboard @@ -558,7 +558,7 @@ public: controls_model_handle.DirtyVariable("inputs"); }); - recomp::register_event(listener, "area_index_changed", + recompui::register_event(listener, "area_index_changed", [](const std::string& param, Rml::Event& event) { debug_context.area_index = event.GetParameter("value", 0); debug_context.scene_index = 0; @@ -570,7 +570,7 @@ public: debug_context.model_handle.DirtyVariable("entrance_names"); }); - recomp::register_event(listener, "scene_index_changed", + recompui::register_event(listener, "scene_index_changed", [](const std::string& param, Rml::Event& event) { debug_context.scene_index = event.GetParameter("value", 0); debug_context.entrance_index = 0; @@ -579,14 +579,14 @@ public: debug_context.model_handle.DirtyVariable("entrance_names"); }); - recomp::register_event(listener, "do_warp", + recompui::register_event(listener, "do_warp", [](const std::string& param, Rml::Event& event) { - recomp::do_warp(debug_context.area_index, debug_context.scene_index, debug_context.entrance_index); + zelda64::do_warp(debug_context.area_index, debug_context.scene_index, debug_context.entrance_index); }); - recomp::register_event(listener, "set_time", + recompui::register_event(listener, "set_time", [](const std::string& param, Rml::Event& event) { - recomp::set_time(debug_context.set_time_day, debug_context.set_time_hour, debug_context.set_time_minute); + zelda64::set_time(debug_context.set_time_day, debug_context.set_time_hour, debug_context.set_time_minute); }); } @@ -722,7 +722,7 @@ public: [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { scanned_input_index = inputs.at(0).Get(); scanned_binding_index = inputs.at(1).Get(); - recomp::start_scanning_input(cur_device); + recomp::start_scanning_input(cur_device); model_handle.DirtyVariable("active_binding_input"); model_handle.DirtyVariable("active_binding_slot"); }); @@ -730,18 +730,18 @@ public: constructor.BindEventCallback("reset_input_bindings_to_defaults", [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { if (cur_device == recomp::InputDevice::Controller) { - recomp::reset_cont_input_bindings(); + zelda64::reset_cont_input_bindings(); } else { - recomp::reset_kb_input_bindings(); + zelda64::reset_kb_input_bindings(); } model_handle.DirtyAllVariables(); }); constructor.BindEventCallback("clear_input_bindings", [](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) { - recomp::GameInput input = static_cast(inputs.at(0).Get()); + recomp::GameInput input = static_cast(inputs.at(0).Get()); for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) { - recomp::set_input_binding(input, binding_index, cur_device, recomp::InputField{}); + recomp::set_input_binding(input, binding_index, cur_device, recomp::InputField{}); } model_handle.DirtyVariable("inputs"); }); @@ -776,7 +776,7 @@ public: virtual int Size(void* ptr) override { return recomp::bindings_per_input; } virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override { - recomp::GameInput input = static_cast((uintptr_t)ptr); + recomp::GameInput input = static_cast((uintptr_t)ptr); return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(input, address.index, cur_device)}; } }; @@ -812,7 +812,7 @@ public: return Rml::DataVariable(&binding_array_var_instance, nullptr); } else { - recomp::GameInput input = recomp::get_input_from_enum_name(address.name); + recomp::GameInput input = recomp::get_input_from_enum_name(address.name); if (input != recomp::GameInput::COUNT) { return Rml::DataVariable(&binding_container_var_instance, (void*)(uintptr_t)input); } @@ -966,14 +966,14 @@ public: constructor.Bind("prompt__confirmLabel", &prompt_context.confirmLabel); constructor.Bind("prompt__cancelLabel", &prompt_context.cancelLabel); - constructor.BindEventCallback("prompt__on_click", &recomp::PromptContext::on_click, &prompt_context); + constructor.BindEventCallback("prompt__on_click", &recompui::PromptContext::on_click, &prompt_context); prompt_context.model_handle = constructor.GetModelHandle(); } void make_bindings(Rml::Context* context) override { // initially set cont state for ui help - recomp::config_menu_set_cont_or_kb(recomp::get_cont_active()); + recomp::config_menu_set_cont_or_kb(recompui::get_cont_active()); make_nav_help_bindings(context); make_general_bindings(context); make_controls_bindings(context); @@ -984,22 +984,22 @@ public: } }; -std::unique_ptr recomp::create_config_menu() { +std::unique_ptr recompui::create_config_menu() { return std::make_unique(); } -bool recomp::get_debug_mode_enabled() { +bool zelda64::get_debug_mode_enabled() { return debug_context.debug_enabled; } -void recomp::set_debug_mode_enabled(bool enabled) { +void zelda64::set_debug_mode_enabled(bool enabled) { debug_context.debug_enabled = enabled; if (debug_context.model_handle) { debug_context.model_handle.DirtyVariable("debug_enabled"); } } -void recomp::update_supported_options() { +void recompui::update_supported_options() { msaa2x_supported = ultramodern::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA2X; msaa4x_supported = ultramodern::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA4X; msaa8x_supported = ultramodern::RT64MaxMSAA() >= RT64::UserConfiguration::Antialiasing::MSAA8X; @@ -1010,7 +1010,7 @@ void recomp::update_supported_options() { graphics_model_handle.DirtyAllVariables(); } -void recomp::toggle_fullscreen() { +void recompui::toggle_fullscreen() { new_options.wm_option = (new_options.wm_option == ultramodern::WindowMode::Windowed) ? ultramodern::WindowMode::Fullscreen : ultramodern::WindowMode::Windowed; apply_graphics_config(); graphics_model_handle.DirtyVariable("wm_option"); diff --git a/src/ui/ui_launcher.cpp b/src/ui/ui_launcher.cpp index 19a0804..b4b6312 100644 --- a/src/ui/ui_launcher.cpp +++ b/src/ui/ui_launcher.cpp @@ -1,7 +1,7 @@ #include "recomp_ui.h" -#include "recomp_config.h" -#include "recomp_game.h" -#include "../../ultramodern/ultramodern.hpp" +#include "zelda_config.h" +#include "librecomp/game.hpp" +#include "ultramodern/ultramodern.hpp" #include "RmlUi/Core.h" #include "nfd.h" #include @@ -11,6 +11,8 @@ std::string version_number = "v1.1.1"; Rml::DataModelHandle model_handle; bool mm_rom_valid = false; +extern std::vector supported_games; + void select_rom() { nfdnchar_t* native_path = nullptr; nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr); @@ -21,39 +23,39 @@ void select_rom() { NFD_FreePathN(native_path); native_path = nullptr; - recomp::RomValidationError rom_error = recomp::select_rom(path, recomp::Game::MM); - - switch (rom_error) { - case recomp::RomValidationError::Good: - mm_rom_valid = true; - model_handle.DirtyVariable("mm_rom_valid"); - break; - case recomp::RomValidationError::FailedToOpen: - recomp::message_box("Failed to open ROM file."); - break; - case recomp::RomValidationError::NotARom: - recomp::message_box("This is not a valid ROM file."); - break; - case recomp::RomValidationError::IncorrectRom: - recomp::message_box("This ROM is not the correct game."); - break; - case recomp::RomValidationError::NotYet: - recomp::message_box("This game isn't supported yet."); - break; - case recomp::RomValidationError::IncorrectVersion: - recomp::message_box("This ROM is the correct game, but the wrong version.\nThis project requires the NTSC-U N64 version of the game."); - break; - case recomp::RomValidationError::OtherError: - recomp::message_box("An unknown error has occurred."); - break; - } - } + recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id); + switch (rom_error) { + case recomp::RomValidationError::Good: + mm_rom_valid = true; + model_handle.DirtyVariable("mm_rom_valid"); + break; + case recomp::RomValidationError::FailedToOpen: + recompui::message_box("Failed to open ROM file."); + break; + case recomp::RomValidationError::NotARom: + recompui::message_box("This is not a valid ROM file."); + break; + case recomp::RomValidationError::IncorrectRom: + recompui::message_box("This ROM is not the correct game."); + break; + case recomp::RomValidationError::NotYet: + recompui::message_box("This game isn't supported yet."); + break; + case recomp::RomValidationError::IncorrectVersion: + recompui::message_box( + "This ROM is the correct game, but the wrong version.\nThis project requires the NTSC-U N64 version of the game."); + break; + case recomp::RomValidationError::OtherError: + recompui::message_box("An unknown error has occurred."); + break; + } + } } -class LauncherMenu : public recomp::MenuController { +class LauncherMenu : public recompui::MenuController { public: LauncherMenu() { - mm_rom_valid = recomp::is_rom_valid(recomp::Game::MM); + mm_rom_valid = recomp::is_rom_valid(supported_games[0].game_id); } ~LauncherMenu() override { @@ -61,37 +63,37 @@ public: Rml::ElementDocument* load_document(Rml::Context* context) override { return context->LoadDocument("assets/launcher.rml"); } - void register_events(recomp::UiEventListenerInstancer& listener) override { - recomp::register_event(listener, "select_rom", + void register_events(recompui::UiEventListenerInstancer& listener) override { + recompui::register_event(listener, "select_rom", [](const std::string& param, Rml::Event& event) { select_rom(); } ); - recomp::register_event(listener, "rom_selected", + recompui::register_event(listener, "rom_selected", [](const std::string& param, Rml::Event& event) { mm_rom_valid = true; model_handle.DirtyVariable("mm_rom_valid"); } ); - recomp::register_event(listener, "start_game", + recompui::register_event(listener, "start_game", [](const std::string& param, Rml::Event& event) { - recomp::start_game(recomp::Game::MM); - recomp::set_current_menu(recomp::Menu::None); + recomp::start_game(supported_games[0].game_id); + recompui::set_current_menu(recompui::Menu::None); } ); - recomp::register_event(listener, "open_controls", + recompui::register_event(listener, "open_controls", [](const std::string& param, Rml::Event& event) { - recomp::set_current_menu(recomp::Menu::Config); - recomp::set_config_submenu(recomp::ConfigSubmenu::Controls); + recompui::set_current_menu(recompui::Menu::Config); + recompui::set_config_submenu(recompui::ConfigSubmenu::Controls); } ); - recomp::register_event(listener, "open_settings", + recompui::register_event(listener, "open_settings", [](const std::string& param, Rml::Event& event) { - recomp::set_current_menu(recomp::Menu::Config); - recomp::set_config_submenu(recomp::ConfigSubmenu::General); + recompui::set_current_menu(recompui::Menu::Config); + recompui::set_config_submenu(recompui::ConfigSubmenu::General); } ); - recomp::register_event(listener, "exit_game", + recompui::register_event(listener, "exit_game", [](const std::string& param, Rml::Event& event) { ultramodern::quit(); } @@ -107,6 +109,6 @@ public: } }; -std::unique_ptr recomp::create_launcher_menu() { +std::unique_ptr recompui::create_launcher_menu() { return std::make_unique(); } diff --git a/src/ui/ui_renderer.cpp b/src/ui/ui_renderer.cpp index c5ad2d3..08a3d21 100644 --- a/src/ui/ui_renderer.cpp +++ b/src/ui/ui_renderer.cpp @@ -4,16 +4,22 @@ #include #include +#ifdef _WIN32 +#include +#else +#include +#endif + #include "recomp_ui.h" #include "recomp_input.h" -#include "recomp_game.h" -#include "recomp_config.h" +#include "librecomp/game.hpp" +#include "zelda_config.h" #include "ui_rml_hacks.hpp" #include "concurrentqueue.h" -#include "rt64_layer.h" +#include "ultramodern/rt64_layer.hpp" #include "rt64_render_hooks.h" #include "rt64_render_interface_builders.h" @@ -718,7 +724,7 @@ Rml::Element* get_target(Rml::ElementDocument* document, Rml::Element* element) return element; } -namespace recomp { +namespace recompui { class UiEventListener : public Rml::EventListener { event_handler_t* handler_; Rml::String param_; @@ -759,7 +765,7 @@ namespace recomp { }; } -void recomp::register_event(UiEventListenerInstancer& listener, const std::string& name, event_handler_t* handler) { +void recompui::register_event(UiEventListenerInstancer& listener, const std::string& name, event_handler_t* handler) { listener.register_event(name, handler); } @@ -779,8 +785,8 @@ Rml::Element* find_autofocus_element(Rml::Element* start) { struct UIContext { struct UIRenderContext render; class { - std::unordered_map> menus; - std::unordered_map documents; + std::unordered_map> menus; + std::unordered_map documents; Rml::ElementDocument* current_document; Rml::Element* prev_focused; bool mouse_is_active_changed = false; @@ -794,13 +800,13 @@ struct UIContext { std::unique_ptr system_interface; std::unique_ptr render_interface; Rml::Context* context; - recomp::UiEventListenerInstancer event_listener_instancer; + recompui::UiEventListenerInstancer event_listener_instancer; void unload() { render_interface.reset(); } - void swap_document(recomp::Menu menu) { + void swap_document(recompui::Menu menu) { if (current_document != nullptr) { Rml::Element* window_el = current_document->GetElementById("window"); if (window_el != nullptr) { @@ -831,7 +837,7 @@ struct UIContext { mouse_is_active_initialized = false; } - void swap_config_menu(recomp::ConfigSubmenu submenu) { + void swap_config_menu(recompui::ConfigSubmenu submenu) { if (current_document != nullptr) { Rml::Element* config_tabset_base = current_document->GetElementById("config_tabset"); if (config_tabset_base != nullptr) { @@ -911,7 +917,7 @@ struct UIContext { } if (mouse_is_active_initialized) { - recomp::set_cursor_visible(mouse_is_active); + recompui::set_cursor_visible(mouse_is_active); } if (current_document == nullptr) { @@ -939,7 +945,6 @@ struct UIContext { if (cont_is_active || non_mouse_interacted) { if (non_mouse_interacted) { auto focusedEl = current_document->GetFocusLeafNode(); - Rml::Variant* ti = focusedEl == nullptr ? nullptr : focusedEl->GetAttribute("tab-index"); if (focusedEl == nullptr || RecompRml::CanFocusElement(focusedEl) != RecompRml::CanFocus::Yes) { Rml::Element* element = find_autofocus_element(current_document); if (element != nullptr) { @@ -981,14 +986,14 @@ struct UIContext { } } - void add_menu(recomp::Menu menu, std::unique_ptr&& controller) { + void add_menu(recompui::Menu menu, std::unique_ptr&& controller) { menus.emplace(menu, std::move(controller)); } void update_config_menu_loop(bool menu_changed) { static int prevTab = -1; if (menu_changed) prevTab = -1; - recomp::update_rml_display_refresh_rate(); + recompui::update_rml_display_refresh_rate(); Rml::ElementTabSet *tabset = (Rml::ElementTabSet *)current_document->GetElementById("config_tabset"); if (tabset == nullptr) return; @@ -1022,7 +1027,7 @@ struct UIContext { void update_prompt_loop(void) { static bool wasShowingPrompt = false; - recomp::PromptContext *ctx = recomp::get_prompt_context(); + recompui::PromptContext *ctx = recompui::get_prompt_context(); if (!ctx->open && wasShowingPrompt) { Rml::Element* focused = current_document->GetFocusLeafNode(); if (focused) focused->Blur(); @@ -1088,8 +1093,8 @@ struct UIContext { Rml::Element *confirmButton = current_document->GetElementById("prompt__confirm-button"); Rml::Element *cancelButton = current_document->GetElementById("prompt__cancel-button"); - if (confirmButton != nullptr) confirmButton->SetClassNames("button button--" + recomp::button_variants.at(ctx->confirmVariant)); - if (cancelButton != nullptr) cancelButton->SetClassNames( "button button--" + recomp::button_variants.at(ctx->cancelVariant)); + if (confirmButton != nullptr) confirmButton->SetClassNames("button button--" + recompui::button_variants.at(ctx->confirmVariant)); + if (cancelButton != nullptr) cancelButton->SetClassNames( "button button--" + recompui::button_variants.at(ctx->cancelVariant)); } } rml; }; @@ -1100,7 +1105,7 @@ std::mutex ui_context_mutex{}; // TODO make this not be global extern SDL_Window* window; -void recomp::get_window_size(int& width, int& height) { +void recompui::get_window_size(int& width, int& height) { SDL_GetWindowSizeInPixels(window, &width, &height); } @@ -1110,8 +1115,8 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { #endif ui_context = std::make_unique(); - ui_context->rml.add_menu(recomp::Menu::Config, recomp::create_config_menu()); - ui_context->rml.add_menu(recomp::Menu::Launcher, recomp::create_launcher_menu()); + ui_context->rml.add_menu(recompui::Menu::Config, recompui::create_config_menu()); + ui_context->rml.add_menu(recompui::Menu::Launcher, recompui::create_launcher_menu()); ui_context->render.interface = interface; ui_context->render.device = device; @@ -1129,7 +1134,7 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { Rml::Initialise(); // Apply the hack to replace RmlUi's default color parser with one that conforms to HTML5 alpha parsing for SASS compatibility - recomp::apply_color_hack(); + recompui::apply_color_hack(); int width, height; SDL_GetWindowSizeInPixels(window, &width, &height); @@ -1167,16 +1172,16 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) { moodycamel::ConcurrentQueue ui_event_queue{}; -void recomp::queue_event(const SDL_Event& event) { +void recompui::queue_event(const SDL_Event& event) { ui_event_queue.enqueue(event); } -bool recomp::try_deque_event(SDL_Event& out) { +bool recompui::try_deque_event(SDL_Event& out) { return ui_event_queue.try_dequeue(out); } -std::atomic open_menu = recomp::Menu::Launcher; -std::atomic open_config_submenu = recomp::ConfigSubmenu::Count; +std::atomic open_menu = recompui::Menu::Launcher; +std::atomic open_config_submenu = recompui::ConfigSubmenu::Count; int cont_button_to_key(SDL_ControllerButtonEvent& button) { switch (button.button) { @@ -1236,15 +1241,15 @@ void apply_background_input_mode() { last_input_mode = cur_input_mode; } -bool recomp::get_cont_active() { +bool recompui::get_cont_active() { return ui_context->rml.cont_is_active; } -void recomp::set_cont_active(bool active) { +void recompui::set_cont_active(bool active) { ui_context->rml.cont_is_active = active; } -void recomp::activate_mouse() { +void recompui::activate_mouse() { ui_context->rml.update_primary_input(true, false); ui_context->rml.update_focus(true, false); } @@ -1267,12 +1272,12 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s bool reload_sheets = is_reload_held && !was_reload_held; was_reload_held = is_reload_held; - static recomp::Menu prev_menu = recomp::Menu::None; - recomp::Menu cur_menu = open_menu.load(); + static recompui::Menu prev_menu = recompui::Menu::None; + recompui::Menu cur_menu = open_menu.load(); if (reload_sheets) { ui_context->rml.load_documents(); - prev_menu = recomp::Menu::None; + prev_menu = recompui::Menu::None; } bool menu_changed = cur_menu != prev_menu; @@ -1280,10 +1285,10 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s ui_context->rml.swap_document(cur_menu); } - recomp::ConfigSubmenu config_submenu = open_config_submenu.load(); - if (config_submenu != recomp::ConfigSubmenu::Count) { + recompui::ConfigSubmenu config_submenu = open_config_submenu.load(); + if (config_submenu != recompui::ConfigSubmenu::Count) { ui_context->rml.swap_config_menu(config_submenu); - open_config_submenu.store(recomp::ConfigSubmenu::Count); + open_config_submenu.store(recompui::ConfigSubmenu::Count); } prev_menu = cur_menu; @@ -1296,15 +1301,15 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s bool cont_interacted = false; bool kb_interacted = false; - if (cur_menu == recomp::Menu::Config) { + if (cur_menu == recompui::Menu::Config) { ui_context->rml.update_config_menu_loop(menu_changed); } - if (cur_menu != recomp::Menu::None) { + if (cur_menu != recompui::Menu::None) { ui_context->rml.update_prompt_loop(); } - while (recomp::try_deque_event(cur_event)) { - bool menu_is_open = cur_menu != recomp::Menu::None; + while (recompui::try_deque_event(cur_event)) { + bool menu_is_open = cur_menu != recompui::Menu::None; if (!recomp::all_input_disabled()) { // Implement some additional behavior for specific events on top of what RmlUi normally does with them. @@ -1323,7 +1328,7 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s last_mouse_pos[1] = cur_event.motion.y; // if controller is the primary input, don't use mouse movement to allow cursor to reactivate - if (recomp::get_cont_active()) { + if (recompui::get_cont_active()) { break; } } @@ -1401,15 +1406,15 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s } if (open_config) { - cur_menu = recomp::Menu::Config; - open_menu.store(recomp::Menu::Config); + cur_menu = recompui::Menu::Config; + open_menu.store(recompui::Menu::Config); ui_context->rml.swap_document(cur_menu); } } } // end dequeue event loop if (cont_interacted || kb_interacted || mouse_clicked) { - recomp::set_cont_active(cont_interacted); + recompui::set_cont_active(cont_interacted); } recomp::config_menu_set_cont_or_kb(ui_context->rml.cont_is_active); @@ -1421,7 +1426,7 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s ui_context->rml.update_primary_input(mouse_moved, non_mouse_interacted); ui_context->rml.update_focus(mouse_moved, non_mouse_interacted); - if (cur_menu != recomp::Menu::None) { + if (cur_menu != recompui::Menu::None) { int width = swap_chain_framebuffer->getWidth(); int height = swap_chain_framebuffer->getHeight(); @@ -1457,25 +1462,25 @@ void set_rt64_hooks() { RT64::SetRenderHooks(init_hook, draw_hook, deinit_hook); } -void recomp::set_current_menu(Menu menu) { +void recompui::set_current_menu(Menu menu) { open_menu.store(menu); - if (menu == recomp::Menu::None) { + if (menu == recompui::Menu::None) { ui_context->rml.system_interface->SetMouseCursor("arrow"); } } -void recomp::set_config_submenu(recomp::ConfigSubmenu submenu) { +void recompui::set_config_submenu(recompui::ConfigSubmenu submenu) { open_config_submenu.store(submenu); } -void recomp::destroy_ui() { +void recompui::destroy_ui() { } -recomp::Menu recomp::get_current_menu() { +recompui::Menu recompui::get_current_menu() { return open_menu.load(); } -void recomp::message_box(const char* msg) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, recomp::program_name.data(), msg, nullptr); +void recompui::message_box(const char* msg) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, zelda64::program_name.data(), msg, nullptr); printf("[ERROR] %s\n", msg); } diff --git a/ultramodern/audio.cpp b/ultramodern/audio.cpp deleted file mode 100644 index f4f7257..0000000 --- a/ultramodern/audio.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "ultra64.h" -#include "ultramodern.hpp" -#include - -static uint32_t sample_rate = 48000; - -static ultramodern::audio_callbacks_t audio_callbacks; - -void set_audio_callbacks(const ultramodern::audio_callbacks_t& callbacks) { - audio_callbacks = callbacks; -} - -void ultramodern::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 ultramodern::set_audio_frequency(uint32_t freq) { - if (audio_callbacks.set_frequency) { - audio_callbacks.set_frequency(freq); - } - sample_rate = freq; -} - -void ultramodern::queue_audio_buffer(RDRAM_ARG PTR(int16_t) audio_data_, uint32_t byte_count) { - // 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(int16_t); - - // Queue the swapped audio data. - if (audio_callbacks.queue_samples) { - audio_callbacks.queue_samples(TO_PTR(int16_t, audio_data_), sample_count); - } -} - -// For SDL2 -//uint32_t buffer_offset_frames = 1; -// For Godot -float buffer_offset_frames = 0.5f; - -// 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 ultramodern::get_remaining_audio_bytes() { - // Get the number of remaining buffered audio bytes. - uint32_t buffered_byte_count; - if (audio_callbacks.get_frames_remaining != nullptr) { - buffered_byte_count = audio_callbacks.get_frames_remaining() * 2 * sizeof(int16_t); - } - else { - buffered_byte_count = 100; - } - // Adjust the reported count to be some number of refreshes in the future, which helps ensure that - // there are enough samples even if the audio thread 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 > static_cast(buffer_offset_frames * sizeof(int16_t) * samples_per_vi)) { - buffered_byte_count -= static_cast(buffer_offset_frames * sizeof(int16_t) * samples_per_vi); - } - else { - buffered_byte_count = 0; - } - return buffered_byte_count; -} diff --git a/ultramodern/config.hpp b/ultramodern/config.hpp deleted file mode 100644 index 2cd9ea3..0000000 --- a/ultramodern/config.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef __CONFIG_HPP__ -#define __CONFIG_HPP__ - -#include "common/rt64_user_configuration.h" - -namespace ultramodern { - enum class Resolution { - Original, - Original2x, - Auto, - OptionCount - }; - enum class WindowMode { - Windowed, - Fullscreen, - OptionCount - }; - enum class HUDRatioMode { - Original, - Clamp16x9, - Full, - OptionCount - }; - enum class GraphicsApi { - Auto, - D3D12, - Vulkan, - OptionCount - }; - enum class HighPrecisionFramebuffer { - Auto, - On, - Off, - OptionCount - }; - - struct GraphicsConfig { - Resolution res_option; - WindowMode wm_option; - HUDRatioMode hr_option; - GraphicsApi api_option; - RT64::UserConfiguration::AspectRatio ar_option; - RT64::UserConfiguration::Antialiasing msaa_option; - RT64::UserConfiguration::RefreshRate rr_option; - HighPrecisionFramebuffer hpfb_option; - int rr_manual_value; - int ds_option; - bool developer_mode; - - auto operator<=>(const GraphicsConfig& rhs) const = default; - }; - - void set_graphics_config(const GraphicsConfig& config); - GraphicsConfig get_graphics_config(); - - NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::Resolution, { - {ultramodern::Resolution::Original, "Original"}, - {ultramodern::Resolution::Original2x, "Original2x"}, - {ultramodern::Resolution::Auto, "Auto"}, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::WindowMode, { - {ultramodern::WindowMode::Windowed, "Windowed"}, - {ultramodern::WindowMode::Fullscreen, "Fullscreen"} - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::HUDRatioMode, { - {ultramodern::HUDRatioMode::Original, "Original"}, - {ultramodern::HUDRatioMode::Clamp16x9, "Clamp16x9"}, - {ultramodern::HUDRatioMode::Full, "Full"}, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::GraphicsApi, { - {ultramodern::GraphicsApi::Auto, "Auto"}, - {ultramodern::GraphicsApi::D3D12, "D3D12"}, - {ultramodern::GraphicsApi::Vulkan, "Vulkan"}, - }); - - NLOHMANN_JSON_SERIALIZE_ENUM(ultramodern::HighPrecisionFramebuffer, { - {ultramodern::HighPrecisionFramebuffer::Auto, "Auto"}, - {ultramodern::HighPrecisionFramebuffer::On, "On"}, - {ultramodern::HighPrecisionFramebuffer::Off, "Off"}, - }); -}; - -#endif diff --git a/ultramodern/events.cpp b/ultramodern/events.cpp deleted file mode 100644 index cef0b8c..0000000 --- a/ultramodern/events.cpp +++ /dev/null @@ -1,620 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "blockingconcurrentqueue.h" - -#include "ultra64.h" -#include "ultramodern.hpp" -#include "config.hpp" -#include "rt64_layer.h" -#include "recomp.h" -#include "recomp_game.h" -#include "recomp_ui.h" -#include "recomp_input.h" -#include "rsp.h" - -struct SpTaskAction { - OSTask task; -}; - -struct SwapBuffersAction { - uint32_t origin; -}; - -struct UpdateConfigAction { -}; - -struct LoadShaderCacheAction { - std::span data; -}; - -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 gfx_thread; - std::thread task_thread; - PTR(OSMesgQueue) mq = NULLPTR; - OSMesg msg = (OSMesg)0; - } sp; - struct { - PTR(OSMesgQueue) mq = NULLPTR; - OSMesg msg = (OSMesg)0; - } dp; - struct { - PTR(OSMesgQueue) mq = NULLPTR; - OSMesg msg = (OSMesg)0; - } ai; - struct { - 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{}; - moodycamel::BlockingConcurrentQueue sp_task_queue{}; - moodycamel::ConcurrentQueue deleted_threads{}; -} 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; -} - -uint64_t total_vis = 0; - - -extern std::atomic_bool exited; - -void set_dummy_vi(); - -void vi_thread_func() { - ultramodern::set_native_thread_name("VI Thread"); - // This thread should be prioritized over every other thread in the application, as it's what allows - // the game to generate new audio and gfx lists. - ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Critical); - using namespace std::chrono_literals; - - int remaining_retraces = events_context.vi.retrace_count; - - while (!exited) { - // Determine the next VI time (more accurate than adding 16ms each VI interrupt) - auto next = ultramodern::get_start() + (total_vis * 1000000us) / (60 * ultramodern::get_speed_multiplier()); - //if (next > std::chrono::high_resolution_clock::now()) { - // printf("Sleeping for %" PRIu64 " us to get from %" PRIu64 " us to %" PRIu64 " us \n", - // (next - std::chrono::high_resolution_clock::now()) / 1us, - // (std::chrono::high_resolution_clock::now() - events_context.start) / 1us, - // (next - events_context.start) / 1us); - //} else { - // printf("No need to sleep\n"); - //} - // Detect if there's more than a second to wait and wait a fixed amount instead for the next VI if so, as that usually means the system clock went back in time. - if (std::chrono::floor(next - std::chrono::high_resolution_clock::now()) > 1s) { - // printf("Skipping the next VI wait\n"); - next = std::chrono::high_resolution_clock::now(); - } - ultramodern::sleep_until(next); - // Calculate how many VIs have passed - uint64_t new_total_vis = (ultramodern::time_since_start() * (60 * ultramodern::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 (ultramodern::is_game_started()) { - 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"); - } - } - } - else { - set_dummy_vi(); - static bool swap = false; - uint32_t vi_origin = 0x400 + 0x280; // Skip initial RDRAM contents and add the usual origin offset - // Offset by one FB every other frame so RT64 continues drawing - if (swap) { - vi_origin += 0x25800; - } - osViSwapBuffer(rdram, vi_origin); - swap = !swap; - } - } - 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"); - } - } - } - - // TODO move recomp code out of ultramodern. - recomp::update_rumble(); - } -} - -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); -} - -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); -} - - -void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready) { - ultramodern::set_native_thread_name("SP Task Thread"); - ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal); - - // Notify the caller thread that this thread is ready. - thread_ready->signal(); - - while (true) { - // Wait until an RSP task has been sent - OSTask* task; - events_context.sp_task_queue.wait_dequeue(task); - - if (task == nullptr) { - return; - } - - // Run the correct function based on the task type - if (task->t.type == M_AUDTASK) { - run_rsp_microcode(rdram, task, aspMain); - } - else if (task->t.type == M_NJPEGTASK) { - run_rsp_microcode(rdram, task, njpgdspMain); - } - else { - fprintf(stderr, "Unknown task type: %" PRIu32 "\n", task->t.type); - assert(false); - std::quick_exit(EXIT_FAILURE); - } - - // Tell the game that the RSP has completed - sp_complete(); - } -} - -static std::atomic cur_config{}; - -void ultramodern::set_graphics_config(const ultramodern::GraphicsConfig& config) { - cur_config = config; - events_context.action_queue.enqueue(UpdateConfigAction{}); -} - -ultramodern::GraphicsConfig ultramodern::get_graphics_config() { - return cur_config; -} - -std::atomic_uint32_t display_refresh_rate = 60; -std::atomic resolution_scale = 1.0f; - -uint32_t ultramodern::get_target_framerate(uint32_t original) { - ultramodern::GraphicsConfig graphics_config = ultramodern::get_graphics_config(); - - switch (graphics_config.rr_option) { - case RT64::UserConfiguration::RefreshRate::Original: - default: - return original; - case RT64::UserConfiguration::RefreshRate::Manual: - return graphics_config.rr_manual_value; - case RT64::UserConfiguration::RefreshRate::Display: - return display_refresh_rate.load(); - } -} - -uint32_t ultramodern::get_display_refresh_rate() { - return display_refresh_rate.load(); -} - -float ultramodern::get_resolution_scale() { - return resolution_scale.load(); -} - -void ultramodern::load_shader_cache(std::span cache_data) { - events_context.action_queue.enqueue(LoadShaderCacheAction{cache_data}); -} - -std::atomic rt64_setup_result = ultramodern::RT64SetupResult::Success; - -void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_ready, ultramodern::WindowHandle window_handle) { - bool enabled_instant_present = false; - using namespace std::chrono_literals; - - ultramodern::set_native_thread_name("Gfx Thread"); - ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::Normal); - - ultramodern::GraphicsConfig old_config = ultramodern::get_graphics_config(); - - ultramodern::RT64Context rt64{rdram, window_handle, cur_config.load().developer_mode}; - - if (!rt64.valid()) { - // TODO move recomp code out of ultramodern. - rt64_setup_result.store(rt64.get_setup_result()); - // Notify the caller thread that this thread is ready. - thread_ready->signal(); - return; - } - - // TODO move recomp code out of ultramodern. - recomp::update_supported_options(); - - rsp_constants_init(); - - // Notify the caller thread that this thread is ready. - thread_ready->signal(); - - while (!exited) { - // 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)) { - // Turn on instant present if the game has been started and it hasn't been turned on yet. - if (ultramodern::is_game_started() && !enabled_instant_present) { - rt64.enable_instant_present(); - enabled_instant_present = true; - } - // Tell the game that the RSP completed instantly. This will allow it to queue other task types, but it won't - // start another graphics task until the RDP is also complete. Games usually preserve the RSP inputs until the RDP - // is finished as well, so sending this early shouldn't be an issue in most cases. - // If this causes issues then the logic can be replaced with responding to yield requests. - sp_complete(); - ultramodern::measure_input_latency(); - - auto rt64_start = std::chrono::high_resolution_clock::now(); - rt64.send_dl(&task_action->task); - auto rt64_end = std::chrono::high_resolution_clock::now(); - dp_complete(); - // printf("RT64 ProcessDList time: %d us\n", static_cast(std::chrono::duration_cast(rt64_end - rt64_start).count())); - } - else if (const auto* swap_action = std::get_if(&action)) { - events_context.vi.current_buffer = events_context.vi.next_buffer; - rt64.update_screen(swap_action->origin); - display_refresh_rate = rt64.get_display_framerate(); - resolution_scale = rt64.get_resolution_scale(); - } - else if (const auto* config_action = std::get_if(&action)) { - ultramodern::GraphicsConfig new_config = cur_config; - if (old_config != new_config) { - rt64.update_config(old_config, new_config); - old_config = new_config; - } - } - else if (const auto* load_shader_cache_action = std::get_if(&action)) { - rt64.load_shader_cache(load_shader_cache_action->data); - } - } - } - // TODO move recomp code out of ultramodern. - recomp::destroy_ui(); - rt64.shutdown(); -} - -extern unsigned int VI_STATUS_REG; -extern unsigned int VI_ORIGIN_REG; -extern unsigned int VI_WIDTH_REG; -extern unsigned int VI_INTR_REG; -extern unsigned int VI_V_CURRENT_LINE_REG; -extern unsigned int VI_TIMING_REG; -extern unsigned int VI_V_SYNC_REG; -extern unsigned int VI_H_SYNC_REG; -extern unsigned int VI_LEAP_REG; -extern unsigned int VI_H_START_REG; -extern unsigned int VI_V_START_REG; -extern unsigned int VI_V_BURST_REG; -extern unsigned int VI_X_SCALE_REG; -extern unsigned int VI_Y_SCALE_REG; - -uint32_t hstart = 0; -uint32_t vi_origin_offset = 320 * sizeof(uint16_t); -bool vi_black = false; - -void set_dummy_vi() { - VI_STATUS_REG = 0x311E; - VI_WIDTH_REG = 0x140; - VI_V_SYNC_REG = 0x20D; - VI_H_SYNC_REG = 0xC15; - VI_LEAP_REG = 0x0C150C15; - hstart = 0x006C02EC; - VI_X_SCALE_REG = 0x200; - VI_V_CURRENT_LINE_REG = 0x0; - vi_origin_offset = 0x280; - VI_Y_SCALE_REG = 0x400; - VI_V_START_REG = 0x2501FF; - VI_V_BURST_REG = 0xE0204; - VI_INTR_REG = 0x2; -} - -extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) { - if (vi_black) { - VI_H_START_REG = 0; - } else { - VI_H_START_REG = hstart; - } - events_context.vi.next_buffer = frameBufPtr; - events_context.action_queue.enqueue(SwapBuffersAction{ osVirtualToPhysical(frameBufPtr) + vi_origin_offset }); -} - -extern "C" void osViSetMode(RDRAM_ARG PTR(OSViMode) mode_) { - OSViMode* mode = TO_PTR(OSViMode, mode_); - VI_STATUS_REG = mode->comRegs.ctrl; - VI_WIDTH_REG = mode->comRegs.width; - // burst - VI_V_SYNC_REG = mode->comRegs.vSync; - VI_H_SYNC_REG = mode->comRegs.hSync; - VI_LEAP_REG = mode->comRegs.leap; - hstart = mode->comRegs.hStart; - VI_X_SCALE_REG = mode->comRegs.xScale; - VI_V_CURRENT_LINE_REG = mode->comRegs.vCurrent; - - // TODO swap these every VI to account for fields changing - vi_origin_offset = mode->fldRegs[0].origin; - VI_Y_SCALE_REG = mode->fldRegs[0].yScale; - VI_V_START_REG = mode->fldRegs[0].vStart; - VI_V_BURST_REG = mode->fldRegs[0].vBurst; - VI_INTR_REG = mode->fldRegs[0].vIntr; -} - -#define VI_CTRL_TYPE_16 0x00002 -#define VI_CTRL_TYPE_32 0x00003 -#define VI_CTRL_GAMMA_DITHER_ON 0x00004 -#define VI_CTRL_GAMMA_ON 0x00008 -#define VI_CTRL_DIVOT_ON 0x00010 -#define VI_CTRL_SERRATE_ON 0x00040 -#define VI_CTRL_ANTIALIAS_MASK 0x00300 -#define VI_CTRL_ANTIALIAS_MODE_1 0x00100 -#define VI_CTRL_ANTIALIAS_MODE_2 0x00200 -#define VI_CTRL_ANTIALIAS_MODE_3 0x00300 -#define VI_CTRL_PIXEL_ADV_MASK 0x01000 -#define VI_CTRL_PIXEL_ADV_1 0x01000 -#define VI_CTRL_PIXEL_ADV_2 0x02000 -#define VI_CTRL_PIXEL_ADV_3 0x03000 -#define VI_CTRL_DITHER_FILTER_ON 0x10000 - -#define OS_VI_GAMMA_ON 0x0001 -#define OS_VI_GAMMA_OFF 0x0002 -#define OS_VI_GAMMA_DITHER_ON 0x0004 -#define OS_VI_GAMMA_DITHER_OFF 0x0008 -#define OS_VI_DIVOT_ON 0x0010 -#define OS_VI_DIVOT_OFF 0x0020 -#define OS_VI_DITHER_FILTER_ON 0x0040 -#define OS_VI_DITHER_FILTER_OFF 0x0080 - -extern "C" void osViSetSpecialFeatures(uint32_t func) { - if ((func & OS_VI_GAMMA_ON) != 0) { - VI_STATUS_REG |= VI_CTRL_GAMMA_ON; - } - - if ((func & OS_VI_GAMMA_OFF) != 0) { - VI_STATUS_REG &= ~VI_CTRL_GAMMA_ON; - } - - if ((func & OS_VI_GAMMA_DITHER_ON) != 0) { - VI_STATUS_REG |= VI_CTRL_GAMMA_DITHER_ON; - } - - if ((func & OS_VI_GAMMA_DITHER_OFF) != 0) { - VI_STATUS_REG &= ~VI_CTRL_GAMMA_DITHER_ON; - } - - if ((func & OS_VI_DIVOT_ON) != 0) { - VI_STATUS_REG |= VI_CTRL_DIVOT_ON; - } - - if ((func & OS_VI_DIVOT_OFF) != 0) { - VI_STATUS_REG &= ~VI_CTRL_DIVOT_ON; - } - - if ((func & OS_VI_DITHER_FILTER_ON) != 0) { - VI_STATUS_REG |= VI_CTRL_DITHER_FILTER_ON; - VI_STATUS_REG &= ~VI_CTRL_ANTIALIAS_MASK; - } - - if ((func & OS_VI_DITHER_FILTER_OFF) != 0) { - VI_STATUS_REG &= ~VI_CTRL_DITHER_FILTER_ON; - //VI_STATUS_REG |= __osViNext->modep->comRegs.ctrl & VI_CTRL_ANTIALIAS_MASK; - } -} - -extern "C" void osViBlack(uint8_t active) { - vi_black = active; -} - -extern "C" void osViSetXScale(float scale) { - if (scale != 1.0f) { - assert(false); - } -} - -extern "C" void osViSetYScale(float scale) { - if (scale != 1.0f) { - assert(false); - } -} - -extern "C" PTR(void) osViGetNextFramebuffer() { - return events_context.vi.next_buffer; -} - -extern "C" PTR(void) osViGetCurrentFramebuffer() { - return events_context.vi.current_buffer; -} - -void ultramodern::submit_rsp_task(RDRAM_ARG PTR(OSTask) task_) { - OSTask* task = TO_PTR(OSTask, task_); - - // Send gfx tasks to the graphics action queue - if (task->t.type == M_GFXTASK) { - events_context.action_queue.enqueue(SpTaskAction{ *task }); - } - // Set all other tasks as the RSP task - else { - events_context.sp_task_queue.enqueue(task); - } -} - -void ultramodern::send_si_message(RDRAM_ARG1) { - osSendMesg(PASS_RDRAM events_context.si.mq, events_context.si.msg, OS_MESG_NOBLOCK); -} - -std::string get_graphics_api_name(ultramodern::GraphicsApi api) { - if (api == ultramodern::GraphicsApi::Auto) { -#if defined(_WIN32) - api = ultramodern::GraphicsApi::D3D12; -#elif defined(__gnu_linux__) - api = ultramodern::GraphicsApi::Vulkan; -#else - static_assert(false && "Unimplemented") -#endif - } - - switch (api) { - case ultramodern::GraphicsApi::D3D12: - return "D3D12"; - case ultramodern::GraphicsApi::Vulkan: - return "Vulkan"; - default: - return "[Unknown graphics API]"; - } -} - -void ultramodern::init_events(RDRAM_ARG ultramodern::WindowHandle window_handle) { - moodycamel::LightweightSemaphore gfx_thread_ready; - moodycamel::LightweightSemaphore task_thread_ready; - events_context.rdram = rdram; - events_context.sp.gfx_thread = std::thread{ gfx_thread_func, rdram, &gfx_thread_ready, window_handle }; - events_context.sp.task_thread = std::thread{ task_thread_func, rdram, &task_thread_ready }; - - // Wait for the two sp threads to be ready before continuing to prevent the game from - // running before we're able to handle RSP tasks. - gfx_thread_ready.wait(); - task_thread_ready.wait(); - - ultramodern::RT64SetupResult setup_result = rt64_setup_result.load(); - if (rt64_setup_result != ultramodern::RT64SetupResult::Success) { - auto show_rt64_error = [](const std::string& msg) { - // TODO move recomp code out of ultramodern (message boxes). - recomp::message_box(("An error has been encountered on startup: " + msg).c_str()); - }; - const std::string driver_os_suffix = "\nPlease make sure your GPU drivers and your OS are up to date."; - switch (rt64_setup_result) { - case ultramodern::RT64SetupResult::DynamicLibrariesNotFound: - show_rt64_error("Failed to load dynamic libraries. Make sure the DLLs are next to the recomp executable."); - break; - case ultramodern::RT64SetupResult::InvalidGraphicsAPI: - show_rt64_error(get_graphics_api_name(cur_config.load().api_option) + " is not supported on this platform. Please select a different graphics API."); - break; - case ultramodern::RT64SetupResult::GraphicsAPINotFound: - show_rt64_error("Unable to initialize " + get_graphics_api_name(cur_config.load().api_option) + "." + driver_os_suffix); - break; - case ultramodern::RT64SetupResult::GraphicsDeviceNotFound: - show_rt64_error("Unable to find compatible graphics device." + driver_os_suffix); - break; - } - throw std::runtime_error("Failed to initialize RT64"); - } - - events_context.vi.thread = std::thread{ vi_thread_func }; -} - -void ultramodern::join_event_threads() { - events_context.sp.gfx_thread.join(); - events_context.vi.thread.join(); - - // Send a null RSP task to indicate that the RSP task thread should exit. - events_context.sp_task_queue.enqueue(nullptr); - events_context.sp.task_thread.join(); -} diff --git a/ultramodern/mesgqueue.cpp b/ultramodern/mesgqueue.cpp deleted file mode 100644 index 2821c87..0000000 --- a/ultramodern/mesgqueue.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#include - -#include "blockingconcurrentqueue.h" - -#include "ultra64.h" -#include "ultramodern.hpp" -#include "recomp.h" - -struct QueuedMessage { - PTR(OSMesgQueue) mq; - OSMesg mesg; - bool jam; -}; - -static moodycamel::BlockingConcurrentQueue external_messages {}; - -void enqueue_external_message(PTR(OSMesgQueue) mq, OSMesg msg, bool jam) { - external_messages.enqueue({mq, msg, jam}); -} - -bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block); - -void dequeue_external_messages(RDRAM_ARG1) { - QueuedMessage to_send; - while (external_messages.try_dequeue(to_send)) { - do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false); - } -} - -void ultramodern::wait_for_external_message(RDRAM_ARG1) { - QueuedMessage to_send; - external_messages.wait_dequeue(to_send); - do_send(PASS_RDRAM to_send.mq, to_send.mesg, to_send.jam, false); -} - -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; -} - -bool do_send(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, bool jam, bool block) { - OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_); - if (!block) { - // If non-blocking, fail if the queue is full. - if (MQ_IS_FULL(mq)) { - return false; - } - } - 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, ultramodern::this_thread())->id); - ultramodern::thread_queue_insert(PASS_RDRAM GET_MEMBER(OSMesgQueue, mq_, blocked_on_send), ultramodern::this_thread()); - ultramodern::run_next_thread_and_wait(PASS_RDRAM1); - } - } - - if (jam) { - // Jams insert at the head of the message queue's buffer. - mq->first = (mq->first + mq->msgCount - 1) % mq->msgCount; - TO_PTR(OSMesg, mq->msg)[mq->first] = msg; - mq->validCount++; - } - else { - // Sends insert at the tail of the message queue's buffer. - s32 last = (mq->first + mq->validCount) % mq->msgCount; - TO_PTR(OSMesg, mq->msg)[last] = msg; - mq->validCount++; - } - - // If any threads were blocked on receiving from this message queue, pop the first one and schedule it. - PTR(PTR(OSThread)) blocked_queue = GET_MEMBER(OSMesgQueue, mq_, blocked_on_recv); - if (!ultramodern::thread_queue_empty(PASS_RDRAM blocked_queue)) { - ultramodern::schedule_running_thread(PASS_RDRAM ultramodern::thread_queue_pop(PASS_RDRAM blocked_queue)); - } - - return true; -} - -bool do_recv(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, bool block) { - OSMesgQueue* mq = TO_PTR(OSMesgQueue, mq_); - if (!block) { - // If non-blocking, fail if the queue is empty - if (MQ_IS_EMPTY(mq)) { - return false; - } - } 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, ultramodern::this_thread())->id); - ultramodern::thread_queue_insert(PASS_RDRAM GET_MEMBER(OSMesgQueue, mq_, blocked_on_recv), ultramodern::this_thread()); - ultramodern::run_next_thread_and_wait(PASS_RDRAM1); - } - } - - if (msg_ != NULLPTR) { - *TO_PTR(OSMesg, msg_) = TO_PTR(OSMesg, mq->msg)[mq->first]; - } - - mq->first = (mq->first + 1) % mq->msgCount; - mq->validCount--; - - // If any threads were blocked on sending to this message queue, pop the first one and schedule it. - PTR(PTR(OSThread)) blocked_queue = GET_MEMBER(OSMesgQueue, mq_, blocked_on_send); - if (!ultramodern::thread_queue_empty(PASS_RDRAM blocked_queue)) { - ultramodern::schedule_running_thread(PASS_RDRAM ultramodern::thread_queue_pop(PASS_RDRAM blocked_queue)); - } - - return true; -} - -extern "C" s32 osSendMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) { - OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_); - bool jam = false; - - // Don't directly send to the message queue if this isn't a game thread to avoid contention. - if (!ultramodern::is_game_thread()) { - enqueue_external_message(mq_, msg, jam); - return 0; - } - - // Handle any messages that have been received from an external thread. - dequeue_external_messages(PASS_RDRAM1); - - // Try to send the message. - bool sent = do_send(PASS_RDRAM mq_, msg, jam, flags == OS_MESG_BLOCK); - - // Check the queue to see if this thread should swap execution to another. - ultramodern::check_running_queue(PASS_RDRAM1); - - return sent ? 0 : -1; -} - -extern "C" s32 osJamMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, s32 flags) { - OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_); - bool jam = true; - - // Don't directly send to the message queue if this isn't a game thread to avoid contention. - if (!ultramodern::is_game_thread()) { - enqueue_external_message(mq_, msg, jam); - return 0; - } - - // Handle any messages that have been received from an external thread. - dequeue_external_messages(PASS_RDRAM1); - - // Try to send the message. - bool sent = do_send(PASS_RDRAM mq_, msg, jam, flags == OS_MESG_BLOCK); - - // Check the queue to see if this thread should swap execution to another. - ultramodern::check_running_queue(PASS_RDRAM1); - - return sent ? 0 : -1; -} - -extern "C" s32 osRecvMesg(RDRAM_ARG PTR(OSMesgQueue) mq_, PTR(OSMesg) msg_, s32 flags) { - OSMesgQueue *mq = TO_PTR(OSMesgQueue, mq_); - - assert(ultramodern::is_game_thread() && "RecvMesg not allowed outside of game threads."); - - // Handle any messages that have been received from an external thread. - dequeue_external_messages(PASS_RDRAM1); - - // Try to receive a message. - bool received = do_recv(PASS_RDRAM mq_, msg_, flags == OS_MESG_BLOCK); - - // Check the queue to see if this thread should swap execution to another. - ultramodern::check_running_queue(PASS_RDRAM1); - - return received ? 0 : -1; -} diff --git a/ultramodern/misc_ultra.cpp b/ultramodern/misc_ultra.cpp deleted file mode 100644 index 2b794b7..0000000 --- a/ultramodern/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/ultramodern/port_main.c b/ultramodern/port_main.c deleted file mode 100644 index 968b4d3..0000000 --- a/ultramodern/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/ultramodern/rt64_layer.cpp b/ultramodern/rt64_layer.cpp deleted file mode 100644 index 1a03592..0000000 --- a/ultramodern/rt64_layer.cpp +++ /dev/null @@ -1,319 +0,0 @@ -#include -#include -// #include - -#define HLSL_CPU -#include "hle/rt64_application.h" -#include "rt64_layer.h" -#include "rt64_render_hooks.h" - -ultramodern::RT64Context::~RT64Context() = default; - -static RT64::UserConfiguration::Antialiasing device_max_msaa = RT64::UserConfiguration::Antialiasing::None; -static bool sample_positions_supported = false; -static bool high_precision_fb_enabled = false; - -static uint8_t DMEM[0x1000]; -static uint8_t IMEM[0x1000]; - -unsigned int MI_INTR_REG = 0; - -unsigned int DPC_START_REG = 0; -unsigned int DPC_END_REG = 0; -unsigned int DPC_CURRENT_REG = 0; -unsigned int DPC_STATUS_REG = 0; -unsigned int DPC_CLOCK_REG = 0; -unsigned int DPC_BUFBUSY_REG = 0; -unsigned int DPC_PIPEBUSY_REG = 0; -unsigned int DPC_TMEM_REG = 0; - -unsigned int VI_STATUS_REG = 0; -unsigned int VI_ORIGIN_REG = 0; -unsigned int VI_WIDTH_REG = 0; -unsigned int VI_INTR_REG = 0; -unsigned int VI_V_CURRENT_LINE_REG = 0; -unsigned int VI_TIMING_REG = 0; -unsigned int VI_V_SYNC_REG = 0; -unsigned int VI_H_SYNC_REG = 0; -unsigned int VI_LEAP_REG = 0; -unsigned int VI_H_START_REG = 0; -unsigned int VI_V_START_REG = 0; -unsigned int VI_V_BURST_REG = 0; -unsigned int VI_X_SCALE_REG = 0; -unsigned int VI_Y_SCALE_REG = 0; - -void dummy_check_interrupts() { - -} - -RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampleCounts bits) { - if (bits & RT64::RenderSampleCount::Bits::COUNT_2) { - if (bits & RT64::RenderSampleCount::Bits::COUNT_4) { - if (bits & RT64::RenderSampleCount::Bits::COUNT_8) { - return RT64::UserConfiguration::Antialiasing::MSAA8X; - } - return RT64::UserConfiguration::Antialiasing::MSAA4X; - } - return RT64::UserConfiguration::Antialiasing::MSAA2X; - }; - return RT64::UserConfiguration::Antialiasing::None; -} - -RT64::UserConfiguration::InternalColorFormat to_rt64(ultramodern::HighPrecisionFramebuffer option) { - switch (option) { - case ultramodern::HighPrecisionFramebuffer::Off: - return RT64::UserConfiguration::InternalColorFormat::Standard; - case ultramodern::HighPrecisionFramebuffer::On: - return RT64::UserConfiguration::InternalColorFormat::High; - case ultramodern::HighPrecisionFramebuffer::Auto: - return RT64::UserConfiguration::InternalColorFormat::Automatic; - default: - return RT64::UserConfiguration::InternalColorFormat::OptionCount; - } -} - -void set_application_user_config(RT64::Application* application, const ultramodern::GraphicsConfig& config) { - switch (config.res_option) { - default: - case ultramodern::Resolution::Auto: - application->userConfig.resolution = RT64::UserConfiguration::Resolution::WindowIntegerScale; - application->userConfig.downsampleMultiplier = 1; - break; - case ultramodern::Resolution::Original: - application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual; - application->userConfig.resolutionMultiplier = config.ds_option; - application->userConfig.downsampleMultiplier = config.ds_option; - break; - case ultramodern::Resolution::Original2x: - application->userConfig.resolution = RT64::UserConfiguration::Resolution::Manual; - application->userConfig.resolutionMultiplier = 2.0 * config.ds_option; - application->userConfig.downsampleMultiplier = config.ds_option; - break; - } - - switch (config.hr_option) { - default: - case ultramodern::HUDRatioMode::Original: - application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Original; - break; - case ultramodern::HUDRatioMode::Clamp16x9: - application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Manual; - application->userConfig.extAspectTarget = 16.0/9.0; - break; - case ultramodern::HUDRatioMode::Full: - application->userConfig.extAspectRatio = RT64::UserConfiguration::AspectRatio::Expand; - break; - } - - application->userConfig.aspectRatio = config.ar_option; - application->userConfig.antialiasing = config.msaa_option; - application->userConfig.refreshRate = config.rr_option; - application->userConfig.refreshRateTarget = config.rr_manual_value; - application->userConfig.internalColorFormat = to_rt64(config.hpfb_option); -} - -ultramodern::RT64SetupResult map_setup_result(RT64::Application::SetupResult rt64_result) { - switch (rt64_result) { - case RT64::Application::SetupResult::Success: - return ultramodern::RT64SetupResult::Success; - case RT64::Application::SetupResult::DynamicLibrariesNotFound: - return ultramodern::RT64SetupResult::DynamicLibrariesNotFound; - case RT64::Application::SetupResult::InvalidGraphicsAPI: - return ultramodern::RT64SetupResult::InvalidGraphicsAPI; - case RT64::Application::SetupResult::GraphicsAPINotFound: - return ultramodern::RT64SetupResult::GraphicsAPINotFound; - case RT64::Application::SetupResult::GraphicsDeviceNotFound: - return ultramodern::RT64SetupResult::GraphicsDeviceNotFound; - } -} - -ultramodern::RT64Context::RT64Context(uint8_t* rdram, ultramodern::WindowHandle window_handle, bool debug) { - static unsigned char dummy_rom_header[0x40]; - set_rt64_hooks(); - - // Set up the RT64 application core fields. - RT64::Application::Core appCore{}; -#if defined(_WIN32) - appCore.window = window_handle.window; -#elif defined(__ANDROID__) - assert(false && "Unimplemented"); -#elif defined(__linux__) - appCore.window.display = window_handle.display; - appCore.window.window = window_handle.window; -#endif - - appCore.checkInterrupts = dummy_check_interrupts; - - appCore.HEADER = dummy_rom_header; - appCore.RDRAM = rdram; - appCore.DMEM = DMEM; - appCore.IMEM = IMEM; - - appCore.MI_INTR_REG = &MI_INTR_REG; - - appCore.DPC_START_REG = &DPC_START_REG; - appCore.DPC_END_REG = &DPC_END_REG; - appCore.DPC_CURRENT_REG = &DPC_CURRENT_REG; - appCore.DPC_STATUS_REG = &DPC_STATUS_REG; - appCore.DPC_CLOCK_REG = &DPC_CLOCK_REG; - appCore.DPC_BUFBUSY_REG = &DPC_BUFBUSY_REG; - appCore.DPC_PIPEBUSY_REG = &DPC_PIPEBUSY_REG; - appCore.DPC_TMEM_REG = &DPC_TMEM_REG; - - appCore.VI_STATUS_REG = &VI_STATUS_REG; - appCore.VI_ORIGIN_REG = &VI_ORIGIN_REG; - appCore.VI_WIDTH_REG = &VI_WIDTH_REG; - appCore.VI_INTR_REG = &VI_INTR_REG; - appCore.VI_V_CURRENT_LINE_REG = &VI_V_CURRENT_LINE_REG; - appCore.VI_TIMING_REG = &VI_TIMING_REG; - appCore.VI_V_SYNC_REG = &VI_V_SYNC_REG; - appCore.VI_H_SYNC_REG = &VI_H_SYNC_REG; - appCore.VI_LEAP_REG = &VI_LEAP_REG; - appCore.VI_H_START_REG = &VI_H_START_REG; - appCore.VI_V_START_REG = &VI_V_START_REG; - appCore.VI_V_BURST_REG = &VI_V_BURST_REG; - appCore.VI_X_SCALE_REG = &VI_X_SCALE_REG; - appCore.VI_Y_SCALE_REG = &VI_Y_SCALE_REG; - - // Set up the RT64 application configuration fields. - RT64::ApplicationConfiguration appConfig; - appConfig.useConfigurationFile = false; - - // Create the RT64 application. - app = std::make_unique(appCore, appConfig); - - // Set initial user config settings based on the current settings. - ultramodern::GraphicsConfig cur_config = ultramodern::get_graphics_config(); - set_application_user_config(app.get(), cur_config); - app->userConfig.developerMode = debug; - // Force gbi depth branches to prevent LODs from kicking in. - app->enhancementConfig.f3dex.forceBranch = true; - // Scale LODs based on the output resolution. - app->enhancementConfig.textureLOD.scale = true; - // Pick an API if the user has set an override. - switch (cur_config.api_option) { - case ultramodern::GraphicsApi::D3D12: - app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::D3D12; - break; - case ultramodern::GraphicsApi::Vulkan: - app->userConfig.graphicsAPI = RT64::UserConfiguration::GraphicsAPI::Vulkan; - break; - default: - case ultramodern::GraphicsApi::Auto: - // Don't override if auto is selected. - break; - } - - // Set up the RT64 application. - uint32_t thread_id = 0; -#ifdef _WIN32 - thread_id = window_handle.thread_id; -#endif - setup_result = map_setup_result(app->setup(thread_id)); - if (setup_result != ultramodern::RT64SetupResult::Success) { - app = nullptr; - return; - } - - // Set the application's fullscreen state. - app->setFullScreen(cur_config.wm_option == ultramodern::WindowMode::Fullscreen); - - // Check if the selected device actually supports MSAA sample positions and MSAA for for the formats that will be used - // and downgrade the configuration accordingly. - if (app->device->getCapabilities().sampleLocations) { - RT64::RenderSampleCounts color_sample_counts = app->device->getSampleCountsSupported(RT64::RenderFormat::R8G8B8A8_UNORM); - RT64::RenderSampleCounts depth_sample_counts = app->device->getSampleCountsSupported(RT64::RenderFormat::D32_FLOAT); - RT64::RenderSampleCounts common_sample_counts = color_sample_counts & depth_sample_counts; - device_max_msaa = compute_max_supported_aa(common_sample_counts); - sample_positions_supported = true; - } - else { - device_max_msaa = RT64::UserConfiguration::Antialiasing::None; - sample_positions_supported = false; - } - - high_precision_fb_enabled = app->shaderLibrary->usesHDR; -} - -void ultramodern::RT64Context::send_dl(const OSTask* task) { - app->state->rsp->reset(); - app->interpreter->loadUCodeGBI(task->t.ucode & 0x3FFFFFF, task->t.ucode_data & 0x3FFFFFF, true); - app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true); -} - -void ultramodern::RT64Context::update_screen(uint32_t vi_origin) { - VI_ORIGIN_REG = vi_origin; - - app->updateScreen(); -} - -void ultramodern::RT64Context::shutdown() { - if (app != nullptr) { - app->end(); - } -} - -void ultramodern::RT64Context::update_config(const ultramodern::GraphicsConfig& old_config, const ultramodern::GraphicsConfig& new_config) { - if (new_config.wm_option != old_config.wm_option) { - app->setFullScreen(new_config.wm_option == ultramodern::WindowMode::Fullscreen); - } - - set_application_user_config(app.get(), new_config); - - app->updateUserConfig(true); - - if (new_config.msaa_option != old_config.msaa_option) { - app->updateMultisampling(); - } -} - -void ultramodern::RT64Context::enable_instant_present() { - // Enable the present early presentation mode for minimal latency. - app->enhancementConfig.presentation.mode = RT64::EnhancementConfiguration::Presentation::Mode::PresentEarly; - - app->updateEnhancementConfig(); -} - -uint32_t ultramodern::RT64Context::get_display_framerate() { - return app->presentQueue->ext.sharedResources->swapChainRate; -} - -float ultramodern::RT64Context::get_resolution_scale() { - constexpr int ReferenceHeight = 240; - switch (app->userConfig.resolution) { - case RT64::UserConfiguration::Resolution::WindowIntegerScale: - if (app->sharedQueueResources->swapChainHeight > 0) { - return std::max(float((app->sharedQueueResources->swapChainHeight + ReferenceHeight - 1) / ReferenceHeight), 1.0f); - } - else { - return 1.0f; - } - case RT64::UserConfiguration::Resolution::Manual: - return float(app->userConfig.resolutionMultiplier); - case RT64::UserConfiguration::Resolution::Original: - default: - return 1.0f; - } -} - -void ultramodern::RT64Context::load_shader_cache(std::span cache_binary) { - // TODO figure out how to avoid a copy here. - std::istringstream cache_stream{std::string{cache_binary.data(), cache_binary.size()}}; - - if (!app->rasterShaderCache->loadOfflineList(cache_stream)) { - printf("Failed to preload shader cache!\n"); - assert(false); - } -} - -RT64::UserConfiguration::Antialiasing ultramodern::RT64MaxMSAA() { - return device_max_msaa; -} - -bool ultramodern::RT64SamplePositionsSupported() { - return sample_positions_supported; -} - -bool ultramodern::RT64HighPrecisionFBEnabled() { - return high_precision_fb_enabled; -} diff --git a/ultramodern/scheduling.cpp b/ultramodern/scheduling.cpp deleted file mode 100644 index 3b6c9ad..0000000 --- a/ultramodern/scheduling.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "ultramodern.hpp" - -void ultramodern::schedule_running_thread(RDRAM_ARG PTR(OSThread) t_) { - debug_printf("[Scheduling] Adding thread %d to the running queue\n", TO_PTR(OSThread, t_)->id); - thread_queue_insert(PASS_RDRAM running_queue, t_); - TO_PTR(OSThread, t_)->state = OSThreadState::QUEUED; -} - -void swap_to_thread(RDRAM_ARG OSThread *to) { - debug_printf("[Scheduling] Thread %d giving execution to thread %d\n", TO_PTR(OSThread, ultramodern::this_thread())->id, to->id); - // Insert this thread in the running queue. - ultramodern::thread_queue_insert(PASS_RDRAM ultramodern::running_queue, ultramodern::this_thread()); - TO_PTR(OSThread, ultramodern::this_thread())->state = OSThreadState::QUEUED; - // Unpause the target thread and wait for this one to be unpaused. - ultramodern::resume_thread_and_wait(PASS_RDRAM to); -} - -void ultramodern::check_running_queue(RDRAM_ARG1) { - // Check if there are any threads in the running queue. - if (!thread_queue_empty(PASS_RDRAM running_queue)) { - // Check if the highest priority thread in the queue is higher priority than the current thread. - OSThread* next_thread = TO_PTR(OSThread, ultramodern::thread_queue_peek(PASS_RDRAM running_queue)); - OSThread* self = TO_PTR(OSThread, ultramodern::this_thread()); - if (next_thread->priority > self->priority) { - ultramodern::thread_queue_pop(PASS_RDRAM running_queue); - // Swap to the higher priority thread. - swap_to_thread(PASS_RDRAM next_thread); - } - } -} - -extern "C" void pause_self(RDRAM_ARG1) { - while (true) { - // Wait until an external message arrives, then allow the next thread to run. - ultramodern::wait_for_external_message(PASS_RDRAM1); - ultramodern::check_running_queue(PASS_RDRAM1); - } -} diff --git a/ultramodern/task_win32.cpp b/ultramodern/task_win32.cpp deleted file mode 100644 index c3e511c..0000000 --- a/ultramodern/task_win32.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifdef _WIN32 - -#include - -#include "ultra64.h" -#include "ultramodern.hpp" - -extern "C" unsigned int sleep(unsigned int seconds) { - Sleep(seconds * 1000); - return 0; -} - -#endif \ No newline at end of file diff --git a/ultramodern/threadqueue.cpp b/ultramodern/threadqueue.cpp deleted file mode 100644 index 5869811..0000000 --- a/ultramodern/threadqueue.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include - -#include "ultramodern.hpp" - -static PTR(OSThread) running_queue_impl = NULLPTR; - -static PTR(OSThread)* queue_to_ptr(RDRAM_ARG PTR(PTR(OSThread)) queue) { - if (queue == ultramodern::running_queue) { - return &running_queue_impl; - } - return TO_PTR(PTR(OSThread), queue); -} - -void ultramodern::thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) toadd_) { - PTR(OSThread)* cur = queue_to_ptr(PASS_RDRAM queue_); - OSThread* toadd = TO_PTR(OSThread, toadd_); - debug_printf("[Thread Queue] Inserting thread %d into queue 0x%08X\n", toadd->id, (uintptr_t)queue_); - while (*cur && TO_PTR(OSThread, *cur)->priority > toadd->priority) { - cur = &TO_PTR(OSThread, *cur)->next; - } - toadd->next = (*cur); - toadd->queue = queue_; - *cur = toadd_; - - debug_printf(" Contains:"); - cur = queue_to_ptr(PASS_RDRAM queue_); - while (*cur) { - debug_printf("%d (%d) ", TO_PTR(OSThread, *cur)->id, TO_PTR(OSThread, *cur)->priority); - cur = &TO_PTR(OSThread, *cur)->next; - } - debug_printf("\n"); -} - -PTR(OSThread) ultramodern::thread_queue_pop(RDRAM_ARG PTR(PTR(OSThread)) queue_) { - PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_); - PTR(OSThread) ret = *queue; - *queue = TO_PTR(OSThread, ret)->next; - TO_PTR(OSThread, ret)->queue = NULLPTR; - debug_printf("[Thread Queue] Popped thread %d from queue 0x%08X\n", TO_PTR(OSThread, ret)->id, (uintptr_t)queue_); - return ret; -} - -bool ultramodern::thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) t_) { - debug_printf("[Thread Queue] Removing thread %d from queue 0x%08X\n", TO_PTR(OSThread, t_)->id, (uintptr_t)queue_); - - PTR(PTR(OSThread)) cur = queue_; - while (cur != NULLPTR) { - PTR(OSThread)* cur_ptr = queue_to_ptr(PASS_RDRAM queue_); - if (*cur_ptr == t_) { - return true; - } - cur = TO_PTR(OSThread, *cur_ptr)->next; - } - - return false; -} - -bool ultramodern::thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue_) { - PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_); - return *queue == NULLPTR; -} - -PTR(OSThread) ultramodern::thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue_) { - PTR(OSThread)* queue = queue_to_ptr(PASS_RDRAM queue_); - return *queue; -} diff --git a/ultramodern/threads.cpp b/ultramodern/threads.cpp deleted file mode 100644 index 9b17a52..0000000 --- a/ultramodern/threads.cpp +++ /dev/null @@ -1,346 +0,0 @@ -#include -#include -#include -#include - -#include "ultra64.h" -#include "ultramodern.hpp" -#include "blockingconcurrentqueue.h" - -// 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 ultramodern::set_main_thread() { - ::is_game_thread = true; - is_main_thread = true; -} - -bool ultramodern::is_game_thread() { - return ::is_game_thread; -} - -#if 0 -int main(int argc, char** argv) { - ultramodern::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 - -#if defined(_WIN32) -void ultramodern::set_native_thread_name(const std::string& name) { - std::wstring wname{name.begin(), name.end()}; - - HRESULT r; - r = SetThreadDescription( - GetCurrentThread(), - wname.c_str() - ); -} - -void ultramodern::set_native_thread_priority(ThreadPriority pri) { - int nPriority = THREAD_PRIORITY_NORMAL; - - // Convert ThreadPriority to Win32 priority - switch (pri) { - case ThreadPriority::Low: - nPriority = THREAD_PRIORITY_BELOW_NORMAL; - break; - case ThreadPriority::Normal: - nPriority = THREAD_PRIORITY_NORMAL; - break; - case ThreadPriority::High: - nPriority = THREAD_PRIORITY_ABOVE_NORMAL; - break; - case ThreadPriority::VeryHigh: - nPriority = THREAD_PRIORITY_HIGHEST; - break; - case ThreadPriority::Critical: - nPriority = THREAD_PRIORITY_TIME_CRITICAL; - break; - default: - throw std::runtime_error("Invalid thread priority!"); - break; - } - // SetThreadPriority(GetCurrentThread(), nPriority); -} -#elif defined(__linux__) -void ultramodern::set_native_thread_name(const std::string& name) { - pthread_setname_np(pthread_self(), name.c_str()); -} - -void ultramodern::set_native_thread_priority(ThreadPriority pri) { - // TODO linux thread priority - // printf("set_native_thread_priority unimplemented\n"); - // int nPriority = THREAD_PRIORITY_NORMAL; - - // // Convert ThreadPriority to Win32 priority - // switch (pri) { - // case ThreadPriority::Low: - // nPriority = THREAD_PRIORITY_BELOW_NORMAL; - // break; - // case ThreadPriority::Normal: - // nPriority = THREAD_PRIORITY_NORMAL; - // break; - // case ThreadPriority::High: - // nPriority = THREAD_PRIORITY_ABOVE_NORMAL; - // break; - // case ThreadPriority::VeryHigh: - // nPriority = THREAD_PRIORITY_HIGHEST; - // break; - // case ThreadPriority::Critical: - // nPriority = THREAD_PRIORITY_TIME_CRITICAL; - // break; - // default: - // throw std::runtime_error("Invalid thread priority!"); - // break; - // } -} -#endif - -std::atomic_int temporary_threads = 0; -std::atomic_int permanent_threads = 0; - -void wait_for_resumed(RDRAM_ARG UltraThreadContext* thread_context) { - TO_PTR(OSThread, ultramodern::this_thread())->context->running.wait(); - // If this thread's context was replaced by another thread or deleted, destroy it again from its own context. - // This will trigger thread cleanup instead. - if (TO_PTR(OSThread, ultramodern::this_thread())->context != thread_context) { - osDestroyThread(PASS_RDRAM NULLPTR); - } -} - -void resume_thread(OSThread* t) { - debug_printf("[Thread] Resuming execution of thread %d\n", t->id); - t->context->running.signal(); -} - -void run_next_thread(RDRAM_ARG1) { - if (ultramodern::thread_queue_empty(PASS_RDRAM ultramodern::running_queue)) { - throw std::runtime_error("No threads left to run!\n"); - } - - OSThread* to_run = TO_PTR(OSThread, ultramodern::thread_queue_pop(PASS_RDRAM ultramodern::running_queue)); - debug_printf("[Scheduling] Resuming execution of thread %d\n", to_run->id); - to_run->context->running.signal(); -} - -void ultramodern::run_next_thread_and_wait(RDRAM_ARG1) { - UltraThreadContext* cur_context = TO_PTR(OSThread, thread_self)->context; - run_next_thread(PASS_RDRAM1); - wait_for_resumed(PASS_RDRAM cur_context); -} - -void ultramodern::resume_thread_and_wait(RDRAM_ARG OSThread *t) { - UltraThreadContext* cur_context = TO_PTR(OSThread, thread_self)->context; - resume_thread(t); - wait_for_resumed(PASS_RDRAM cur_context); -} - -static void _thread_func(RDRAM_ARG PTR(OSThread) self_, PTR(thread_func_t) entrypoint, PTR(void) arg, UltraThreadContext* thread_context) { - 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 - ultramodern::set_native_thread_name("Game Thread " + std::to_string(self->id)); - ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::High); - - // TODO fix these being hardcoded (this is only used for quicksaving) - if ((self->id == 2 && self->priority == 5) || self->id == 13) { // slowly, flashrom - temporary_threads.fetch_add(1); - } - else if (self->id != 1 && self->id != 2) { // ignore idle and fault - permanent_threads.fetch_add(1); - } - - // Signal the initialized semaphore to indicate that this thread can be started. - thread_context->initialized.signal(); - - debug_printf("[Thread] Thread waiting to be started: %d\n", self->id); - - // Wait until the thread is marked as running. - wait_for_resumed(PASS_RDRAM thread_context); - - // Make sure the thread wasn't replaced or destroyed before it was started. - if (self->context == thread_context) { - debug_printf("[Thread] Thread started: %d\n", self->id); - try { - // Run the thread's function with the provided argument. - run_thread_function(PASS_RDRAM entrypoint, self->sp, arg); - } catch (ultramodern::thread_terminated& terminated) { - } - } - else { - debug_printf("[Thread] Thread destroyed before being started: %d\n", self->id); - } - - // Check if the thread hasn't been destroyed or replaced. If so, then the thread terminated or destroyed itself, - // so mark this thread as destroyed and run the next queued thread. - if (self->context == thread_context) { - self->context = nullptr; - run_next_thread(PASS_RDRAM1); - } - - // Dispose of this thread now that it's completed or terminated. - ultramodern::cleanup_thread(thread_context); - - // TODO fix these being hardcoded (this is only used for quicksaving) - if ((self->id == 2 && self->priority == 5) || self->id == 13) { // slowly, flashrom - temporary_threads.fetch_sub(1); - } -} - -uint32_t ultramodern::permanent_thread_count() { - return permanent_threads.load(); -} - -uint32_t ultramodern::temporary_thread_count() { - return temporary_threads.load(); -} - -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 ready to be started. - t->context->initialized.wait(); - - debug_printf("[os] Thread %d is ready to be started\n", t->id); - - // If this is a game thread, insert the new thread into the running queue and then check the running queue. - if (thread_self) { - ultramodern::schedule_running_thread(PASS_RDRAM t_); - ultramodern::check_running_queue(PASS_RDRAM1); - } - // Otherwise, immediately start the thread and terminate this one. - else { - t->state = OSThreadState::QUEUED; - resume_thread(t); - //throw ultramodern::thread_terminated{}; - } -} - -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->queue = NULLPTR; - t->priority = pri; - t->id = id; - t->state = OSThreadState::STOPPED; - 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. - // Pass the context as an argument to the thread function to ensure that it can't get cleared before the thread captures its value. - t->context = new UltraThreadContext{}; - t->context->host_thread = std::thread{_thread_func, PASS_RDRAM t_, entrypoint, arg, t->context}; -} - -extern "C" void osStopThread(RDRAM_ARG PTR(OSThread) t_) { - assert(false); -} - -extern "C" void osDestroyThread(RDRAM_ARG PTR(OSThread) t_) { - if (t_ == NULLPTR) { - t_ = thread_self; - } - OSThread* t = TO_PTR(OSThread, t_); - // Check if the thread is destroying itself (arg is null or thread_self) - if (t_ == thread_self) { - throw ultramodern::thread_terminated{}; - } - // Otherwise if the thread isn't stopped, remove it from its currrent queue., - if (t->state != OSThreadState::STOPPED) { - ultramodern::thread_queue_remove(PASS_RDRAM t->queue, t_); - } - // Check if the thread has already been destroyed to prevent destroying it again. - UltraThreadContext* cur_context = t->context; - if (cur_context != nullptr) { - // Mark the target thread as destroyed and resume it. When it starts it'll check this and terminate itself instead of resuming. - t->context = nullptr; - cur_context->running.signal(); - } -} - -extern "C" void osSetThreadPri(RDRAM_ARG PTR(OSThread) t_, OSPri pri) { - if (t_ == NULLPTR) { - t_ = thread_self; - } - OSThread* t = TO_PTR(OSThread, t_); - - if (t->priority != pri) { - t->priority = pri; - - if (t_ != ultramodern::this_thread() && t->state != OSThreadState::STOPPED) { - ultramodern::thread_queue_remove(PASS_RDRAM t->queue, t_); - ultramodern::thread_queue_insert(PASS_RDRAM t->queue, t_); - } - - ultramodern::check_running_queue(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; -} - -PTR(OSThread) ultramodern::this_thread() { - return thread_self; -} - -static std::thread thread_cleaner_thread; -static moodycamel::BlockingConcurrentQueue deleted_threads{}; -extern std::atomic_bool exited; - -void thread_cleaner_func() { - using namespace std::chrono_literals; - while (!exited) { - UltraThreadContext* to_delete; - if (deleted_threads.wait_dequeue_timed(to_delete, 10ms)) { - debug_printf("[Cleanup] Deleting thread context %p\n", to_delete); - - to_delete->host_thread.join(); - delete to_delete; - } - } -} - -void ultramodern::init_thread_cleanup() { - thread_cleaner_thread = std::thread{thread_cleaner_func}; -} - -void ultramodern::cleanup_thread(UltraThreadContext *cur_context) { - deleted_threads.enqueue(cur_context); -} - -void ultramodern::join_thread_cleaner_thread() { - thread_cleaner_thread.join(); -} diff --git a/ultramodern/timer.cpp b/ultramodern/timer.cpp deleted file mode 100644 index b278ff3..0000000 --- a/ultramodern/timer.cpp +++ /dev/null @@ -1,224 +0,0 @@ -#include -#include -#include -#include "blockingconcurrentqueue.h" - -#include "ultra64.h" -#include "ultramodern.hpp" - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include "Windows.h" -#endif - -// Start time for the program -static std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_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(OSTimer) 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::high_resolution_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::high_resolution_clock::time_point ticks_to_timepoint(uint64_t ticks) { - return start_time + ticks_to_duration(ticks); -} - -uint64_t time_now() { - return duration_to_ticks(std::chrono::high_resolution_clock::now() - start_time); -} - -void timer_thread(RDRAM_ARG1) { - ultramodern::set_native_thread_name("Timer Thread"); - ultramodern::set_native_thread_priority(ultramodern::ThreadPriority::VeryHigh); - - // 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::high_resolution_clock::now(); - - // Wait for either the duration to complete or a new action to come through - if (wait_duration.count() >= 0 && 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 ultramodern::init_timers(RDRAM_ARG1) { - timer_context.thread = std::thread{ timer_thread, PASS_RDRAM1 }; - timer_context.thread.detach(); -} - -uint32_t ultramodern::get_speed_multiplier() { - return speed_multiplier; -} - -std::chrono::high_resolution_clock::time_point ultramodern::get_start() { - return start_time; -} - -std::chrono::high_resolution_clock::duration ultramodern::time_since_start() { - return std::chrono::high_resolution_clock::now() - start_time; -} - -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; -} - -#ifdef _WIN32 - -// The implementations of std::chrono::sleep_until and sleep_for were affected by changing the system clock backwards in older versions -// of Microsoft's STL. This was fixed as of Visual Studio 2022 17.9, but to be safe ultramodern uses Win32 Sleep directly. -void ultramodern::sleep_milliseconds(uint32_t millis) { - Sleep(millis); -} - -void ultramodern::sleep_until(const std::chrono::high_resolution_clock::time_point& time_point) { - auto time_now = std::chrono::high_resolution_clock::now(); - if (time_point > time_now) { - long long delta_ms = std::chrono::ceil(time_point - time_now).count(); - // printf("Sleeping %lld %d ms\n", delta_ms, (uint32_t)delta_ms); - Sleep(delta_ms); - } -} - -#else - -void ultramodern::sleep_milliseconds(uint32_t millis) { - std::this_thread::sleep_for(std::chrono::milliseconds{millis}); -} - -void ultramodern::sleep_until(const std::chrono::high_resolution_clock::time_point& time_point) { - std::this_thread::sleep_until(time_point); -} - -#endif diff --git a/ultramodern/ultra64.h b/ultramodern/ultra64.h deleted file mode 100644 index f2cc5ca..0000000 --- a/ultramodern/ultra64.h +++ /dev/null @@ -1,260 +0,0 @@ -#ifndef __ULTRA64_ultramodern_H__ -#define __ULTRA64_ultramodern_H__ - -#include - -#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; - -#if 0 // For native compilation -# define PTR(x) x* -# define RDRAM_ARG -# define RDRAM_ARG1 -# define PASS_RDRAM -# define PASS_RDRAM1 -# define TO_PTR(type, var) var -# define GET_MEMBER(type, addr, member) (&addr->member) -# ifdef __cplusplus -# define NULLPTR nullptr -# endif -#else -# 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])) -# define GET_MEMBER(type, addr, member) (addr + (intptr_t)&(((type*)nullptr)->member)) -# ifdef __cplusplus -# define NULLPTR (PTR(void))0 -# endif -#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 // -///////////// - -// Threads - -typedef struct UltraThreadContext UltraThreadContext; - -typedef enum { - STOPPED, - QUEUED, - RUNNING, - BLOCKED -} OSThreadState; - -typedef struct OSThread_t { - PTR(struct OSThread_t) next; // Next thread in the given queue - OSPri priority; - PTR(PTR(struct OSThread_t)) queue; // Queue this thread is in, if any - 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; - -// RSP - -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_s; - -typedef union { - OSTask_s t; - int64_t force_structure_alignment; -} OSTask; - -// PI - -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 */ -}; - -typedef struct { - u32 ctrl; - u32 width; - u32 burst; - u32 vSync; - u32 hSync; - u32 leap; - u32 hStart; - u32 xScale; - u32 vCurrent; -} OSViCommonRegs; - -typedef struct { - u32 origin; - u32 yScale; - u32 vStart; - u32 vBurst; - u32 vIntr; -} OSViFieldRegs; - -typedef struct { - u8 padding[3]; - u8 type; - OSViCommonRegs comRegs; - OSViFieldRegs fldRegs[2]; -} OSViMode; - -/////////////// -// 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 osYieldThread(RDRAM_ARG1); -void osSetThreadPri(RDRAM_ARG PTR(OSThread) t, OSPri pri); -OSPri osGetThreadPri(RDRAM_ARG PTR(OSThread) thread); -OSId osGetThreadId(RDRAM_ARG PTR(OSThread) t); - -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); -void osViSetMode(RDRAM_ARG PTR(OSViMode)); -void osViSetSpecialFeatures(uint32_t func); -void osViBlack(uint8_t active); -void osViSetXScale(float scale); -void osViSetYScale(float scale); -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/ultramodern/ultrainit.cpp b/ultramodern/ultrainit.cpp deleted file mode 100644 index 9517113..0000000 --- a/ultramodern/ultrainit.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "ultra64.h" -#include "ultramodern.hpp" - -void ultramodern::preinit(RDRAM_ARG ultramodern::WindowHandle window_handle) { - ultramodern::set_main_thread(); - ultramodern::init_events(PASS_RDRAM window_handle); - ultramodern::init_timers(PASS_RDRAM1); - ultramodern::init_audio(); - ultramodern::init_saving(PASS_RDRAM1); - ultramodern::init_thread_cleanup(); -} - -extern "C" void osInitialize() { -} diff --git a/ultramodern/ultramodern.hpp b/ultramodern/ultramodern.hpp deleted file mode 100644 index d0cd944..0000000 --- a/ultramodern/ultramodern.hpp +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef __ultramodern_HPP__ -#define __ultramodern_HPP__ - -#include -#include -#include -#include - -#undef MOODYCAMEL_DELETE_FUNCTION -#define MOODYCAMEL_DELETE_FUNCTION = delete -#include "lightweightsemaphore.h" -#include "ultra64.h" - -#if defined(_WIN32) -# define WIN32_LEAN_AND_MEAN -# include -#elif defined(__ANDROID__) -# include "android/native_window.h" -#elif defined(__linux__) -# include "X11/Xlib.h" -# undef None -# undef Status -# undef LockMask -# undef Always -# undef Success -#endif - -struct UltraThreadContext { - std::thread host_thread; - moodycamel::LightweightSemaphore running; - moodycamel::LightweightSemaphore initialized; -}; - -namespace ultramodern { - -#if defined(_WIN32) - // Native HWND handle to the target window. - struct WindowHandle { - HWND window; - DWORD thread_id = (DWORD)-1; - auto operator<=>(const WindowHandle&) const = default; - }; -#elif defined(__ANDROID__) - using WindowHandle = ANativeWindow*; -#elif defined(__linux__) - struct WindowHandle { - Display* display; - Window window; - auto operator<=>(const WindowHandle&) const = default; - }; -#endif - -// We need a place in rdram to hold the PI handles, so pick an address in extended rdram -constexpr uint32_t rdram_size = 1024 * 1024 * 16; // 16MB to give extra room for anything custom -constexpr int32_t cart_handle = 0x80800000; -constexpr int32_t drive_handle = (int32_t)(cart_handle + sizeof(OSPiHandle)); -constexpr int32_t flash_handle = (int32_t)(drive_handle + sizeof(OSPiHandle)); -constexpr uint32_t save_size = 1024 * 1024 / 8; // Maximum save size, 1Mbit for flash - -// Initialization. -void preinit(RDRAM_ARG WindowHandle window_handle); -void init_saving(RDRAM_ARG1); -void init_events(RDRAM_ARG WindowHandle window_handle); -void init_timers(RDRAM_ARG1); -void init_thread_cleanup(); - -// Thread queues. -constexpr PTR(PTR(OSThread)) running_queue = (PTR(PTR(OSThread)))-1; - -void thread_queue_insert(RDRAM_ARG PTR(PTR(OSThread)) queue, PTR(OSThread) toadd); -PTR(OSThread) thread_queue_pop(RDRAM_ARG PTR(PTR(OSThread)) queue); -bool thread_queue_remove(RDRAM_ARG PTR(PTR(OSThread)) queue_, PTR(OSThread) t_); -bool thread_queue_empty(RDRAM_ARG PTR(PTR(OSThread)) queue); -PTR(OSThread) thread_queue_peek(RDRAM_ARG PTR(PTR(OSThread)) queue); - -// Message queues. -void wait_for_external_message(RDRAM_ARG1); - -// Thread scheduling. -void check_running_queue(RDRAM_ARG1); -void run_next_thread_and_wait(RDRAM_ARG1); -void resume_thread_and_wait(RDRAM_ARG OSThread* t); -void schedule_running_thread(RDRAM_ARG PTR(OSThread) t); -void cleanup_thread(UltraThreadContext* thread_context); -uint32_t permanent_thread_count(); -uint32_t temporary_thread_count(); -struct thread_terminated : std::exception {}; - -enum class ThreadPriority { - Low, - Normal, - High, - VeryHigh, - Critical -}; - -void set_native_thread_name(const std::string& name); -void set_native_thread_priority(ThreadPriority pri); -PTR(OSThread) this_thread(); -void set_main_thread(); -bool is_game_thread(); -void submit_rsp_task(RDRAM_ARG PTR(OSTask) task); -void send_si_message(RDRAM_ARG1); -uint32_t get_speed_multiplier(); - -// Time -std::chrono::high_resolution_clock::time_point get_start(); -std::chrono::high_resolution_clock::duration time_since_start(); -void measure_input_latency(); -void sleep_milliseconds(uint32_t millis); -void sleep_until(const std::chrono::high_resolution_clock::time_point& time_point); - -// Graphics -uint32_t get_target_framerate(uint32_t original); -uint32_t get_display_refresh_rate(); -float get_resolution_scale(); -void load_shader_cache(std::span cache_data); - -// Audio -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(); - -struct audio_callbacks_t { - using queue_samples_t = void(int16_t*, size_t); - using get_samples_remaining_t = size_t(); - using set_frequency_t = void(uint32_t); - queue_samples_t* queue_samples; - get_samples_remaining_t* get_frames_remaining; - set_frequency_t* set_frequency; -}; - -// Input -struct input_callbacks_t { - using poll_input_t = void(void); - using get_input_t = void(uint16_t*, float*, float*); - using set_rumble_t = void(bool); - poll_input_t* poll_input; - get_input_t* get_input; - set_rumble_t* set_rumble; -}; - -struct gfx_callbacks_t { - using gfx_data_t = void*; - using create_gfx_t = gfx_data_t(); - using create_window_t = WindowHandle(gfx_data_t); - using update_gfx_t = void(gfx_data_t); - create_gfx_t* create_gfx; - create_window_t* create_window; - update_gfx_t* update_gfx; -}; -bool is_game_started(); -void quit(); -void join_event_threads(); -void join_thread_cleaner_thread(); -void join_saving_thread(); - -} // namespace ultramodern - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -#define debug_printf(...) -//#define debug_printf(...) printf(__VA_ARGS__); - -#endif