You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4870 lines
91 KiB
C

// 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 _