// The Digital Grove Codebase // Copyright (c) Ryan Fleury. All rights reserved. // This code was adapted from the excellent work by the authors of the // libdragon project (https://libdragon.dev). libdragon is licensed under // The Unlicense: // // This is free and unencumbered software released into the public domain. // // Anyone is free to copy, modify, publish, use, compile, sell, or distribute // this software, either in source code form or as a compiled binary, for any // purpose, commercial or non-commercial, and by any means. // // In jurisdictions that recognize copyright laws, the author or authors of // this software dedicate any and all copyright interest in the software to the // public domain. We make this dedication for the benefit of the public at // large and to the detriment of our heirs and successors. We intend this // dedication to be an overt act of relinquishment in perpetuity of all present // and future rights to this software under copyright law. // // 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 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. // // For more information, please refer to http://unlicense.org //////////////////////////////// //~ rjf: Globals #define N64_VI_V_TOTAL_SET(vsync) ((vsync)-1) // (?) set the total number of visible and non-visible half-lines. #define N64_VI_H_TOTAL_SET(leap_pattern, hsync) ((((leap_pattern) & 0x1F) << 16) | ((int)((hsync)*4-1) & 0xFFF)) // (?) set the total width of a line in quarter-pixel units (-1), and the 5-bit leap pattern. #define N64_VI_H_TOTAL_LEAP_SET(leap_a, leap_b) ((((int)((leap_a)*4-1) & 0xFFF) << 16) | ((int)((leap_b)*4-1) & 0xFFF)) // (?) set alternate scanline lengths for one scanline during vsync, leap_a and leap_b are selected based on the leap pattern in h_sync. #define N64_VI_V_BURST_SET(start, end) ((((start) & 0x3FF) << 16) | ((end) & 0x3FF)) // (?) set the start and end of color burst enable, in half-lines #define N64_VI_BURST_START(value) ((value & 0x3FF) << 20) // (?) set start of color burst in pixels from hsync. #define N64_VI_VSYNC_WIDTH(value) ((value & 0xF) << 16) // (?) set vertical sync width in half lines. #define N64_VI_BURST_WIDTH(value) ((value & 0xFF) << 8) // (?) set color burst width in pixels. #define N64_VI_HSYNC_WIDTH(value) ((value & 0xFF) << 0) // (?) set horizontal sync width in pixels. #define N64_VI_BURST_SET(burst_start, vsync_width, burst_width, hsync_width) \ N64_VI_BURST_START(burst_start) | N64_VI_VSYNC_WIDTH(vsync_width) | \ N64_VI_BURST_WIDTH(burst_width) | N64_VI_HSYNC_WIDTH(hsync_width) // (?) set all burst values global N64_TimingPreset n64_timing_preset_from_tv_kind_table[N64_TVKind_COUNT] = { //- rjf: PAL timing presets { .h_total = N64_VI_H_TOTAL_SET(0b10101, 794.5), .h_total_leap = N64_VI_H_TOTAL_LEAP_SET(796.0, 795.75), .v_total = N64_VI_V_TOTAL_SET(626), .burst = N64_VI_BURST_SET(64, 4, 35, 58), .v_burst = N64_VI_V_BURST_SET(9, 619), .display_x0 = 128, .display_y0 = 45, .display_width = 640, .display_height = 576, }, //- rjf: NTSC timing presets { .h_total = N64_VI_H_TOTAL_SET(0b00000, 773.5), .h_total_leap = N64_VI_H_TOTAL_LEAP_SET(773.5, 773.5), .v_total = N64_VI_V_TOTAL_SET(526), .burst = N64_VI_BURST_SET(62, 5, 34, 57), .v_burst = N64_VI_V_BURST_SET(14, 516), .display_x0 = 108, .display_y0 = 35, .display_width = 640, .display_height = 480, }, //- rjf: MPAL timing presets { .h_total = N64_VI_H_TOTAL_SET(0b00000, 772.75), .h_total_leap = N64_VI_H_TOTAL_LEAP_SET(774.50, 774.50), ///< (774.00, 774.00) is slightly more stable for interlaced over composite .v_total = N64_VI_V_TOTAL_SET(526), .burst = N64_VI_BURST_SET(70, 5, 30, 57), .v_burst = N64_VI_V_BURST_SET(14, 516), .display_x0 = 108, .display_y0 = 35, .display_width = 640, .display_height = 480, }, }; //////////////////////////////// //~ rjf: Low-Level Post-IPL3 Entry Point __attribute__((naked, section(".text.boot"))) __attribute__((optnone)) void _start(void) { __asm__ __volatile__ ( ".set noreorder\n" ".set noat\n" // rjf: initialize small data pointer "la $gp, _gp\n" // rjf: from libdragon: // // a bit from libgloss so we can start at a known state // // SR_CU1: 0x20000000 - Mark CP1 as usable // SR_FR: 0x04000000 - Enable MIPS III FP registers // SR_PE: 0x00100000 - Mark soft reset (clear parity error) // SR_KX: 0x00000080 - Kernel extended addressing enabled // SR_SX: 0x00000040 - Supervisor extended addressing enabled // SR_UX: 0x00000020 - User extended addressing enabled // "li $v0, 0x20000000|0x04000000|0x00100000|0x00000080|0x00000040|0x00000020\n" "mtc0 $v0, $12\n" // C0_SR: $12 - Status register // rjf: enable interrupts // // writing to COP0 status register ($12): // - 0x1 is the global interrupt enabling bit // - 0x400 is the bit used to enable interrupts from the RCP // // (see cop0.h in libdragon for all the definitions) // "mfc0 $v0, $12\n" "ori $v0, 0x1|0x400\n" "mtc0 $v0, $12\n" // rjf: enable flush-to-zero mode on the FPU "cfc1 $v0, $31\n" "lui $v1, 0x0100\n" "or $v0, $v0, $v1\n" "ctc1 $v0, $31\n" // rjf: call C entry point "li $a0, 0\n" "jal N64_EntryPoint\n" "li $a1, 0\n" // rjf: block forever if n64_main returns "3:\n" "b 3b\n" "nop\n" ); } //////////////////////////////// //~ rjf: Low-Level Interrupt Handler __attribute__((naked, section(".intvectors"))) void _interrupt_handler(void) { __asm__ __volatile__ ( ".section .intvectors, \"ax\"\n" //- rjf: jump to general interrupt handler path in all cases // // 0: TLB miss vector // 0x80: XTLB miss vector // 0x100: cache error vector // 0x180: general exception vector // ".set reorder\n" ".org 0\n" "j inthandler\n" ".org 0x80\n" "j inthandler\n" ".org 0x100\n" "j inthandler\n" ".org 0x180\n" "j inthandler\n" //- rjf: interrupt handler code ".section .text\n" ".p2align 5\n" "inthandler:\n" ".set noreorder\n" ".set noat\n" //- rjf: reserve space for pre-interrupt register states // // from libdragon: // // we need 544 + 32 bytes for this - 544 for storing all GPRs and FPRs, but // also 32 extra bytes, required by MIPS ABI to call C functions (this is // called "argument slots", required even if the C API does not have any // parameters, as callees may use that space to store locals). // // stack layout for register states: // - [0, 32): keep empty for calling C // - [32, 576): GPRs, then FPRs, then special registers // "subu $sp, 576\n" //- rjf: save caller-saved GPRs; others are callee-saved, meaning called // functions are responsible for cleaning them up. "sd $1, (32 + 1*8)($sp)\n" "sd $2, (32 + 2*8)($sp)\n" "sd $3, (32 + 3*8)($sp)\n" "sd $4, (32 + 4*8)($sp)\n" "sd $5, (32 + 5*8)($sp)\n" "sd $6, (32 + 6*8)($sp)\n" "sd $7, (32 + 7*8)($sp)\n" "sd $8, (32 + 8*8)($sp)\n" "sd $9, (32 + 9*8)($sp)\n" "sd $10, (32 + 10*8)($sp)\n" "sd $11, (32 + 11*8)($sp)\n" "sd $12, (32 + 12*8)($sp)\n" "sd $13, (32 + 13*8)($sp)\n" "sd $14, (32 + 14*8)($sp)\n" "sd $15, (32 + 15*8)($sp)\n" "sd $24, (32 + 24*8)($sp)\n" "sd $25, (32 + 25*8)($sp)\n" "addiu $t0, $sp, 576\n" "sd $t0, (32 + 29*8)($sp)\n" "sd $31, (32 + 31*8)($sp)\n" //- rjf: save LO/HI "mflo $t0\n" "mfhi $t1\n" "sd $t0, (32 + 32*8)($sp)\n" "sd $t1, (32 + 33*8)($sp)\n" //- rjf: save C0 special registers (SR/CR/EPC) "mfc0 $t0, $12\n" // C0 $12 -> SR "mfc0 $t8, $13\n" // C0 $13 -> cause info "mfc0 $t1, $14\n" // C0 $14 -> EPC "sw $t0, (32 + 34*8 + 0)($sp)\n" "sw $t8, (32 + 34*8 + 4)($sp)\n" "sw $t1, (32 + 34*8 + 8)($sp)\n" //- rjf: save FPU regs "cfc1 $1, $31\n" "sw $1, (32 + 34*8 + 12)($sp)\n" ".set at\n" "sdc1 $f0, (32 + 34*8 + 16 + 0*8)($sp)\n" "sdc1 $f1, (32 + 34*8 + 16 + 1*8)($sp)\n" "sdc1 $f2, (32 + 34*8 + 16 + 2*8)($sp)\n" "sdc1 $f3, (32 + 34*8 + 16 + 3*8)($sp)\n" "sdc1 $f4, (32 + 34*8 + 16 + 4*8)($sp)\n" "sdc1 $f5, (32 + 34*8 + 16 + 5*8)($sp)\n" "sdc1 $f6, (32 + 34*8 + 16 + 6*8)($sp)\n" "sdc1 $f7, (32 + 34*8 + 16 + 7*8)($sp)\n" "sdc1 $f8, (32 + 34*8 + 16 + 8*8)($sp)\n" "sdc1 $f9, (32 + 34*8 + 16 + 9*8)($sp)\n" "sdc1 $f10,(32 + 34*8 + 16 + 10*8)($sp)\n" "sdc1 $f11,(32 + 34*8 + 16 + 11*8)($sp)\n" "sdc1 $f12,(32 + 34*8 + 16 + 12*8)($sp)\n" "sdc1 $f13,(32 + 34*8 + 16 + 13*8)($sp)\n" "sdc1 $f14,(32 + 34*8 + 16 + 14*8)($sp)\n" "sdc1 $f15,(32 + 34*8 + 16 + 15*8)($sp)\n" "sdc1 $f16,(32 + 34*8 + 16 + 16*8)($sp)\n" "sdc1 $f17,(32 + 34*8 + 16 + 17*8)($sp)\n" "sdc1 $f18,(32 + 34*8 + 16 + 18*8)($sp)\n" "sdc1 $f19,(32 + 34*8 + 16 + 19*8)($sp)\n" // rjf: adjust C0 SR ($12) to reset into known state // // SR_CU1: 0x20000000 - Mark CP1 as usable // SR_FR: 0x04000000 - Enable MIPS III FP registers // SR_PE: 0x00100000 - Mark soft reset (clear parity error) // SR_KX: 0x00000080 - Kernel extended addressing enabled // SR_SX: 0x00000040 - Supervisor extended addressing enabled // SR_UX: 0x00000020 - User extended addressing enabled // // keep interrupts off temporarily // "li $v0, 0x20000000|0x04000000|0x00100000|0x00000080|0x00000040|0x00000020\n" "mtc0 $v0, $12\n" //- rjf: call interrupt handler "mfc0 $t0, $13\n" // C0 $13 -> (cause register) "andi $t1, $t0, 0x7c\n" // 0x7c -> mask exception code off cause register "srl $t1, 2\n" // shift off bottom 2 bits - $t1 now holds exception code "addiu $a0, $sp, 32\n" // $a0 gets the pointer to the register state ($sp + 32) "move $a1, $t1\n" // $a1 gets the exception code parameter "jal N64_InterruptHandler\n" "nop\n" // rjf: end interrupt: restore FP regs ".set noat\n" "lw $1, (32 + 34*8 + 12)($sp)\n" "ctc1 $1, $31\n" ".set at\n" "ldc1 $f0, (32 + 34*8 + 16 + 0*8)($sp)\n" "ldc1 $f1, (32 + 34*8 + 16 + 1*8)($sp)\n" "ldc1 $f2, (32 + 34*8 + 16 + 2*8)($sp)\n" "ldc1 $f3, (32 + 34*8 + 16 + 3*8)($sp)\n" "ldc1 $f4, (32 + 34*8 + 16 + 4*8)($sp)\n" "ldc1 $f5, (32 + 34*8 + 16 + 5*8)($sp)\n" "ldc1 $f6, (32 + 34*8 + 16 + 6*8)($sp)\n" "ldc1 $f7, (32 + 34*8 + 16 + 7*8)($sp)\n" "ldc1 $f8, (32 + 34*8 + 16 + 8*8)($sp)\n" "ldc1 $f9, (32 + 34*8 + 16 + 9*8)($sp)\n" "ldc1 $f10,(32 + 34*8 + 16 + 10*8)($sp)\n" "ldc1 $f11,(32 + 34*8 + 16 + 11*8)($sp)\n" "ldc1 $f12,(32 + 34*8 + 16 + 12*8)($sp)\n" "ldc1 $f13,(32 + 34*8 + 16 + 13*8)($sp)\n" "ldc1 $f14,(32 + 34*8 + 16 + 14*8)($sp)\n" "ldc1 $f15,(32 + 34*8 + 16 + 15*8)($sp)\n" "ldc1 $f16,(32 + 34*8 + 16 + 16*8)($sp)\n" "ldc1 $f17,(32 + 34*8 + 16 + 17*8)($sp)\n" "ldc1 $f18,(32 + 34*8 + 16 + 18*8)($sp)\n" "ldc1 $f19,(32 + 34*8 + 16 + 19*8)($sp)\n" // rjf: end interrupt: restore C0 special registers (SR/EPC - can skip CR) "lw $t0, (32 + 34*8 + 0)($sp)\n" "lw $t1, (32 + 34*8 + 8)($sp)\n" "mtc0 $t0, $12\n" "mtc0 $t1, $14\n" // rjf: end interrupt: restore LO, HI "ld $t0, (32 + 32*8)($sp)\n" "ld $t1, (32 + 33*8)($sp)\n" "mtlo $t0\n" "mthi $t1\n" // rjf: end interrupt: restore GPRs "ld $1, (32 + 1*8)($sp)\n" "ld $2, (32 + 2*8)($sp)\n" "ld $3, (32 + 3*8)($sp)\n" "ld $4, (32 + 4*8)($sp)\n" "ld $5, (32 + 5*8)($sp)\n" "ld $6, (32 + 6*8)($sp)\n" "ld $7, (32 + 7*8)($sp)\n" "ld $8, (32 + 8*8)($sp)\n" "ld $9, (32 + 9*8)($sp)\n" "ld $10, (32 + 10*8)($sp)\n" "ld $11, (32 + 11*8)($sp)\n" "ld $12, (32 + 12*8)($sp)\n" "ld $13, (32 + 13*8)($sp)\n" "ld $14, (32 + 14*8)($sp)\n" "ld $15, (32 + 15*8)($sp)\n" "ld $24, (32 + 24*8)($sp)\n" "ld $25, (32 + 25*8)($sp)\n" "ld $31, (32 + 31*8)($sp)\n" // rjf: pop stack scratch space "addiu $sp, 576\n" // rjf: return from exception "eret\n" ); } //////////////////////////////// //~ rjf: Detected Console Kind function N64_ConsoleKind N64_GetConsoleKind(void) { return n64_state.console_kind; } //////////////////////////////// //~ rjf: JoyBus Specs function N64_JoyBus_CmdIDInfo N64_JoyBus_InfoFromCmdID(N64_JoyBus_CmdID id) { N64_JoyBus_CmdIDInfo result = {0}; switch(id) { default:{}break; #define Map(name) case N64_JoyBus_CmdID_##name:{result.send_size = sizeof(((N64_JoyBus_CmdPayload_##name *)0)->send); result.recv_size = sizeof(((N64_JoyBus_CmdPayload_##name *)0)->recv);}break Map(Reset); Map(Identify); Map(N64ControllerRead); #undef Map } return result; } //////////////////////////////// //~ rjf: Cache Operations function void N64_DataCacheHitWriteback(volatile void *addr, UAddr length) { N64_CacheOp(N64_CacheOpKind_HitWriteback, N64_CacheKind_Data); } function void N64_DataCacheHitInvalidate(volatile void *addr, UAddr length) { // addr must be 16-byte aligned; length must be multiple of 16: // // (((U32)addr % 16) == 0 && (length % 16) == 0) // N64_CacheOp(N64_CacheOpKind_HitInvalidate, N64_CacheKind_Data); } function void N64_DataCacheHitWritebackInvalidate(volatile void *addr, UAddr length) { N64_CacheOp(N64_CacheOpKind_HitWritebackInvalidate, N64_CacheKind_Data); } function void N64_DataCacheIndexWritebackInvalidate(volatile void *addr, UAddr length) { N64_CacheOp(N64_CacheOpKind_IndexWritebackInvalidate, N64_CacheKind_Data); } function void N64_InstCacheHitWriteback(volatile void *addr, UAddr length) { N64_CacheOp(N64_CacheOpKind_HitWriteback, N64_CacheKind_Inst); } function void N64_InstCacheHitInvalidate(volatile void *addr, UAddr length) { N64_CacheOp(N64_CacheOpKind_HitInvalidate, N64_CacheKind_Inst); } function void N64_InstCacheFill(volatile void *addr, UAddr length) { N64_CacheOp(N64_CacheOpKind_Fill, N64_CacheKind_Inst); } function void N64_InstCacheIndexInvalidate(volatile void *addr, UAddr length) { N64_CacheOp(N64_CacheOpKind_IndexInvalidate, N64_CacheKind_Inst); } //////////////////////////////// //~ rjf: JoyBus Command Processing function B32 N64_JoyBusCmdPush(N64_JoyBus_CmdID id) { B32 result = 0; if((n64_state.joybus_cmds_write_pos - n64_state.joybus_cmds_read_pos) < ArrayCount(n64_state.joybus_cmds)) { result = 1; //- rjf: take next command slot U32 next_cmd_idx = n64_state.joybus_cmds_write_pos%ArrayCount(n64_state.joybus_cmds); N64_JoyBus_Cmd *cmd = &n64_state.joybus_cmds[next_cmd_idx]; //- rjf: zero payloads, set ID MemoryZero(cmd->send_payload, sizeof(cmd->send_payload)); MemoryZero(cmd->recv_payload, sizeof(cmd->recv_payload)); cmd->cmd_id = id; //- rjf: get command ID's metadata info N64_JoyBus_CmdIDInfo id_info = N64_JoyBus_InfoFromCmdID(id); //- rjf: serialize send payload - pack data per port U64 write_off = 0; for(U32 port_idx = 0; port_idx < N64_JOYBUS_PORT_COUNT; port_idx += 1) { //- rjf: IQue -> prefix all commands w/ 0xFF if(N64_GetConsoleKind() == N64_ConsoleKind_IQue) { cmd->send_payload[write_off] = 0xFF; write_off += 1; } //- rjf: push command header cmd->send_payload[write_off+0] = id_info.send_size; cmd->send_payload[write_off+1] = id_info.recv_size; write_off += 2; //- rjf: push this port's part of the payload, based on command ID switch(id) { default:{}break; //- rjf: identify commands case N64_JoyBus_CmdID_Reset: case N64_JoyBus_CmdID_Identify: { N64_JoyBus_CmdPayload_Identify payload = { .send.cmd_id = cmd->cmd_id, }; MemoryCopy(&cmd->send_payload[write_off], &payload.send, sizeof(payload.send)); }break; //- rjf: n64 controller reads case N64_JoyBus_CmdID_N64ControllerRead: { N64_JoyBus_CmdPayload_N64ControllerRead payload = { .send.cmd_id = cmd->cmd_id, }; MemoryCopy(&cmd->send_payload[write_off], &payload.send, sizeof(payload.send)); }break; } write_off += id_info.send_size + id_info.recv_size; //- rjf: IQue -> pad to 8 byte alignment if(N64_GetConsoleKind() == N64_ConsoleKind_IQue) { for(;(write_off&7) != 0;) { cmd->send_payload[write_off] = 0xFF; write_off += 1; } } } //- rjf: fill ending markers in payload cmd->send_payload[write_off] = 0xFE; cmd->send_payload[sizeof(cmd->send_payload)-1] = 0x1; //- rjf: send immediately if possible, to start chain of interrupts B32 can_send_immediately = (n64_state.joybus_cmds_read_pos == n64_state.joybus_cmds_write_pos); n64_state.joybus_cmds_write_pos += 1; if(can_send_immediately) { n64_state.joybus_status = N64_JoyBus_Status_Sending; N64_JoyBusCmdSend(cmd); } } return result; } function void N64_JoyBusCmdSend(N64_JoyBus_Cmd *cmd) { n64_state.joybus_status = N64_JoyBus_Status_Sending; for(;N64_SI_REGS->status & (N64_SI_STATUS_DMA_BUSY|N64_SI_STATUS_IO_BUSY);); N64_DataCacheHitWriteback(cmd->send_payload, sizeof(cmd->send_payload)); N64_SI_REGS->dram_addr = (U32)(&cmd->send_payload[0]); N64_MemoryBarrier(); N64_SI_REGS->pif_addr_write = (U32)N64_PIF_RAM; N64_MemoryBarrier(); } //////////////////////////////// //~ rjf: Rendering Helpers function U16 * N64_Framebuffer(void) { return n64_fb; } function void N64_DrawMonochromeBitmap(Vec2F32 pos, Vec4F32 color, U8 *ptr, U32 width, U32 height) { U16 color_5551 = RGBA5551From4F32(color); S32 draw_x = (S32)pos.x; S32 draw_y = (S32)pos.y; U16 *p = N64_Framebuffer(); for(S32 bmp_y = 0; bmp_y < height; bmp_y += 1) { for(S32 bmp_x = 0; bmp_x < width; bmp_x += 1) { U8 bmp_v = ptr[bmp_y*width + bmp_x]; if(bmp_v) { U64 fb_off = (draw_y + bmp_y)*N64_FB_WIDTH + draw_x + bmp_x; p[fb_off] = color_5551; } } } } function Vec2F32 N64_DrawDebugString(B32 big, Vec2F32 pos, Vec4F32 color, String8 string) { Vec2F32 advance = {0}; for(U64 off = 0; off < string.size; off += 1) { U8 byte = string.str[off]; if(big) { N64_BigDefaultFontGlyph *glyph = &n64_big_default_font_glyphs[byte]; N64_DrawMonochromeBitmap(pos, color, glyph->v, N64_BIG_DEFAULT_FONT_GLYPH_WIDTH, N64_BIG_DEFAULT_FONT_GLYPH_HEIGHT); pos.x += N64_BIG_DEFAULT_FONT_GLYPH_ADVANCE; advance.x += N64_BIG_DEFAULT_FONT_GLYPH_ADVANCE; } else { N64_SmallDefaultFontGlyph *glyph = &n64_small_default_font_glyphs[byte]; N64_DrawMonochromeBitmap(pos, color, glyph->v, N64_SMALL_DEFAULT_FONT_GLYPH_WIDTH, N64_SMALL_DEFAULT_FONT_GLYPH_HEIGHT); pos.x += N64_SMALL_DEFAULT_FONT_GLYPH_ADVANCE; advance.x += N64_SMALL_DEFAULT_FONT_GLYPH_ADVANCE; } } advance.y += big ? N64_BIG_DEFAULT_FONT_GLYPH_HEIGHT : N64_SMALL_DEFAULT_FONT_GLYPH_HEIGHT; return advance; } //////////////////////////////// //~ rjf: High-Level Interrupt Handler void N64_InterruptHandler(N64_ExceptionRegState *regs, N64_ExceptionCode exception_code) { switch(exception_code) { ////////////////////////////// //- rjf: default case -> unrecoverable exception, display info // default: { Temp scratch = ScratchBegin(0); UAddr epc = regs->epc + (regs->cr&0x80000000 ? 4 : 0); // if 0x80000000 is set, -> advance to next instruction UAddr bad_vaddr = (UAddr)regs->gpr[8]; // $8 gets the bad vaddr of the exception //- rjf: get exception explanation String8 explanation = S("Unrecognized exception"); if(exception_code < ArrayCount(n64_string_from_exception_code_table)) { explanation = n64_string_from_exception_code_table[exception_code]; switch(exception_code) { default:{}break; //- rjf: floating-point exceptions case N64_ExceptionCode_FloatingPoint: { // NOTE(rjf): floating-point exception cracking adapted from libdragon U32 opcode = *(U32 *)epc; //- rjf: standard FPU opcode (not MTC/DMTC) B32 specialized = 0; if((opcode>>26) == 0x11 && (opcode&0x3F) != 0) { U32 fs = (opcode>>11) & 0x1F; U32 ft = (opcode>>16) & 0x1F; switch((opcode>>21) & 0x1F) { //- rjf: single-precision case 0x10: { U32 reg_fs = regs->fpr[fs]; U32 reg_ft = regs->fpr[ft]; // rjf: check for uninitialized values as initialized by -ftrivial-auto-var-init=pattern if((reg_fs & 0xFFFF0000) == 0xFEFE0000 || (reg_ft & 0xFFFF0000) == 0xFEFE0000) { specialized = 1; explanation = S("Uninitialized floating point variable"); } // rjf: check for denormals else if(((reg_fs & 0x7F800000) == 0 && (reg_fs & 0x007FFFFF) != 0) || ((reg_ft & 0x7F800000) == 0 && (reg_ft & 0x007FFFFF) != 0)) { specialized = 1; explanation = S("Invalid floating point value (denormal)"); } // rjf: check for negative square roots else if((opcode & 0x3F) == 0x04 && (S32)reg_fs < 0) { specialized = 1; explanation = S("Floating point negative square root"); } }break; //- rjf: double-precision case 0x11: { U64 reg_fs64 = regs->fpr[fs]; U64 reg_ft64 = regs->fpr[ft]; // rjf: check for uninitialized values as initialized by -ftrivial-auto-var-init=pattern if((reg_fs64 & 0xFFFFFFFF00000000ull) == 0xFEFEFEFE00000000ull || (reg_ft64 & 0xFFFFFFFF00000000ull) == 0xFEFEFEFE00000000ull) { specialized = 1; explanation = S("Uninitialized floating point variable"); } // rjf: check for denormals else if(((reg_fs64 & 0x7FF0000000000000ull) == 0 && (reg_fs64 & 0x000FFFFFFFFFFFFFull) != 0) || ((reg_ft64 & 0x7FF0000000000000ull) == 0 && (reg_ft64 & 0x000FFFFFFFFFFFFFull) != 0)) { specialized = 1; explanation = S("Invalid floating point value (denormal)"); } // rjf: check for negative square roots else if((opcode & 0x3F) == 0x04 && (S64)reg_fs64 < 0) { specialized = 1; explanation = S("Floating point negative square root"); } }break; } } // rjf: check general cases if(!specialized) { if(regs->fc31 & 0x00008000) { explanation = S("Floating point divide by zero"); } else if(regs->fc31 & 0x00010000) { explanation = S("Floating point invalid operation"); } else if(regs->fc31 & 0x00004000) { explanation = S("Floating point overflow"); } else if(regs->fc31 & 0x00002000) { explanation = S("Floating point underflow"); } else if(regs->fc31 & 0x00001000) { explanation = S("Floating point inexact operation"); } } }break; } } //- rjf: blue screen & render exception info { n64_fb = (U16 *)0xA0100000u; N64_VI_REGS->origin = 0x00100000u; for(int pixel_idx = 0; pixel_idx < N64_FB_WIDTH*N64_FB_HEIGHT; pixel_idx += 1) { n64_fb[pixel_idx] = 0x003F; } N64_DrawDebugString(1, V2F32(16, 16 + 0*11), V4F32(1, 0, 0, 1), S("Critical exception occurred")); N64_DrawDebugString(0, V2F32(16, 16 + 14 + 0*11), V4F32(1, 1, 1, 1), SF("Code: %i32x", exception_code)); N64_DrawDebugString(0, V2F32(16, 16 + 14 + 1*11), V4F32(1, 1, 1, 1), SF("EPC: %i32x", regs->epc)); N64_DrawDebugString(0, V2F32(16, 16 + 14 + 2*11), V4F32(1, 1, 1, 1), SF("SP: %p", (UAddr)(regs->sp))); N64_DrawDebugString(0, V2F32(16, 16 + 14 + 3*11), V4F32(1, 1, 1, 1), SF("Address: %p", bad_vaddr)); N64_DrawDebugString(0, V2F32(16, 16 + 14 + 4*11), V4F32(1, 1, 1, 1), S("Explanation:")); N64_DrawDebugString(0, V2F32(16, 16 + 14 + 5*11), V4F32(0.8f, 0.8f, 0.8f, 1), explanation); for(;;){} } ScratchEnd(scratch); }break; ////////////////////////////// //- rjf: interrupts // case N64_ExceptionCode_Interrupt: { U64 status = *N64_MI_INTERRUPT & *N64_MI_MASK; ///////////////////////////// //- rjf: [SI] serial interface interrupt // if(status & N64_MI_InterruptStatusMask_SI) { //- rjf: clear interrupt N64_SI_REGS->status = 0; //- rjf: update joybus state switch(n64_state.joybus_status) { //- rjf: [idle] nothing to do - this interrupt was not caused by our own joybus interactions. default: case N64_JoyBus_Status_Idle: {}break; //- rjf: [sending] send is complete - now initiate receive. case N64_JoyBus_Status_Sending: { // rjf: get current cmd index U32 cmd_idx = n64_state.joybus_cmds_read_pos%ArrayCount(n64_state.joybus_cmds); // rjf: initiate receive transfer for(;N64_SI_REGS->status & (N64_SI_STATUS_DMA_BUSY|N64_SI_STATUS_IO_BUSY);); N64_DataCacheHitInvalidate(n64_state.joybus_cmds[cmd_idx].recv_payload, sizeof(n64_state.joybus_cmds[cmd_idx].recv_payload)); N64_SI_REGS->dram_addr = (U32)(&n64_state.joybus_cmds[cmd_idx].recv_payload[0]); N64_MemoryBarrier(); N64_SI_REGS->pif_addr_read = (U32)N64_PIF_RAM; N64_MemoryBarrier(); // rjf: sending -> recving n64_state.joybus_status = N64_JoyBus_Status_Recving; }break; //- rjf: [receiving] receive is complete - push next command, or move back to idle. case N64_JoyBus_Status_Recving: { for(;N64_SI_REGS->status & (N64_SI_STATUS_DMA_BUSY|N64_SI_STATUS_IO_BUSY);); // rjf: get current cmd index U32 cmd_idx = n64_state.joybus_cmds_read_pos%ArrayCount(n64_state.joybus_cmds); N64_JoyBus_CmdID cmd_id = n64_state.joybus_cmds[cmd_idx].cmd_id; N64_JoyBus_CmdIDInfo cmd_id_info = N64_JoyBus_InfoFromCmdID(cmd_id); // rjf: unpack received payload U8 *data = n64_state.joybus_cmds[cmd_idx].recv_payload; UAddr read_off_opl = sizeof(n64_state.joybus_cmds[cmd_idx].recv_payload); UAddr read_off = 0; // rjf: parse payload for(U32 port_idx = 0; port_idx < N64_JOYBUS_PORT_COUNT; port_idx += 1) { // rjf: early-out if we're on iQue and we're on port 4 if(N64_GetConsoleKind() == N64_ConsoleKind_IQue && port_idx == 4) { break; } // rjf: from libdragon: // // iQue has a highly fixed layout for commands, and it tends to corrupt // other parts of PIF-RAM, so we'll want to just parse at fixed offsets // in those cases. // if(N64_GetConsoleKind() == N64_ConsoleKind_IQue) { read_off = (port_idx*8)+1; } // rjf: early-out if we don't have space for the next payload UAddr bytes_per_payload = 2 + cmd_id_info.send_size + cmd_id_info.recv_size; if(read_off + bytes_per_payload > read_off_opl) { break; } // rjf: parse next payload if the error bit (0x80) is not set in the recv size if(!(data[read_off+1] & 0x80)) { U8 *payload_raw = data + read_off + 2; switch(cmd_id) { default:{}break; //- rjf: reset/identify case N64_JoyBus_CmdID_Reset: case N64_JoyBus_CmdID_Identify: { N64_JoyBus_CmdPayload_Identify payload = {0}; MemoryCopy(&payload, payload_raw, sizeof(payload)); // rjf: get last ID on this port N64_JoyBus_ID last_id = n64_state.joybus_port_states[port_idx].id; // rjf: fill joybus port state n64_state.joybus_port_states[port_idx].id = payload.recv.id; n64_state.joybus_port_states[port_idx].status = payload.recv.status; // rjf: bump generation on changed ID if(last_id != payload.recv.id) { n64_state.joybus_port_states[port_idx].connect_gen += 1; } // rjf: reflect generation in controller state if(port_idx < ArrayCount(n64_state.game_pads)) { n64_state.game_pads[port_idx].connect_gen = n64_state.joybus_port_states[port_idx].connect_gen; } }break; //- rjf: n64 controller state reads case N64_JoyBus_CmdID_N64ControllerRead: if(port_idx < ArrayCount(n64_state.game_pads)) { N64_JoyBus_CmdPayload_N64ControllerRead payload = {0}; MemoryCopy(&payload, payload_raw, sizeof(payload)); // rjf: get associated controller state GamePad *pad = &n64_state.game_pads[port_idx]; // rjf: remember last button state MemoryCopy(pad->last_buttons, pad->buttons, sizeof(pad->buttons)); // rjf: fill new controller button states #define Map(recv_name, button_name) if(payload.recv.recv_name) { BitArrayAdd(pad->buttons, GamePadButtonKind_##button_name); } else { BitArrayRemove(pad->buttons, GamePadButtonKind_##button_name); } Map(a, A); Map(b, B); Map(z, Z); Map(start, Start); Map(d_up, DUp); Map(d_down, DDown); Map(d_left, DLeft); Map(d_right, DRight); Map(l, LeftBumper); Map(r, RightBumper); Map(c_up, CUp); Map(c_down, CDown); Map(c_left, CLeft); Map(c_right, CRight); #undef Map // rjf: fill controller stick state pad->sticks[0].x = ((F32)payload.recv.stick_x / 128.f); pad->sticks[0].y = ((F32)payload.recv.stick_y / 128.f); }break; } } read_off += bytes_per_payload; } // rjf: advance one cmd n64_state.joybus_cmds_read_pos += 1; // rjf: recving -> idle n64_state.joybus_status = N64_JoyBus_Status_Idle; // rjf: try to push next command, if there are any in the queue if(n64_state.joybus_cmds_write_pos - n64_state.joybus_cmds_read_pos > 0) { N64_JoyBus_Cmd *next_cmd = &n64_state.joybus_cmds[n64_state.joybus_cmds_read_pos%ArrayCount(n64_state.joybus_cmds)]; N64_JoyBusCmdSend(next_cmd); } }break; } //- rjf: [DEBUG] blue screen & render info #if 0 { n64_fb = (U16 *)0xA0100000u; N64_VI_REGS->origin = 0x00100000u; for(int pixel_idx = 0; pixel_idx < N64_FB_WIDTH*N64_FB_HEIGHT; pixel_idx += 1) { n64_fb[pixel_idx] = 0x003F; } N64_DrawDebugString(1, V2F32(16, 16 + 0*14), V4F32(1, 0, 0, 1), S("SI Interrupt")); for(;;){} } #endif } }break; } } //////////////////////////////// //~ rjf: High-Level N64 Program Entry Point void N64_EntryPoint(void) { //- rjf: determine console kind n64_state.console_kind = N64_ConsoleKind_N64; if(!!(N64_BOOT_INFO->flags & N64_BootInfoFlag_IsIQue)) { n64_state.console_kind = N64_ConsoleKind_IQue; } else if((*N64_MI_VERSION & 0xFF) == 0x3) { n64_state.console_kind = N64_ConsoleKind_Analogue3D; } //- rjf: set up main user memory arena Arena *user_arena = 0; { U32 memory_size = N64_BOOT_INFO->memory_size; U32 bss_data_end = (U32)__bss_end; U32 bss_data_end_aligned = ((bss_data_end + 63) & ~63); U32 rdram_end = 0x80000000 + memory_size; U32 stack_size = 64*1024; U32 user_memory_end = rdram_end - stack_size; U32 user_memory_size = (user_memory_end - bss_data_end_aligned); user_arena = ArenaMakeStatic((U8 *)bss_data_end_aligned, user_memory_size); } //- rjf: choose timing preset N64_TVKind tv_kind = (N64_TVKind)((N64_BOOT_INFO->flags & N64_BootInfoFlags_TVKindMask) >> N64_BootInfoFlags_TVKindShift); N64_TimingPreset *timing_preset = &n64_timing_preset_from_tv_kind_table[tv_kind]; //- rjf: allocate space for framebuffers // // NOTE(rjf): must be 8-byte aligned. virtual addresses should be in uncached KSEG1 memory. // U32 framebuffer_color_paddrs[2] = {0}; U32 framebuffer_depth_paddrs[2] = {0}; for(U32 idx = 0; idx < ArrayCount(framebuffer_color_paddrs); idx += 1) { void *color_memory = PushArrayNoZeroAligned(user_arena, U16, N64_FB_WIDTH*N64_FB_HEIGHT, 64); framebuffer_color_paddrs[idx] = (UAddr)color_memory - 0x80000000; } for(U32 idx = 0; idx < ArrayCount(framebuffer_depth_paddrs); idx += 1) { void *depth_memory = PushArrayNoZeroAligned(user_arena, U16, N64_FB_WIDTH*N64_FB_HEIGHT, 64); framebuffer_depth_paddrs[idx] = (UAddr)depth_memory - 0x80000000; } //- rjf: allocate space for scratch arenas UAddr scratch_arena_storage_size = 128*1024; U8 *scratch_arena_storage = PushArrayNoZeroAligned(user_arena, U8, scratch_arena_storage_size, 64); //- rjf: store global scratch arena pointers { UAddr size_per_scratch_arena = scratch_arena_storage_size/2; n64_tctx.scratch_arenas[0] = ArenaMakeStatic(scratch_arena_storage + 0, size_per_scratch_arena); n64_tctx.scratch_arenas[1] = ArenaMakeStatic(scratch_arena_storage + size_per_scratch_arena, size_per_scratch_arena); } //- rjf: set up VI registers { N64_VI_REGS->ctrl = N64_VI_CtrlRegVal(N64_VI_CtrlKind_16bpp, N64_VI_AAMode_ResampleFetchAlways, N64_VI_PixelAdvance_Default, N64_VI_CtrlFlag_GammaCorrect| N64_VI_CtrlFlag_GammaDitherEnable| N64_VI_CtrlFlag_Divot| N64_VI_CtrlFlag_Dedither); N64_VI_REGS->origin = framebuffer_color_paddrs[1]; N64_VI_REGS->width = N64_FB_WIDTH; N64_VI_REGS->v_intr = 0x00000002u; N64_VI_REGS->v_current = 0x00000000u; N64_VI_REGS->h_total = timing_preset->h_total; N64_VI_REGS->h_total_leap= timing_preset->h_total_leap; N64_VI_REGS->burst = timing_preset->burst; N64_VI_REGS->v_burst = timing_preset->v_burst; N64_VI_REGS->v_total &= 1; // NOTE(rjf): the bottom bit of v_total is used for toggling interlaced mode, so we just keep that bit in this case. N64_VI_REGS->v_total |= 0xFFFFFFFE & (timing_preset->v_total & ~1); N64_VI_REGS->h_video = ((timing_preset->display_x0 & 0x3ff) << 16) | (((timing_preset->display_x0 + timing_preset->display_width) & 0x3ff) << 0); N64_VI_REGS->v_video = ((timing_preset->display_y0 & 0x3ff) << 16) | (((timing_preset->display_y0 + timing_preset->display_height) & 0x3ff) << 0); N64_VI_REGS->x_scale = 0x200u; N64_VI_REGS->y_scale = 0x400u; N64_MemoryBarrier(); } //- rjf: set up SI interrupt handling { *N64_MI_MASK = N64_MI_MaskCmd_SetSI; N64_SI_REGS->status = 0; N64_MemoryBarrier(); } //- rjf: set up RDP { N64_DP_REGS->status_flags = (N64_DP_StatusCmd_ResetFlush| N64_DP_StatusCmd_ResetFreeze| N64_DP_StatusCmd_ResetXBusDMemDMA); N64_MemoryBarrier(); } //- rjf: do main loop for(U64 tick_idx = 0;; tick_idx += 1) { //- rjf: do detection of all JoyBus devices periodically if(tick_idx%10 == 0) { N64_JoyBusCmdPush(N64_JoyBus_CmdID_Identify); } //- rjf: do JoyBus input detection { N64_JoyBusCmdPush(N64_JoyBus_CmdID_N64ControllerRead); } //- rjf: prep current framebuffer pointer n64_fb = (U16 *)(framebuffer_color_paddrs[tick_idx&1] + 0xA0000000); //- rjf: set up empty RDP queue; wait for RDP to be ready U32 payload_start_paddr = (U32)(n64_r_state.rdp_command_buffer) - 0x80000000; #if 1 n64_r_state.rdp_command_buffer_write_pos = 0; N64_MemoryBarrier(); N64_DP_REGS->start = payload_start_paddr; N64_DP_REGS->end = payload_start_paddr; N64_MemoryBarrier(); for(;N64_DP_REGS->current != payload_start_paddr;){} //- rjf: clear depth buffer { // rjf: sync pipe { N64_DP_Cmd_Stub cmd = {0}; cmd.id = N64_DP_CmdID_SyncPipe; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set color image to depth buffer { N64_DP_Cmd_SetColorImage cmd = {0}; cmd.id = N64_DP_CmdID_SetColorImage; cmd.format = N64_DP_PixelFormat_RGBA; cmd.pixel_size_kind = N64_DP_PixelSizeKind_16; cmd.width = N64_FB_WIDTH-1; cmd.image_paddr = framebuffer_depth_paddrs[tick_idx&1]; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set other modes { N64_DP_Cmd_SetOtherModes cmd = {0}; cmd.id = N64_DP_CmdID_SetOtherModes; cmd.atomic_prim = 1; cmd.cycle_type = 3; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set fill color { N64_DP_Cmd_SetFillColor cmd = {0}; cmd.id = N64_DP_CmdID_SetFillColor; cmd.rgba32 = 0xfffcfffc; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: clear #if 0 { N64_DP_Cmd_FillRectangle cmd = {0}; cmd.lower_right_x = ((N64_FB_WIDTH-1)<<2); cmd.lower_right_y = ((N64_FB_HEIGHT-1)<<2); WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } #endif // rjf: clear #if 1 { N64_DP_Cmd_FillTriangleBase cmd = {0}; cmd.id = N64_DP_CmdID_FillTriangle; cmd.flags = N64_DP_Cmd_FillTriangleFlag_LeftMajor; cmd.yl = Fixed112FromF32(N64_FB_HEIGHT); cmd.ym = Fixed112FromF32(N64_FB_HEIGHT); cmd.yh = 0; cmd.xl = Fixed1616FromF32(N64_FB_WIDTH); cmd.dxldy = 0; cmd.xh = 0; cmd.dxhdy = 0; cmd.xm = Fixed1616FromF32(N64_FB_WIDTH); cmd.dxmdy = 0; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } #endif } #endif //- rjf: set up common main pass parameters #if 1 { // rjf: sync pipe { N64_DP_Cmd_Stub cmd = {0}; cmd.id = N64_DP_CmdID_SyncPipe; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set color image to framebuffer color { N64_DP_Cmd_SetColorImage cmd = {0}; cmd.id = N64_DP_CmdID_SetColorImage; cmd.format = N64_DP_PixelFormat_RGBA; cmd.pixel_size_kind = N64_DP_PixelSizeKind_16; cmd.width = N64_FB_WIDTH-1; cmd.image_paddr = framebuffer_color_paddrs[tick_idx&1]; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set scissor { N64_DP_Cmd_SetScissor cmd = {0}; cmd.id = N64_DP_CmdID_SetScissor; cmd.lower_right_x = (N64_FB_WIDTH<<2); cmd.lower_right_y = (N64_FB_HEIGHT<<2); WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set depth image to framebuffer depth #if 1 { N64_DP_Cmd_SetDepthImage cmd = {0}; cmd.id = N64_DP_CmdID_SetDepthImage; cmd.image_paddr = framebuffer_depth_paddrs[tick_idx&1]; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } #endif // rjf: set combine mode { N64_DP_Cmd_SetCombineMode cmd = {0}; cmd.v[0] = 0xffffffffffffffffull; // sets all bits high - minus ID, this will fix all mixed colors at 0 - then we just override the inputs we want cmd.id = N64_DP_CmdID_SetCombineMode; cmd.rgb_d_1 = N64_DP_Cmd_CombineColorCode_Shade; cmd.alpha_d_1 = N64_DP_Cmd_CombineColorCode_Shade; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } } #endif //- TODO(rjf): RDP command test #if 1 { // rjf: set scissor { N64_DP_Cmd_SetScissor cmd = {0}; MemoryZeroStruct(&cmd); cmd.id = N64_DP_CmdID_SetScissor; cmd.lower_right_x = ((N64_FB_WIDTH)<<2); cmd.lower_right_y = ((N64_FB_HEIGHT)<<2); WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set other modes { N64_DP_Cmd_SetOtherModes cmd = {0}; MemoryZeroStruct(&cmd); cmd.id = N64_DP_CmdID_SetOtherModes; cmd.atomic_prim = 1; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: set color image to framebuffer color { N64_DP_Cmd_SetColorImage cmd = {0}; MemoryZeroStruct(&cmd); cmd.id = N64_DP_CmdID_SetColorImage; cmd.format = N64_DP_PixelFormat_RGBA; cmd.pixel_size_kind = N64_DP_PixelSizeKind_16; cmd.width = N64_FB_WIDTH-1; cmd.image_paddr = framebuffer_color_paddrs[tick_idx&1]; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } // rjf: fill screen { N64_DP_Cmd_FillTriangleBase cmd = {0}; MemoryZeroStruct(&cmd); cmd.id = N64_DP_CmdID_FillTriangleS; cmd.flags = N64_DP_Cmd_FillTriangleFlag_LeftMajor; cmd.yl = Fixed112FromF32(N64_FB_HEIGHT); cmd.ym = Fixed112FromF32(N64_FB_HEIGHT); cmd.yh = 0; cmd.xl = Fixed1616FromF32(N64_FB_WIDTH); cmd.dxldy = 0; cmd.xh = 0; cmd.dxhdy = 0; cmd.xm = Fixed1616FromF32(N64_FB_WIDTH); cmd.dxmdy = 0; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); { N64_DP_CmdExt_FillTriangleShading ext = {0}; ext.r_i = 255; ext.a_i = 255; WriteStructAndInc(n64_r_state.rdp_command_buffer, &ext, &n64_r_state.rdp_command_buffer_write_pos); } } } #endif //- rjf: do one tick B32 keep_running = Run(tick_idx, user_arena, n64_state.game_pads); //- rjf: push sync full command #if 1 { N64_DP_Cmd_Stub cmd = {0}; cmd.id = N64_DP_CmdID_SyncFull; WriteStructAndInc(n64_r_state.rdp_command_buffer, &cmd, &n64_r_state.rdp_command_buffer_write_pos); } //- rjf: push payload to RDP U32 payload_opl_paddr = ((U32)(n64_r_state.rdp_command_buffer) + n64_r_state.rdp_command_buffer_write_pos) - 0x80000000; N64_DataCacheHitWriteback(n64_r_state.rdp_command_buffer, n64_r_state.rdp_command_buffer_write_pos); N64_DP_REGS->end = payload_opl_paddr; N64_MemoryBarrier(); //- rjf: wait for RDP to be done for(;N64_DP_REGS->current != payload_opl_paddr || N64_DP_REGS->status_flags & (N64_DP_StatusFlag_DMABusy|N64_DP_StatusFlag_PipeBusy);){} #endif //- rjf: wait for last frame presentation to complete before swapping for(;;) { U32 current_scanline = (N64_VI_REGS->v_current >> 1); if(N64_FB_HEIGHT < current_scanline && current_scanline < N64_FB_HEIGHT + 20) { break; } } //- rjf: swap framebuffer N64_VI_REGS->origin = framebuffer_color_paddrs[tick_idx&1]; N64_MemoryBarrier(); //- rjf: exit if keep_running is off if(!keep_running) { break; } } } //////////////////////////////// //~ rjf: Built-In Resources #define B #define _ 0, #define X 1, U8 n64_dgtlgrove_logo_32x48[32*48] = { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ X _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ X X _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ X X _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ }; U8 n64_controller_icon_32x32[32*32] = { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X X X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ X _ _ _ _ _ _ _ _ _ _ _ _ X _ X X _ _ _ _ _ X X _ _ _ _ _ X _ _ X _ _ _ _ _ _ _ _ _ _ _ _ X X X X X _ _ _ _ X X _ _ _ X X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ X X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ X _ X X _ _ _ _ _ X X _ _ _ _ _ X X _ X _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ X X X X _ X _ _ X X _ X _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ X _ X X X X X X _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ X _ X _ X X _ X _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ X _ X _ _ _ _ X _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ X _ _ X _ _ _ _ X _ _ X _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ X _ _ X _ _ _ _ X _ _ X _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X _ _ _ _ X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ }; N64_BigDefaultFontGlyph n64_big_default_font_glyphs[256] = { ['A'] = { { B _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X X X X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['a'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ X _ _ _ X X X X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['B'] = { { B _ _ _ _ _ _ _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['b'] = { { B _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['C'] = { { B _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ X _ _ X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ X _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['c'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['D'] = { { B _ _ _ _ _ _ _ _ X X X _ _ _ _ X _ _ X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['d'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ X X X X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['E'] = { { B _ _ _ _ _ _ _ _ X X X X X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['e'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X X X X X _ _ X _ _ _ _ _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['F'] = { { B _ _ _ _ _ _ _ _ X X X X X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['f'] = { { B _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ X X X X X _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['G'] = { { B _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ X _ _ X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ X X X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X _ _ X _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['g'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X X _ _ _ _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ } }, ['H'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X X X X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['h'] = { { B _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['I'] = { { B _ _ _ _ _ _ _ _ X X X X X _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['i'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['J'] = { { B _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ X _ _ X _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['j'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ X _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['K'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ X _ _ _ X _ X _ _ _ _ X X X _ _ _ _ X _ _ X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['k'] = { { B _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ X _ _ X _ _ X _ _ _ X _ X _ _ _ _ X X X _ _ _ _ X _ _ X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['L'] = { { B _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['l'] = { { B _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['M'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X _ X X _ _ X X _ X X _ _ X _ X _ X _ _ X _ X _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['m'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ _ X _ X _ X _ _ X _ X _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['N'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X _ _ X _ _ X X _ _ X _ _ X _ X _ X _ _ X _ _ X X _ _ X _ _ X X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['n'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['O'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['o'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['P'] = { { B _ _ _ _ _ _ _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X X X _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['p'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X X X _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['Q'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ X X _ _ _ X X X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ } }, ['q'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X X _ _ _ _ _ _ X _ _ _ _ _ _ X X _ _ _ _ _ X _ _ _ _ _ _ _ _ } }, ['R'] = { { B _ _ _ _ _ _ _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X X X X _ _ _ X _ X _ _ _ _ X _ _ X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['r'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X X _ _ _ X X _ _ X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['S'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['s'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['T'] = { { B _ _ _ _ _ _ _ _ X X X X X _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['t'] = { { B _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ X X X X X _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['U'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['u'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['V'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X _ X _ _ _ _ X _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['v'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X _ X _ _ _ _ X _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['W'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ X _ X _ _ X _ X _ X _ _ X X _ X X _ _ X X _ X X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['w'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ X _ X _ _ X X _ X X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['X'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ X _ _ _ _ X _ X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['x'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ _ _ X _ X _ _ _ _ _ X _ _ _ _ _ X _ X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['Y'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X _ X _ _ _ _ X _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['y'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X X _ _ _ _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ } }, ['Z'] = { { B _ _ _ _ _ _ _ _ X X X X X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['z'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ X X _ _ _ _ X _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['0'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ X _ X _ _ X _ X _ X _ _ X _ X _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['1'] = { { B _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X X _ _ _ _ X _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['2'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['3'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['4'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ X _ _ X _ _ _ X _ _ X X X X X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['5'] = { { B _ _ _ _ _ _ _ _ X X X X X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['6'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['7'] = { { B _ _ _ _ _ _ _ _ X X X X X _ _ X _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ X X X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['8'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['9'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X X X X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ X _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['`'] = { { B _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['~'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ X _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['!'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ _ X X X _ _ _ _ X X X _ _ _ _ X X X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['@'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ X X _ _ X _ X _ X _ _ X _ X _ X _ _ X _ X _ X _ _ X _ _ X X _ _ X _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['#'] = { { B _ _ _ _ _ _ _ _ _ X _ X _ _ _ _ X _ X _ _ _ X X X X X _ _ _ X _ X _ _ _ _ X _ X _ _ _ _ X _ X _ _ _ X X X X X _ _ _ X _ X _ _ _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['$'] = { { B _ _ _ X _ _ _ _ _ X X X _ _ _ X _ X _ X _ _ X _ X _ _ _ _ X _ X _ _ _ _ _ X X X _ _ _ _ _ X _ X _ _ _ _ X _ X _ _ X _ X _ X _ _ _ X X X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['%'] = { { B _ _ _ _ _ _ _ _ X X _ _ X _ _ X X _ _ X _ _ X X _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ X X _ _ X _ _ X X _ _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['^'] = { { B _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ X _ _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['&'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ X _ _ _ _ _ _ X X _ _ _ _ X _ _ X _ X _ X _ _ _ X _ _ X _ _ X X _ _ _ X X _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['*'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ X _ X _ X _ _ _ X X X _ _ _ _ _ X _ _ _ _ _ X X X _ _ _ X _ X _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['('] = { { B _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, [')'] = { { B _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['-'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['_'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['='] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ X X X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['+'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ X X X X X _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['['] = { { B _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, [']'] = { { B _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['{'] = { B { _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['}'] = { { B _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, [';'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ X X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, [':'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['\''] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['"'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ _ _ X _ X _ _ _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['|'] = { { B _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['\\'] = { { B _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['.'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, [','] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ X X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['<'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['>'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['?'] = { { B _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ X _ _ X _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['/'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, }; N64_SmallDefaultFontGlyph n64_small_default_font_glyphs[256] = { ['A'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ X _ _ X X X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['a'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ X _ _ _ X X _ _ X _ X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ } }, ['B'] = { { B _ _ _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['b'] = { { B _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['C'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['c'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ _ _ _ X _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['D'] = { { B _ _ _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['d'] = { { B _ _ _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ } }, ['E'] = { { B _ _ _ _ _ _ X X X _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['e'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X _ X _ _ X X X _ _ X _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['F'] = { { B _ _ _ _ _ _ X X X _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['f'] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ X X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['G'] = { { B _ _ _ _ _ _ _ X X _ _ X _ _ _ _ X _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ } }, ['g'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X _ X _ _ X _ X _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ X X _ _ } }, ['H'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['h'] = { { B _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['I'] = { { B _ _ _ _ _ _ X X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['i'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['J'] = { { B _ _ _ _ _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ X _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['j'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ X _ X _ _ _ X _ _ _ } }, ['K'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['k'] = { { B _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ X _ _ X X _ _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['L'] = { { B _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['l'] = { { B _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ } }, ['M'] = { { B _ _ _ _ _ _ X _ X _ _ X X X _ _ X X X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['m'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ X X X _ _ X X X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['N'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ _ X X X _ _ X X X _ _ X X X _ _ X X X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['n'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['O'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['o'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['P'] = { { B _ _ _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['p'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ } }, ['Q'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X X _ _ _ _ X X _ _ _ _ _ } }, ['q'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X _ X _ _ X _ X _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ _ _ X X } }, ['R'] = { { B _ _ _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['r'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['S'] = { { B _ _ _ _ _ _ _ X X _ _ X _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['s'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ X _ _ _ _ X X X _ _ _ _ X _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['T'] = { { B _ _ _ _ _ _ X X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['t'] = { { B _ _ _ _ _ _ _ X _ _ _ _ X _ _ _ X X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ } }, ['U'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['u'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['V'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['v'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['W'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ X X X _ _ X X X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['w'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ X _ X _ _ X X X _ _ X X X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['X'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['x'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ X _ X _ _ _ X _ _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['Y'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['y'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ X X _ _ } }, ['Z'] = { { B _ _ _ _ _ _ X X X _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['z'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ X _ _ _ X _ _ _ X _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['0'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ X _ _ X X X _ _ X X X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['1'] = { { B _ _ _ _ _ _ _ X _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['2'] = { { B _ _ _ _ _ _ X X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ } }, ['3'] = { { B _ _ _ _ _ _ X X _ _ _ _ _ X _ _ _ _ X _ _ X X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['4'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ _ _ X _ _ _ _ X _ X _ _ X X X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ } }, ['5'] = { { B _ _ _ _ _ _ X X X _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['6'] = { { B _ _ _ _ _ _ _ X X _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['7'] = { { B _ _ _ _ _ _ X X X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['8'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ X _ _ _ X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['9'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ X _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['`'] = { { B _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['~'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ X _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['!'] = { { B _ _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ } }, ['@'] = { { B _ _ _ _ _ _ X X X _ X _ _ _ X X _ _ X X X _ X _ X X _ X _ X X _ X _ X X _ _ X X X _ _ _ _ _ X X X _ _ _ _ _ _ } }, ['#'] = { { B _ _ _ _ _ _ X _ X _ _ X _ X _ X X X X X _ X _ X _ _ X _ X _ _ X _ X _ X X X X X _ X _ X _ _ X _ X _ _ _ _ _ _ } }, ['$'] = { { B _ _ X _ _ _ _ X X _ _ X _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ X X _ _ _ _ X _ _ _ _ _ _ _ } }, ['%'] = { { B _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X X _ _ _ _ _ X _ _ _ X _ _ _ X _ _ _ _ _ X X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ } }, ['^'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['&'] = { { B _ _ _ _ _ _ _ X _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ X _ _ _ X X X _ _ X _ X X _ X _ X _ _ _ X _ X _ _ _ _ _ } }, ['*'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X X X _ _ _ X _ _ _ X X X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['('] = { { B _ _ _ _ _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ } }, [')'] = { { B _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ _ _ _ _ _ } }, ['-'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['_'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X X X } }, ['='] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ X X X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['+'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ X _ _ _ X X X _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['['] = { { B _ _ _ _ _ _ _ X X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ _ _ _ } }, [']'] = { { B _ _ _ _ _ _ X X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X X _ _ _ _ _ _ _ } }, ['{'] = { B { _ _ _ _ _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ } }, ['}'] = { { B _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ _ _ _ _ _ } }, [';'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X X _ _ _ _ X _ _ _ X _ _ _ } }, [':'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['\''] = { { B _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['"'] = { { B _ _ _ _ _ _ _ _ _ _ _ X _ X _ _ X _ X _ _ X _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['|'] = { { B _ _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ } }, ['\\'] = { { B _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ } }, ['.'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ } }, [','] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X X _ _ _ X X _ _ _ _ X _ _ _ X _ _ _ } }, ['<'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ X _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['>'] = { { B _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ X _ _ _ _ _ X _ _ _ _ _ X _ _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ } }, ['?'] = { { B _ _ _ _ _ _ X X _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ _ _ _ _ _ _ X _ _ _ _ _ _ _ _ _ _ _ _ } }, ['/'] = { { B _ _ _ _ _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ _ _ _ _ } }, }; #undef B #undef X #undef _