diff --git a/Makefile b/Makefile index 351f9bc..56f8de2 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,10 @@ OPT = -Os all: cartridge.bin -phony: +phony: res + +res: + make -C res/ arm9/%.bin: phony make -C arm9/ $(notdir $@) @@ -14,6 +17,7 @@ DEFAULT = header.o arm7/arm7.bin.o triangle.elf: $(DEFAULT) arm9/triangle.bin.o triangle_rotating.elf: $(DEFAULT) arm9/triangle_rotating.bin.o +texture.elf: $(DEFAULT) arm9/texture.bin.o TARGET = arm-none-eabi- AARCH = -march=armv4t -mlittle-endian @@ -21,4 +25,4 @@ OBJARCH = -O elf32-littlearm -B armv4t LDSCRIPT = cartridge.lds include common.mk -.PHONY: phony +.PHONY: phony res diff --git a/arm9/Makefile b/arm9/Makefile index b0e1f04..0ad5465 100644 --- a/arm9/Makefile +++ b/arm9/Makefile @@ -20,6 +20,8 @@ triangle.elf: start.o examples/triangle.o triangle_rotating.elf: start.o examples/triangle_rotating.o ../math/cos_table_fp12.o ../math/cos.o +texture.elf: start.o examples/texture.o ../res/hotaru_futaba.data.o ../res/hotaru_futaba.data.pal.o + CFLAGS += -I../include -I../math include arm9.mk diff --git a/arm9/examples/texture.c b/arm9/examples/texture.c new file mode 100644 index 0000000..67575b7 --- /dev/null +++ b/arm9/examples/texture.c @@ -0,0 +1,272 @@ +#include "io_registers.h" +#include "bits.h" + +#include "../res/hotaru_futaba.data.h" +#include "../res/hotaru_futaba.data.pal.h" + +static inline uint16_t rgb565(const uint8_t * buf) +{ + uint8_t r = buf[0] >> 3; + uint8_t g = buf[1] >> 3; + uint8_t g_l = (buf[1] >> 2) & 1; + uint8_t b = buf[2] >> 3; + + return (g_l << 15) | (b << 10) | (g << 5) | (r << 0); +} + +void main() +{ + // power control + io_registers.a.POWCNT = 0 + | POWCNT__lcd_output_destination__a_to_upper__b_to_lower + | POWCNT__geometry_engine__enable + | POWCNT__rendering_engine__enable + | POWCNT__lcd__enable; + + // enable bg0 and 3d graphics + io_registers.a.DISPCNT = 0 + | DISPCNT__display_mode__graphics_display + | DISPCNT__bg0__enable + | DISPCNT__display_selection_for_bg0__3d_graphics + ; + + // disable all 3d effects, enable texture mapping + io_registers.a.DISP3DCNT = 0 + | DISP3DCNT__clear_image__disable + | DISP3DCNT__fog_master__disable + | DISP3DCNT__edge_marking__disable + | DISP3DCNT__anti_aliasing__disable + | DISP3DCNT__alpha_blending__disable + | DISP3DCNT__alpha_test__disable + | DISP3DCNT__texture_mapping__enable; + + // memory bank allocation + // use VRAM-A for texture pixel data + // use VRAM-E for texture palette data + + // temporarily map VRAM-A (128KB) to the arm9 address space: + // 0x06800000 - 0x0681FFFF + io_registers.a.VRAMCNT = 0 + | VRAMCNT__vram_a__enable + | VRAMCNT__vram_a__mst(0b00); // arm9 + + // temporarily map VRAM-F (16KB) to the arm9 address space: + // 0x06890000 - 0x06893FFF + io_registers.a.WVRAMCNT = 0 + | WVRAMCNT__vram_f__enable + | WVRAMCNT__vram_f__mst(0b000); // arm9 + + // at this point, VRAM-A/VRAM-E are not accessible by the 3d engine. + + // copy palette data + const uint8_t * pal = (const uint8_t *)&_binary_hotaru_futaba_data_pal_start; + volatile uint16_t * vram_f = (volatile uint16_t *)(0x06890000); + for (int i = 0; i < 16; i++) { + // as exported by GIMP, the palette data is three bytes per pixel, packed + // RGB24 + vram_f[i] = rgb565(&pal[i * 3]); + } + + // copy pixel data + const uint8_t * pixels = (const uint8_t *)&_binary_hotaru_futaba_data_start; + volatile uint16_t * vram_a = (volatile uint16_t *)(0x06800000); + + // notice the += 2 increment + for (int i = 0; i < 96 * 64; i += 2) { + // as exported by GIMP, the pixel data is two bytes per pixel. + // [0]: color index (0-15) + // [1]: alpha (0 or 255) + + // generate a3i5 pixel data: + // | 15 14 13 | 12 10 09 08 | 07 06 05 | 04 03 02 01 00 | + // | t1 alpha | t1 index | t0 alpha | t0 index | + + // process two pixels per loop iteration + uint8_t t0_index = pixels[(i + 0) * 2 + 0]; + uint8_t t0_alpha = pixels[(i + 0) * 2 + 1]; + + uint8_t t1_index = pixels[(i + 1) * 2 + 0]; + uint8_t t1_alpha = pixels[(i + 1) * 2 + 1]; + + // scale alpha from 8-bit to 3-bit + t0_alpha >>= 5; + t1_alpha >>= 5; + + // mask index to 4-bit (though the data should already be 16-color, just in case) + t0_index &= 0xf; + t1_index &= 0xf; + + // the data is now one byte per a3i5 texel, two a3i5 texels per uint16_t + uint16_t a3i5_texel = (t1_alpha << 13) | (t1_index << 8) | (t0_alpha << 5) | (t0_index << 0); + vram_a[i / 2] = a3i5_texel; + } + + // arm9 no longer needs read/write access to texture data, remap vram to the 3d engine + + // map VRAM-A (128KB) to the 3d-engine "texture image slot 0": + // 0x00000 - 0x1ffff (3d engine texture image address space) + io_registers.a.VRAMCNT = 0 + | VRAMCNT__vram_a__enable + | VRAMCNT__vram_a__ofs(0) // slot 0 + | VRAMCNT__vram_a__mst(0b11); // texture image + + // map VRAM-F (16KB) to the 3d-engine "texture palette slot 0": + // 0x0000 - 0x3fff (3d engine texture palette address space) + io_registers.a.WVRAMCNT = 0 + | WVRAMCNT__vram_f__enable + | WVRAMCNT__vram_f__ofs(0) // slot 0 + | WVRAMCNT__vram_f__mst(0b011); // texture palette + + // set the texture palette base address + io_registers.a.TEXPLTT_BASE = TEXPLTT_BASE__base_address(0); + + io_registers.a.TEXIMAGE_PARAM = 0 + | TEXIMAGE_PARAM__texture_coordinate_transformation_mode__texcoord_source + | TEXIMAGE_PARAM__texture_format__a3i5_translucent + | TEXIMAGE_PARAM__t_size__128_texels // actually 96 pixels + | TEXIMAGE_PARAM__s_size__64_texels + | TEXIMAGE_PARAM__texture_starting_address(0); + + // the following polygons are fully opaque and are not + // backface-culled + // use + io_registers.a.POLYGON_ATTR = 0 + | POLYGON_ATTR__alpha_value(31) + | POLYGON_ATTR__render_front_surface__enable + | POLYGON_ATTR__render_back_surface__enable; + + // clear matrix stack status + io_registers.a.GXSTAT |= GXSTAT__matrix_stack_status__overflow_or_underflow; + + // load identity matrices + io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__projection; + io_registers.a.MTX_IDENTITY = 0; + // scale the x-axis by the ratio of the display height by the display width. + io_registers.a.MTX_SCALE = (192 << 12) / 256; + io_registers.a.MTX_SCALE = 1 << 12; + io_registers.a.MTX_SCALE = 1 << 12; + + io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position_and_vector; + io_registers.a.MTX_IDENTITY = 0; + + io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position; + io_registers.a.MTX_IDENTITY = 0; + // scale the x-axis by the ratio of the hotaru sprite dimensions + io_registers.a.MTX_SCALE = (64 << 12) / 96; + io_registers.a.MTX_SCALE = 1 << 12; + io_registers.a.MTX_SCALE = 1 << 12; + + io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__texture; + io_registers.a.MTX_IDENTITY = 0; + + // set the 3d clear color to a dark red + io_registers.a.CLEAR_COLOR = 0 + | CLEAR_COLOR__clear_polygon_id(31) + | CLEAR_COLOR__alpha_value(31) + | CLEAR_COLOR__blue(1) + | CLEAR_COLOR__green(1) + | CLEAR_COLOR__red(10); + + // set the depth buffer clear value to the maximum value + io_registers.a.CLEAR_DEPTH = CLEAR_DEPTH__value(0x7fff); + + // the 3d viewport is the entire display area + io_registers.a.VIEWPORT = 0 + | VIEWPORT__y2(191) + | VIEWPORT__x2(255) + | VIEWPORT__y1(0) + | VIEWPORT__x1(0); + + // VTX_10 uses signed 4.6 floating point (10 bit) + // | 9 | 8 7 6 | 5 4 3 2 1 0 | + // | s | int | decimal | + int vtx_10_fixed_point = 64; // == 2⁶ + + // TEXCOORD uses signed 12.4 floating point (16 bit) + int texcoord_fixed_point = 16; // == 2⁴ + + // quadrilateral; top-left corner is top-left of screen + /* + A--D + | | + | | + B--C + + abcd + */ + int ax = -1.0 * vtx_10_fixed_point; + int ay = 1.0 * vtx_10_fixed_point; + int as = 0.0 * texcoord_fixed_point; + int at = 0.0 * texcoord_fixed_point; + + int bx = -1.0 * vtx_10_fixed_point; + int by = -1.0 * vtx_10_fixed_point; + int bs = 0.0 * texcoord_fixed_point; + int bt = 95.0 * texcoord_fixed_point; + + int cx = 1.0 * vtx_10_fixed_point; + int cy = -1.0 * vtx_10_fixed_point; + int cs = 63.0 * texcoord_fixed_point; + int ct = 95.0 * texcoord_fixed_point; + + int dx = 1.0 * vtx_10_fixed_point; + int dy = 1.0 * vtx_10_fixed_point; + int ds = 63.0 * texcoord_fixed_point; + int dt = 0.0 * texcoord_fixed_point; + + int z = 1.0 * vtx_10_fixed_point; + + while (1) { + io_registers.a.COLOR = 0 + | COLOR__blue(31) + | COLOR__green(31) + | COLOR__red(31); + + // first quadrilateral: ABCD + io_registers.a.BEGIN_VTXS = BEGIN_VTXS__type__quadrilateral; + + io_registers.a.TEXCOORD = 0 + | TEXCOORD__t_coordinate(at) + | TEXCOORD__s_coordinate(as); + io_registers.a.VTX_10 = 0 + | VTX_10__z_coordinate(z) + | VTX_10__y_coordinate(ay) + | VTX_10__x_coordinate(ax); + + io_registers.a.TEXCOORD = 0 + | TEXCOORD__t_coordinate(bt) + | TEXCOORD__s_coordinate(bs); + io_registers.a.VTX_10 = 0 + | VTX_10__z_coordinate(z) + | VTX_10__y_coordinate(by) + | VTX_10__x_coordinate(bx); + + io_registers.a.TEXCOORD = 0 + | TEXCOORD__t_coordinate(ct) + | TEXCOORD__s_coordinate(cs); + io_registers.a.VTX_10 = 0 + | VTX_10__z_coordinate(z) + | VTX_10__y_coordinate(cy) + | VTX_10__x_coordinate(cx); + + io_registers.a.TEXCOORD = 0 + | TEXCOORD__t_coordinate(dt) + | TEXCOORD__s_coordinate(ds); + io_registers.a.VTX_10 = 0 + | VTX_10__z_coordinate(z) + | VTX_10__y_coordinate(dy) + | VTX_10__x_coordinate(dx); + + // end of the ABCD quadrilateral + io_registers.a.END_VTXS = 0; + + // wait for the geometry engine + while (io_registers.a.GXSTAT & GXSTAT__geometry_engine_busy); + + while (io_registers.a.VCOUNT != 262); + while (io_registers.a.VCOUNT != 0); + + // swap buffers + io_registers.a.SWAP_BUFFERS = 0; + } +} diff --git a/arm9/examples/triangle.c b/arm9/examples/triangle.c index ddc2ce2..700b3ab 100644 --- a/arm9/examples/triangle.c +++ b/arm9/examples/triangle.c @@ -17,6 +17,16 @@ void main() | DISPCNT__display_selection_for_bg0__3d_graphics ; + // disable all 3d effects + io_registers.a.DISP3DCNT = 0 + | DISP3DCNT__clear_image__disable + | DISP3DCNT__fog_master__disable + | DISP3DCNT__edge_marking__disable + | DISP3DCNT__anti_aliasing__disable + | DISP3DCNT__alpha_blending__disable + | DISP3DCNT__alpha_test__disable + | DISP3DCNT__texture_mapping__disable; + // clear matrix stack status io_registers.a.GXSTAT |= GXSTAT__matrix_stack_status__overflow_or_underflow; @@ -30,16 +40,6 @@ void main() io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position_and_vector; io_registers.a.MTX_IDENTITY = 0; - // disable all 3d effects - io_registers.a.DISP3DCNT = 0 - | DISP3DCNT__clear_image__disable - | DISP3DCNT__fog_master__disable - | DISP3DCNT__edge_marking__disable - | DISP3DCNT__anti_aliasing__disable - | DISP3DCNT__alpha_blending__disable - | DISP3DCNT__alpha_test__disable - | DISP3DCNT__texture_mapping__disable; - // set the 3d clear color to a dark red io_registers.a.CLEAR_COLOR = 0 | CLEAR_COLOR__clear_polygon_id(31) diff --git a/arm9/examples/triangle_rotating.c b/arm9/examples/triangle_rotating.c index 3ca6dac..e625ab6 100644 --- a/arm9/examples/triangle_rotating.c +++ b/arm9/examples/triangle_rotating.c @@ -19,6 +19,16 @@ void main() | DISPCNT__display_selection_for_bg0__3d_graphics ; + // disable all 3d effects + io_registers.a.DISP3DCNT = 0 + | DISP3DCNT__clear_image__disable + | DISP3DCNT__fog_master__disable + | DISP3DCNT__edge_marking__disable + | DISP3DCNT__anti_aliasing__disable + | DISP3DCNT__alpha_blending__disable + | DISP3DCNT__alpha_test__disable + | DISP3DCNT__texture_mapping__disable; + // clear matrix stack status io_registers.a.GXSTAT |= GXSTAT__matrix_stack_status__overflow_or_underflow; @@ -36,16 +46,6 @@ void main() io_registers.a.MTX_MODE = MTX_MODE__matrix_mode__position_and_vector; io_registers.a.MTX_IDENTITY = 0; - // disable all 3d effects - io_registers.a.DISP3DCNT = 0 - | DISP3DCNT__clear_image__disable - | DISP3DCNT__fog_master__disable - | DISP3DCNT__edge_marking__disable - | DISP3DCNT__anti_aliasing__disable - | DISP3DCNT__alpha_blending__disable - | DISP3DCNT__alpha_test__disable - | DISP3DCNT__texture_mapping__disable; - // set the 3d clear color to a dark red io_registers.a.CLEAR_COLOR = 0 | CLEAR_COLOR__clear_polygon_id(31) diff --git a/include/bits.h b/include/bits.h index 68085ee..c1de7eb 100644 --- a/include/bits.h +++ b/include/bits.h @@ -113,6 +113,40 @@ #define BG3CNT__mosaic__enable (0x1 << 6) #define BG3CNT__character_base_block(v) (((v) & 0xf) << 2) #define BG3CNT__priority(v) (((v) & 0x3) << 0) +#define VRAMCNT__vram_d__disable (0x0 << 31) +#define VRAMCNT__vram_d__enable (0x1 << 31) +#define VRAMCNT__vram_d__ofs(v) (((v) & 0x3) << 27) +#define VRAMCNT__vram_d__mst(v) (((v) & 0x7) << 24) +#define VRAMCNT__vram_c__disable (0x0 << 23) +#define VRAMCNT__vram_c__enable (0x1 << 23) +#define VRAMCNT__vram_c__ofs(v) (((v) & 0x3) << 19) +#define VRAMCNT__vram_c__mst(v) (((v) & 0x7) << 16) +#define VRAMCNT__vram_b__disable (0x0 << 15) +#define VRAMCNT__vram_b__enable (0x1 << 15) +#define VRAMCNT__vram_b__ofs(v) (((v) & 0x3) << 11) +#define VRAMCNT__vram_b__mst(v) (((v) & 0x3) << 8) +#define VRAMCNT__vram_a__disable (0x0 << 7) +#define VRAMCNT__vram_a__enable (0x1 << 7) +#define VRAMCNT__vram_a__ofs(v) (((v) & 0x3) << 3) +#define VRAMCNT__vram_a__mst(v) (((v) & 0x3) << 0) +#define WVRAMCNT__wram__bank(v) (((v) & 0x3) << 24) +#define WVRAMCNT__vram_g__disable (0x0 << 23) +#define WVRAMCNT__vram_g__enable (0x1 << 23) +#define WVRAMCNT__vram_g__ofs(v) (((v) & 0x3) << 19) +#define WVRAMCNT__vram_g__mst(v) (((v) & 0x7) << 16) +#define WVRAMCNT__vram_f__disable (0x0 << 15) +#define WVRAMCNT__vram_f__enable (0x1 << 15) +#define WVRAMCNT__vram_f__ofs(v) (((v) & 0x3) << 11) +#define WVRAMCNT__vram_f__mst(v) (((v) & 0x7) << 8) +#define WVRAMCNT__vram_e__disable (0x0 << 7) +#define WVRAMCNT__vram_e__enable (0x1 << 7) +#define WVRAMCNT__vram_e__mst(v) (((v) & 0x7) << 0) +#define VRAM_HI_CNT__vram_i__disable (0x0 << 15) +#define VRAM_HI_CNT__vram_i__enable (0x1 << 15) +#define VRAM_HI_CNT__vram_i__mst(v) (((v) & 0x3) << 8) +#define VRAM_HI_CNT__vram_h__disable (0x0 << 7) +#define VRAM_HI_CNT__vram_h__enable (0x1 << 7) +#define VRAM_HI_CNT__vram_h__mst(v) (((v) & 0x3) << 0) #define POWCNT__lcd_output_destination__a_to_lower__b_to_upper (0x0 << 15) #define POWCNT__lcd_output_destination__a_to_upper__b_to_lower (0x1 << 15) #define POWCNT__2d_graphics_engine_b__disable (0x0 << 9) @@ -171,7 +205,7 @@ #define NORMAL__y_component(v) (((v) & 0x3ff) << 10) #define NORMAL__x_component(v) (((v) & 0x3ff) << 0) #define TEXCOORD__t_coordinate(v) (((v) & 0xffff) << 16) -#define TEXCOORD__x_coordinate(v) (((v) & 0xffff) << 0) +#define TEXCOORD__s_coordinate(v) (((v) & 0xffff) << 0) #define VTX_16__0__y_coordinate(v) (((v) & 0xffff) << 16) #define VTX_16__0__x_coordinate(v) (((v) & 0xffff) << 0) #define VTX_16__1__z_coordinate(v) (((v) & 0xffff) << 0) diff --git a/registers/graphics_engine_bits.csv b/registers/graphics_engine_bits.csv index 139d2dc..cd4a355 100644 --- a/registers/graphics_engine_bits.csv +++ b/registers/graphics_engine_bits.csv @@ -121,6 +121,44 @@ "BG3CNT",,"5-2","character_base_block",,"0b1111", "BG3CNT",,"1-0","priority",,"0b11", ,,,,,, +"VRAMCNT","vram_d",31,"disable",0,, +"VRAMCNT","vram_d",31,"enable",1,, +"VRAMCNT","vram_d","28-27","ofs",,"0b11", +"VRAMCNT","vram_d","26-24","mst",,"0b111", +"VRAMCNT","vram_c",23,"disable",0,, +"VRAMCNT","vram_c",23,"enable",1,, +"VRAMCNT","vram_c","20-19","ofs",,"0b11", +"VRAMCNT","vram_c","18-16","mst",,"0b111", +"VRAMCNT","vram_b",15,"disable",0,, +"VRAMCNT","vram_b",15,"enable",1,, +"VRAMCNT","vram_b","12-11","ofs",,"0b11", +"VRAMCNT","vram_b","9-8","mst",,"0b11", +"VRAMCNT","vram_a",7,"disable",0,, +"VRAMCNT","vram_a",7,"enable",1,, +"VRAMCNT","vram_a","4-3","ofs",,"0b11", +"VRAMCNT","vram_a","1-0","mst",,"0b11", +,,,,,, +"WVRAMCNT","wram","25-24","bank",,"0b11", +"WVRAMCNT","vram_g",23,"disable",0,, +"WVRAMCNT","vram_g",23,"enable",1,, +"WVRAMCNT","vram_g","20-19","ofs",,"0b11", +"WVRAMCNT","vram_g","18-16","mst",,"0b111", +"WVRAMCNT","vram_f",15,"disable",0,, +"WVRAMCNT","vram_f",15,"enable",1,, +"WVRAMCNT","vram_f","12-11","ofs",,"0b11", +"WVRAMCNT","vram_f","10-8","mst",,"0b111", +"WVRAMCNT","vram_e",7,"disable",0,, +"WVRAMCNT","vram_e",7,"enable",1,, +"WVRAMCNT","vram_e","2-0","mst",,"0b111", +,,,,,, +"VRAM_HI_CNT","vram_i",15,"disable",0,, +"VRAM_HI_CNT","vram_i",15,"enable",1,, +"VRAM_HI_CNT","vram_i","9-8","mst",,"0b11", +"VRAM_HI_CNT","vram_h",7,"disable",0,, +"VRAM_HI_CNT","vram_h",7,"enable",1,, +"VRAM_HI_CNT","vram_h","1-0","mst",,"0b11", +,,,,,, +,,,,,, "POWCNT","lcd_output_destination",15,"a_to_lower__b_to_upper",0,, "POWCNT","lcd_output_destination",15,"a_to_upper__b_to_lower",1,, "POWCNT","2d_graphics_engine_b",9,"disable",0,, @@ -192,7 +230,7 @@ "NORMAL",,"9-0","x_component",,"0x7ff", ,,,,,, "TEXCOORD",,"31-16","t_coordinate",,"0xffff", -"TEXCOORD",,"15-0","x_coordinate",,"0xffff", +"TEXCOORD",,"15-0","s_coordinate",,"0xffff", ,,,,,, "VTX_16",0,"31-16","y_coordinate",,"0xffff", "VTX_16",0,"15-0","x_coordinate",,"0xffff", diff --git a/registers/graphics_engine_bits.ods b/registers/graphics_engine_bits.ods index d6ace6e..171c391 100644 Binary files a/registers/graphics_engine_bits.ods and b/registers/graphics_engine_bits.ods differ diff --git a/res/hotaru_futaba.data b/res/hotaru_futaba.data new file mode 100644 index 0000000..988a3ff Binary files /dev/null and b/res/hotaru_futaba.data differ diff --git a/res/hotaru_futaba.data.h b/res/hotaru_futaba.data.h new file mode 100644 index 0000000..9dda90e --- /dev/null +++ b/res/hotaru_futaba.data.h @@ -0,0 +1,5 @@ +#pragma once +#include +extern uint32_t _binary_hotaru_futaba_data_start __asm("_binary_hotaru_futaba_data_start"); +extern uint32_t _binary_hotaru_futaba_data_end __asm("_binary_hotaru_futaba_data_end"); +extern uint32_t _binary_hotaru_futaba_data_size __asm("_binary_hotaru_futaba_data_size"); diff --git a/res/hotaru_futaba.data.pal b/res/hotaru_futaba.data.pal new file mode 100644 index 0000000..bc024e8 Binary files /dev/null and b/res/hotaru_futaba.data.pal differ diff --git a/res/hotaru_futaba.data.pal.h b/res/hotaru_futaba.data.pal.h new file mode 100644 index 0000000..d8d0718 --- /dev/null +++ b/res/hotaru_futaba.data.pal.h @@ -0,0 +1,5 @@ +#pragma once +#include +extern uint32_t _binary_hotaru_futaba_data_pal_start __asm("_binary_hotaru_futaba_data_pal_start"); +extern uint32_t _binary_hotaru_futaba_data_pal_end __asm("_binary_hotaru_futaba_data_pal_end"); +extern uint32_t _binary_hotaru_futaba_data_pal_size __asm("_binary_hotaru_futaba_data_pal_size");