Ovladač pro barevný TFT displej 76x284 obrazových bodů s čipem ST7789P3. Displej je připojen k Raspberry Pi Pico pomocí SPI sběrnice. Podpora pro UTF-8 znaky. Verze 0.1.
Konstrukce




Zapojení
| pin na displeji | pin na Picu |
|---|---|
GND |
libovolné GND |
VCC |
3V3(OUT) (pin 36) |
SCL |
GP18 (pin 24) |
SDA |
GP19 (pin 25) |
RST |
GP15 (pin 20) |
DC |
GP20 (pin 26) |
CS |
GP17 (pin 22) |
BL |
libovolné GND |
Zdrojové kódy
CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
set(PICO_BOARD pico)
project(st7789p3_test C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_executable(st7789p3_test
st7789p3_76x284_test.c
utf8.c
font_spleen_6x12.c
font_spleen_8x16.c
font_spleen_16x32.c
font_10x20.c
)
target_link_libraries(st7789p3_test pico_stdlib hardware_spi)
# create map/bin/hex file etc.
pico_enable_stdio_usb(st7789p3_test 1)
pico_enable_stdio_uart(st7789p3_test 0)
pico_add_extra_outputs(st7789p3_test)
st7789p3_76x284_test.c
/* RPi Pico driver for display ER-TFT2.25-1 ST7789P3 76x284 pixels
* (2.25 inch TFT Serial SPI 76x284 LCD Display Module w/ST7789P3)
* https://www.buydisplay.com/2-25-inch-tft-serial-spi-76x284-lcd-display-module-w-st7789p3
*
* Copyright (c) Jirka Chráska 2026, <jirka@lixis.cz>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <pico/stdlib.h>
#include "hardware/spi.h"
#include "font.h"
#include "utf8.h"
// barvy
#define RGB(R,G,B) (((R >> 3) << 11) | ((G >> 2) << 5) | (B>>3))
enum Color {
BLACK = RGB( 0, 0, 0),
GRAY = RGB(192, 192, 192),
WHITE = RGB(255, 255, 255),
RED = RGB(255, 0, 0),
PINK = RGB(255, 192, 203),
YELLOW = RGB(255, 255, 0),
GOLDEN = RGB(255, 215, 0),
BROWN = RGB(128, 42, 42),
BLUE = RGB( 0, 0, 255),
GREEN = RGB( 0, 255, 0),
PURPLE = RGB(160, 32, 240),
// Red HTML Color Names
IndianRed = RGB(205, 92, 92),
LightCoral = RGB(240,128,128),
Salmon = RGB(250,128,114),
DarkSalmon = RGB(233,150,122),
LightSalmon = RGB(255,160,122),
Crimson = RGB(220, 20, 60),
Red = RGB(255, 0, 0),
FireBrick = RGB(178, 34, 34),
DarkRed = RGB(139, 0, 0),
// Pink HTML Color Names
Pink = RGB(255,192,203),
LighPink = RGB(255,182,193),
HotPink = RGB(255,105,180),
DeepPink = RGB(255, 20,147),
MediumVioletRed = RGB(199, 21,133),
PaleVioletRed = RGB(219,112,147),
// Orange HTML Color Names
Coral = RGB(255,127, 80),
Tomato = RGB(255, 99, 71),
OrangeRed = RGB(255, 69, 0),
DarkOrange = RGB(255,140, 0),
Orange = RGB(255,165, 0),
// Yellow HTML Color Names
Gold = RGB(255,215, 0),
Yellow = RGB(255,255, 0),
LightYellow = RGB(255,255,224),
LemonChiffon = RGB(255,250,205),
LightGoldenrodYellow = RGB(250,250,210),
PapayaWhip = RGB(255,239,213),
Moccasin = RGB(255,228,181),
PeachPuff = RGB(255,218,185),
PaleGoldenrod = RGB(255,232,170),
Khaki = RGB(240,230,140),
DarkKhaki = RGB(189,183,107),
// Purple HTML Color Names
Lavender = RGB(230,230,250),
Thistle = RGB(216,191,216),
Plum = RGB(221,160,221),
Violet = RGB(238,130,238),
Orchid = RGB(218,112,214),
Fuchsia = RGB(255, 0,255),
Magenta = RGB(255, 0,255),
MediumOrchid = RGB(186, 85,211),
MediumPurple = RGB(147,112,219),
RebeccaPurple = RGB(102, 51,153),
BlueViolet = RGB(138, 43,226),
DarkViolet = RGB(148, 0,211),
DarkOrchid = RGB(153, 50,204),
DarkMagenta = RGB(139, 0,139),
Purple = RGB(128, 0,128),
Indigo = RGB( 75, 0,130),
SlateBlue = RGB(106, 90,205),
DarkSlateBlue = RGB( 72, 61,139),
MediumSlateBlue = RGB(123,104,238),
// Green HTML Color Names
GreenYellow = RGB(173,255, 47),
Chartreuse = RGB(127,255, 0),
LawnGreen = RGB(124,252, 0),
Lime = RGB( 0,255, 0),
LimeGreen = RGB( 50,255, 50),
PaleGreen = RGB(152,251,152),
LightGreen = RGB(144,238,144),
MediumSpringGreen = RGB( 0,250,154),
SpringGreen = RGB( 0,255,127),
MediumSeaGreen = RGB( 60,179,113),
SeaGreen = RGB( 46,139, 87),
ForestGreen = RGB( 34,139, 34),
Green = RGB( 0,128, 0),
DarkGreen = RGB( 0,100, 0),
YellowGreen = RGB(154,205, 50),
OliveDrab = RGB(107,142, 35),
Olive = RGB(128,128, 0),
DarkOliveGreen = RGB( 85,107, 47),
MediumAquamarine = RGB(102,205,170),
DarkSeaGreen = RGB(143,188,139),
LightSeaGreen = RGB( 32,178,170),
DarkCyan = RGB( 0,139,139),
Teal = RGB( 0,128,128),
// Blue HTML Color Names
Aqua = RGB( 0,255,255),
Cyan = RGB( 0,255,255),
LightCyan = RGB(224,255,255),
PaleTurquoise = RGB(175,238,238),
Aquamarine = RGB(127,255,212),
Turquoise = RGB( 64,224,208),
MediumTurquoise = RGB( 72,209,204),
DarkTurquoise = RGB( 0,206,209),
CadetBlue = RGB( 95,158,160),
SteelBlue = RGB( 70,130,180),
LightSteelBlue = RGB(176,196,222),
PowderBlue = RGB(176,224,230),
LightBlue = RGB(173,216,230),
SkyBlue = RGB(135,206,250),
LightSkyBlue = RGB(135,206,250),
DeepSkyBlue = RGB( 0,191,255),
DodgerBlue = RGB( 30,144,255),
CornflowerBlue = RGB(100,149,237),
RoyalBlue = RGB( 65,105,225),
Blue = RGB( 0, 0,255),
MediumBlue = RGB( 0, 0,205),
DarkBlue = RGB( 0, 0,139),
Navy = RGB( 0, 0,128),
MidnightBlue = RGB( 25, 25,112),
// Brown HTML Color Names
Cornsilk = RGB(255,248,220),
BlanchedAlmond = RGB(255,235,205),
Bisque = RGB(255,228,196),
NavajoWhite = RGB(255,222,173),
Wheat = RGB(245,222,179),
BurlyWood = RGB(222,184,135),
Tan = RGB(210,180,140),
RosyBrown = RGB(188,143,143),
SandyBrown = RGB(244,164, 96),
Goldenrod = RGB(218,165, 32),
DarkGoldenrod = RGB(184,134, 11),
Peru = RGB(205,133, 63),
Chocolate = RGB(210,105, 30),
SaddleBrown = RGB(139, 69, 19),
Sienna = RGB(160, 82, 45),
Brown = RGB(165, 42, 42),
Maroon = RGB(128, 0, 0),
// White HTML Color Names
White = RGB(255,255,255),
Snow = RGB(255,250,250),
HoneyDew = RGB(240,255,240),
MintCream = RGB(245,255,250),
Azure = RGB(240,255,255),
AliceBlue = RGB(240,248,255),
GhostWhite = RGB(248,248,255),
WhiteSmoke = RGB(245,245,245),
SeaShell = RGB(255,245,220),
Beige = RGB(245,245,220),
OldLace = RGB(253,245,230),
FloralWhite = RGB(255,250,240),
Ivory = RGB(255,255,240),
AntiqueWhite = RGB(250,235,215),
Linen = RGB(250,240,245),
LavenderBlush = RGB(255,240,245),
MistyRose = RGB(255,228,225),
// Gray HTML Color Names
Gainsboro = RGB(220,220,220),
LightGray = RGB(211,211,211),
Silver = RGB(192,192,192),
DarkGray = RGB(169,169,169),
Gray = RGB(128,128,128),
DimGray = RGB(105,105,105),
LightSlateGray = RGB(119,136,153),
SlateGray = RGB(112,128,144),
DarkSlateGray = RGB( 47, 79, 79),
Black = RGB( 0, 0, 0),
};
// barevné formáty obrázků
typedef enum {
COLOR_FORMAT_UNKNOWN = 0,
COLOR_FORMAT_RGB565 = 1,
} color_format_t;
// popis obrázku
typedef struct {
color_format_t cf; // barevný formát obrázku
uint32_t w; // šířka obrázku
uint32_t h; // výška obrázku
uint32_t data_size; // velikost dat obrázku v bytech
const uint8_t *data; // ukazatel na pole dat obrázku
} image_dsc_t;
// testovací obrázek
// lze získat z webu: https://lvgl.io/tools/imageconverter
// parametry: LVGL v9, Color format RGB565
const uint8_t malina_map[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x00, 0x00, 0x41, 0x08, 0xe3, 0x20, 0x00, 0x00, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0xe6, 0x5b, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0xe6, 0x5b, 0x41, 0x08, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0xe3, 0x20, 0x09, 0x96, 0xe3, 0x20, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0xe6, 0x5b, 0xe3, 0x20, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x41, 0x08, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0xe3, 0x20, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x09, 0x96, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0xe6, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe6, 0x5b, 0xe6, 0x5b, 0x09, 0x96, 0x09, 0x96, 0xe6, 0x5b, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0xe3, 0x20, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0xe3, 0x20, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x25, 0x98, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x25, 0x98, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0xe3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0xe3, 0x20, 0x03, 0x58, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x03, 0x58, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x61, 0x08, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x25, 0x98, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x20, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x08, 0x03, 0x58, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x25, 0x98, 0x03, 0x58, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x03, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x58, 0x25, 0x98, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x28, 0xc8, 0x25, 0x98, 0x25, 0x98, 0x41, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const image_dsc_t malina = {
.cf = COLOR_FORMAT_RGB565,
.w = 65,
.h = 76,
.data_size = 4940 * 2,
.data = malina_map,
};
// hardware
#define TFT_WIDTH 76
#define TFT_HEIGHT 284
#define SPI spi0
#define SPI_BAUDRATE 45000000
#define DC_PIN 20 // pokud je na pinu DC 0 posílá se příkaz, je-li 1 posílají se data
#define CS_PIN 17 // chip select pin
#define RST_PIN 15 // reset pin
#define MOSI_PIN 19
#define CLK_PIN 18
#define SCREEN_W 284
#define SCREEN_H 76
static uint8_t buffer[TFT_WIDTH * TFT_HEIGHT * 2];
// -----------------------------------------------------------------------------------------
#define MADCTL 0x36 // Memory access control command
// data pro MADCTL
/* +----------------------------------------+
* | MY | MX | MV | ML | RGB | MH | 0 | 0 |
* +----------------------------------------+
* MY Row Address Order ( 0 shora dolů, 1 zdola nahoru )
* MX Colum Address Order ( 0 zleva doprav, 1 zprava doleva )
* MV Page/Column Order ( 0 normální režim, 1 obrácený režim )
* ML Line Address Order ( 0 shora dolů, 1 zdola nahoru ) posílání řádků
* RGB pořadí barev ( 0 RGB, 1 BGR )
* MH Display Data Latch Data Order pořadí posílání dat sloupců
* ( 0 obnovení zleva doprava, 1 obnovení zprava doleva )
*/
#define MADCTL_MY 0x80
#define MADCTL_MX 0x40
#define MADCTL_MV 0x20 // Vertical addressing mode
#define MADCTL_ML 0x10
#define MADCTL_RGB 0x08
#define MADCTL_MH 0x04
// -----------------------------------------------------------------------------------------
/* 8.11 Address Control (ST7789P3 strana 86)
* Čítač adres nastavuje adresy paměti RAM s daty displeje pro zápis a čtení.
* Data se zapisují po pixelech do matice paměti RAM ovladače DRIVER.
* Shromažďují se data pro jeden nebo dva pixely (RGB 6-6-6-bit) v závislosti na datových formátech.
* Jakmile jsou tyto informace o pixelových datech kompletní, aktivuje se přístup „Zápis“ v paměti RAM.
* Umístění v paměti RAM jsou adresována ukazateli adres.
* Rozsahy adres jsou X=0 až X=239 (EFh) a Y=0 až Y=319 (13Fh).
* Adresy mimo tyto rozsahy nejsou povoleny.
* Před zápisem do paměti RAM je nutné definovat okno, do kterého se bude zapisovat.
* Okno je programovatelné pomocí příkazových registrů XS, YS, které označují počáteční adresu,
* a XE, YE, které označují koncovou adresu.
*
* Například bude zapsán celý obsah zobrazení, okno je definováno následujícími hodnotami:
* XS=0 (0h) YS=0 (0h) a XE=239 (EFh), YE=319 (13Fh).
*
* Ve vertikálním adresovacím režimu (MV=1) se adresa Y zvyšuje po každém bajtu,
* po poslední adrese Y (Y=YE) se Y zalomí na YS a X se zvýší pro adresování dalšího sloupce.
* V horizontálním adresovacím režimu (V=0) se adresa X zvýší po každém bajtu,
* po poslední adrese X (X=XE) se X zalomí na XS a Y se zvýší pro adresování dalšího řádku.
* Po každé poslední adrese (X=XE a Y=Y) se ukazatele adres zalomí na adresu (X=XS a Y=YS).
*
* Pro flexibilitu při práci s širokou škálou architektur displejů definují příkazy
* „CASET, RASET a MADCTL“ příznaky MX a MY, které umožňují zrcadlení adresy X a adresy Y.
* Jsou povoleny všechny kombinace příznaků.
* Sekce 8.12 ukazuje dostupné kombinace zápisu do RAM displeje.
* Pokud se změní MX, MY a MV, data se přepíší do RAM displeje.
*/
#define ROTATION_0 0x00 // hodnoty pro rotaci displeje
#define ROTATION_90 0x60
#define ROTATION_180 0xc0
#define ROTATION_270 0xa0
#define ROTATION ROTATION_0
// poslání příkazu do displeje
void sc(uint8_t cmd )
{
gpio_put(CS_PIN, 0);
gpio_put(DC_PIN, 0);
spi_write_blocking(SPI, &cmd, 1);
gpio_put(CS_PIN, 1);
}
// poslání dat do displeje
void sd(uint8_t cmd )
{
gpio_put(CS_PIN, 0);
gpio_put(DC_PIN, 1);
spi_write_blocking(SPI, &cmd, 1);
gpio_put(CS_PIN, 1);
}
// vynulování framebufferu
void clear_display()
{
memset(buffer, 0, sizeof(buffer));
}
// inicializace displeje
void display_init()
{
gpio_init(RST_PIN);
gpio_set_dir(RST_PIN, GPIO_OUT);
gpio_init(DC_PIN);
gpio_set_dir(DC_PIN, GPIO_OUT);
gpio_init(CS_PIN);
gpio_set_dir(CS_PIN, GPIO_OUT);
gpio_set_function(MOSI_PIN, GPIO_FUNC_SPI);
gpio_set_function(CLK_PIN, GPIO_FUNC_SPI);
gpio_put(CS_PIN, 1);
spi_init(SPI,SPI_BAUDRATE);
// reset displeje
gpio_put(CS_PIN,0);
gpio_put(RST_PIN,1);
sleep_ms(10);
gpio_put(RST_PIN,0);
sleep_ms(20);
gpio_put(RST_PIN,1);
sleep_ms(120);
gpio_put(CS_PIN,1);
// nastavení displeje
// Porch control
sc(0xb2); sd(0x0c); sd(0x0c); sd(0x00); sd(0x33), sd(0x33);
// RAM control
sc(0xb0); sd(0x00); sd(0xe0);
// nastavení orientace displeje
// Memory Data Access Control -- top to bottom, left to right, normal mode, RGB, LCD refresh left to right
// sc(0x36); sd(0x00);
sc(MADCTL); sd(0);
// InterfacePixel Format -- 16 bit/pixel 0x05 je asi špatně dám 0x55
/* COMOD 0x3a command
* +-------------------------------------+
* | 0 | D6 | D5 | D4 | 0 | D2 | D1 | D0 |
* +-------------------------------------+
* bity D6, D5, D4: 101 - 65K RGB, 110 262K RGB
* bity D2, D1, D0: 011 - 12bit/pixel, 101 - 16bit/pixel, 110 - 18bit/pixel
*/
sc(0x3a); sd(0x55);
// Gate control
sc(0xb7); sd(0x45);
// VCOMS
sc(0xbb); sd(0x1d);
// LCM control
sc(0xc0); sd(0x2c);
// VRH command enable
sc(0xc2); sd(0x01);
// VRH set
sc(0xc3); sd(0x19);
//
sc(0xc4); sd(0x20);
// FR control
sc(0xc6); sd(0x0f);
// Power control
sc(0xd0); sd(0xa4); sd(0xa1);
// Gate Output Selection in Sleep in Mode
sc(0xd6); sd(0xa1);
// Positive Voltage Gamma Control
sc(0xe0); sd(0xd0); sd(0x10); sd(0x21); sd(0x14); sd(0x15); sd(0x2d); sd(0x41); sd(0x44);sd(0x4f); sd(0x28); sd(0x0e); sd(0x0c); sd(0x1d); sd(0x1f);
// Negative voltage Gamma Control
sc(0xe1); sd(0xd0); sd(0x0f); sd(0x1b); sd(0x0d); sd(0x0d); sd(0x26); sd(0x42); sd(0x54);sd(0x50); sd(0x3e); sd(0x1a); sd(0x18); sd(0x22); sd(0x25);
//
sc(0x11);
sleep_ms(120);
sc(0x29);
clear_display();
}
// nastavení jasu displeje
#define WRDISBV 0x51
void display_brightness( uint8_t br)
{
sc(WRDISBV); sd(br);
}
#define X_OFFSET 0x12
#define Y_OFFSET 0x52
// poslání framebufferu do displeje
void show()
{
sc(MADCTL); sd(MADCTL_MV|MADCTL_MX);
sc(0x2a); // příkaz CASET nastavení adres sloupců
// data nastavení adres: XS jsou bity počíteční x adresy, XE jsou bity koncové x adresy
/* +-------------------------------------------------------+
* | XS15 | XS14 | XS13 | XS12 | XS11 | XS10 | XS09 | XS08 |
* | XS07 | XS06 | XS05 | XS04 | XS03 | XS02 | XS01 | XS00 |
* | XE15 | XE14 | XE13 | XE12 | XE11 | XE10 | XE09 | XE08 |
* | XE07 | XE06 | XE05 | XE04 | XE03 | XE02 | XE01 | XE00 |
* +-------------------------------------------------------+
*/
// počáteční adresa XS je 0x0052 (82 dec)
sd(0); // 0b00000000
sd(0+X_OFFSET); // 0b10100010
// koncová adresa XE je 0x009d (157 dec)
sd((SCREEN_W+X_OFFSET-1) >> 8); // 0x4c+0x51=0x9d 0b10011101 >> 8 => 0b00000000
sd((SCREEN_W+X_OFFSET-1) & 0xff); // 0b10011101 && 0xff => 0b10011101
// rozdíl je 75
sc(0x2b); // příkaz RASET nastavení adres řádků
// data nastavení adres: YS jsou bity počíteční y adresy, YE jsou bity koncové y adresy
/* +-------------------------------------------------------+
* | YS15 | YS14 | YS13 | YS12 | YS11 | YS10 | YS09 | YS08 |
* | YS07 | YS06 | YS05 | YS04 | YS03 | YS02 | YS01 | YS00 |
* | YE15 | YE14 | YE13 | YE12 | YE11 | YE10 | YE09 | YE08 |
* | YE07 | YE06 | YE05 | YE04 | YE03 | YE02 | YE01 | YE00 |
* +-------------------------------------------------------+
*/
sd(0);
sd(Y_OFFSET);
sd((SCREEN_H+Y_OFFSET-1) >> 8);
sd((SCREEN_H+Y_OFFSET-1) & 0xff);
sc(0x2c); // poslání dat do displeje
gpio_put(DC_PIN,1);
gpio_put(CS_PIN,0);
spi_write_blocking(SPI, buffer, SCREEN_H*SCREEN_W*2);
gpio_put(CS_PIN,1);
}
// kreslení bodu do framebufferu
// int x je souřadnice x
// int y je souřadnice y
// uint16_t color je barva ve formátu RGB
void drawPixel(int x, int y, uint16_t color)
{
if( x >= SCREEN_W || y >= SCREEN_H || x < 0 || y < 0) return;
buffer[x*2 + y*SCREEN_W*2] = color >> 8;
buffer[x*2 + y*SCREEN_W*2 + 1] = color;
}
// vyplnění framebufferu barvou pozadí
// uint16_t coler je barva pozací ve formátu RGB
void clear_screen(uint16_t color)
{
static int16_t i, j;
for(i = 0; i < SCREEN_W; i++) {
for( j = 0; j < SCREEN_H; j++) {
drawPixel(i,j,color);
}
}
}
// kreslení obrázku
// int x je vodorovná souřadnice horního rohu
// int y je svislá souřadnice horního rohu obrázku
// image_dsc_t *img_desc je popis obrázku a data obrázku (zatím jenom ve formátu RGB565)
// obrázek se musí vejít na displej
void drawImage(int x, int y, const image_dsc_t *img_desc)
{
static uint8_t *src_ptr, *dst_ptr;
static int i,j,w;
static uint8_t *p;
static uint8_t l, h;
if( x >= SCREEN_W || y >= SCREEN_H || x < 0 || y < 0) return;
if( img_desc->cf == COLOR_FORMAT_RGB565 ) {
dst_ptr = &buffer[x*2 + y*SCREEN_W*2];
src_ptr = img_desc->data;
if( x+img_desc->w < SCREEN_W ) {
for(i = y; i < SCREEN_H; i++, dst_ptr += 2*SCREEN_W, src_ptr += 2*img_desc->w) {
p = dst_ptr;
memcpy(dst_ptr, src_ptr, 2*img_desc->w);
// prohození horního a dolního bytu ve framebufferu
for(j=0; j<2*img_desc->w; j+=2, p+=2) {
l = *p;
h = *(p+1);
*p = h;
*(p+1) = l;
}
}
} else { // oříznout zprava
w = SCREEN_W - x - 1;
printf("drawImage: x=%d w=%d imgw=%d\n",x,w,img_desc->w);
for(i = y; i < SCREEN_H; i++, dst_ptr += 2*SCREEN_W, src_ptr += 2*img_desc->w) {
p = dst_ptr;
memcpy(dst_ptr, src_ptr, 2*w);
// prohození horního a dolního bytu ve framebufferu
for(j=0; j<2*w; j+=2, p+=2) {
l = *p;
h = *(p+1);
*p = h;
*(p+1) = l;
}
}
}
}
}
// kreslení čáry (Bresenham)
// parametry: x0, y0 - souřadnice 1. bodu úsečky
// x1, y1 - souřadnice 2. bodu úsečky
// color - barva úsečky ve formátu RGB
void drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t color)
{
static int dx, sx;
static int dy, sy;
static int err, e2; /* error value e_xy */
dx = abs(x1-x0);
sx = x0<x1 ? 1 : -1;
dy = -abs(y1-y0);
sy = y0<y1 ? 1 : -1;
err = dx+dy;
for(;;){ /* loop */
drawPixel(x0, y0, color);
if (x0==x1 && y0==y1) break;
e2 = 2*err;
if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
}
}
// kreslení vodorovné čáry
// parametry: x,y - souřadnice levého bodu
// len - délka v bodech
// color - barva čáry
void drawHLine( uint16_t x, uint16_t y, uint16_t len, uint16_t color )
{
static int i;
for(i=x ; i<=x+len; i++) {
drawPixel(i,y,color);
}
}
// kreslení svislé čáry
// parametry: x,y - souřadnice horního bodu
// len - výška v bodech
// color - barva čáry
void drawVLine( uint16_t x, uint16_t y, uint16_t len, uint16_t color )
{
static int i;
for(i=y ; i<=y+len; i++) {
drawPixel(x,i,color);
}
}
// kreslení plného obdélníku
// parametry: x0, y0 - souřadnice levého horního rohu
// x1, y1 - souřadnice pravého dolního rohu
// color - barva obdélníka včetně výplně
void drawSquare( int x0, int y0, int x1, int y1, uint16_t color )
{
static int x = 0;
static int y = 0;
if( x0 > x1 ) {
x = x1;
x1 = x0;
x0 = x;
}
if( y0 > y1 ) {
y = y1;
y1 = y0;
y0 = y;
}
for(x=x0 ; x <= x1; x++) {
for(y=y0; y <= y1; y++) {
drawPixel(x,y,color);
}
}
}
// kreslení prázdného obdélníku
// parametry: x,y - souřadnice levého horního rohu
// w - šířka obdélníku
// h - výška obdélníku
// color - barva obrysu obdélníka
void drawRectangle( uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color )
{
drawHLine(x , y , w, color);
drawHLine(x , y+h, w, color);
drawVLine(x , y , h, color);
drawVLine(x+w, y , h, color);
}
// kreslení kružnice
// parametry: xm, ym - souřadnice středu kružnice
// r - poloměr kružnice
// color - barva kružnice (0x0 až 0xf)
void drawCircle(int xm, int ym, int r, uint16_t color)
{
static int x, y, err;
x = -r;
y = 0;
err = 2-2*r; /* II. Quadrant */
do {
drawPixel(xm-x, ym+y, color); /* I. Quadrant */
drawPixel(xm-y, ym-x, color); /* II. Quadrant */
drawPixel(xm+x, ym-y, color); /* III. Quadrant */
drawPixel(xm+y, ym+x, color); /* IV. Quadrant */
r = err;
if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */
if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
} while (x < 0);
}
// kreslení plného kruhu
// parametry: xm, ym - souřadnice středu kruhu
// r - poloměr kruhu
// color - barva (0x0 až 0xf)
void drawFilledCircle(int xm, int ym, int r, uint16_t color)
{
static int x, y, err;
x = -r;
y = 0;
err = 2-2*r; /* II. Quadrant */
do {
drawLine(xm-x,ym+y,xm+x,ym+y, color);
drawLine(xm-x,ym-y,xm+x,ym-y, color);
r = err;
if (r <= y) err += ++y*2+1; /* e_xy+e_y < 0 */
if (r > x || err > y) err += ++x*2+1; /* e_xy+e_x > 0 or no 2nd y-step */
} while (x < 0);
}
// kreslení UTF-8 znaku
// parametry: x, y - souřadníce levého horního rohu písmenka
// Font - ukazatel na bitmapový font
// Index - UTF-8 codepoint
// fcolor - barva písma
// bcolor - barva pozadí
// vrací šířku písmena
uint16_t drawChar(uint32_t x, uint32_t y, const bitmapFONT* Font,
uint32_t Index, uint16_t fcolor, uint16_t bcolor )
{
static uint8_t row, column, forecolor;
static uint8_t col, fcol, zb;
static uint8_t chwidth;
static uint16_t chsize;
static uint32_t bindex;
static uint8_t chw;
static int j = 0;
if( x > SCREEN_W || y > SCREEN_H ) {
return 0;
}
col = Font->Width/8; // sloupce v bytech
fcol = col;
zb = Font->Width%8;
if( zb != 0) {
col ++;
}
if( Index > Font->Chars ) Index = 0;
chwidth = Font->Widths[Index]; // šířka znaku
chsize = Font->Height * col;
bindex = Index*chsize;
for (row = 0; row < Font->Height; row++ ) {
// lezeme po bytech (po osmičkách)
chw = chwidth;
for (column = 0; column < col; column++, chw-- ) {
for( j = 0; j<8; j++ ) {
if( chw == 0 ) break;
forecolor = ((uint8_t) Font->Bitmap[bindex+row*col+column]) & (0x80 >> j);
if( forecolor ) {
drawPixel(x + column*8 + j, y + row, fcolor);
} else {
drawPixel(x + column*8 + j, y + row, bcolor);
}
}
}
}
return chwidth;
}
// nalezení indexu pole znaků metodou půlení intervalu
// const bitmapFONT *font je ukazatel na strukturu fontu
// uint32_t je UTF-8 codepoint
int32_t getUTF8Index(const bitmapFONT *font, uint32_t codepoint)
{
static int32_t l = 0;
static int32_t r = 0;
static int32_t m = 0;
for(l=0, r= font->Chars; l<=r; ) {
m = (l+r)/2;
if(font->Index[m] == codepoint ) return m;
if(codepoint < font->Index[m] ) { // jdeme vlevo
r = m - 1;
} else { // jdeme vpravo
l = m + 1;
}
}
return -1;
}
// kreslení UTF-8 řetězce
// parametry: x,y - levý horní roh řetězce
// *Font - ukazatel na bitmapový font
// *pstring - ukazatel naa řetězec (UTF-8)
// fcolor - barva písma (0x0 - 0xf)
// bcolor - barva pozadí (0x0 - 0xf)
// vrací šířku řetězce zabranou na displeji v bodech
uint16_t drawString(uint32_t x, uint32_t y, const bitmapFONT * Font,
const char * pString, uint16_t fcolor, uint16_t bcolor )
{
static uint32_t Xpoint;
static uint32_t Ypoint;
static uint16_t index;
static uint16_t str_width = 0;
static uint16_t char_width = 0;
static uint32_t codepoint;
static int i = 0;
Xpoint = x;
Ypoint = y;
str_width = 0;
char_width = 0;
if (x > SCREEN_W || y > SCREEN_H) {
return 0;
}
utf8_string ustr = make_utf8_string(pString);
utf8_char_iter iter = make_utf8_char_iter(ustr);
utf8_char c;
while ((c = next_utf8_char(&iter)).byte_len > 0 ) {
codepoint = unicode_code_point(c);
index = 0;
if( (index = getUTF8Index(Font, codepoint)) == -1) continue; // nemáme znak ve fontu
// test zda se to vejde na displej
if((Xpoint + Font->Width ) > SCREEN_W ) {
// zalomíme na další řádek
Xpoint = x;
Ypoint += Font->Height;
}
if ((Ypoint + Font->Height ) > SCREEN_H ) {
// už se to nikam nevejde
return str_width;
// Xpoint = x;
// Ypoint = y;
}
char_width = drawChar(Xpoint, Ypoint, Font, index, fcolor, bcolor);
Xpoint += char_width;
str_width += char_width;
}
return str_width;
}
// --- konec knihovny ------------------------------------------------------------------------
// test
#define RANDOM_MIN 0
#define RANDOM_MAX 255
#define SINUS_TEST 1
#define FONT_10x20_TEST 1
extern bitmapFONT font_spleen_16x32;
extern bitmapFONT font_spleen_8x16;
extern bitmapFONT font_spleen_6x12;
extern bitmapFONT font_10x20;
int main( void )
{
int i,j,k;
int x,y,z;
char buf[128];
uint64_t t;
stdio_init_all();
sleep_ms(2000);
printf("Test displeje ST7789P3 76x284\n");
display_init();
display_brightness(0x08);
clear_screen(BLACK);
drawString(2,2,&font_spleen_8x16,"Test displeje ST7789P3 76x284 na šířku. (c) Jirka Chráska 2026.",Gold, BLACK);
show();
sleep_ms(2000);
printf("Fialová obrazovka.\n");
clear_screen(PURPLE);
drawString(2,2,&font_spleen_16x32,"Fialová.",LightGray, PURPLE);
show();
sleep_ms(2000);
printf("LimeGreen obrazovka.\n");
clear_screen(LimeGreen);
drawString(2,2,&font_spleen_16x32,"LimeGreen.", BLACK, LimeGreen);
show();
sleep_ms(2000);
printf("SteelBlue obrazovka.\n");
clear_screen(SteelBlue);
drawString(2,2,&font_spleen_16x32,"SteelBlue.", WHITE, SteelBlue);
show();
sleep_ms(2000);
printf("Červená obrazovka (IndianRed).\n");
clear_screen(IndianRed);
drawString(2,2,&font_spleen_16x32,"IndianRed.", WHITE, IndianRed);
show();
sleep_ms(2000);
printf("Bílá obrazovka.\n");
clear_screen(WHITE);
drawString(2,2,&font_spleen_16x32,"Bílá.", BLACK, WHITE);
show();
sleep_ms(2000);
printf("Černá obrazovka.\n");
clear_screen(BLACK);
drawString(2,2,&font_spleen_16x32,"Černá.", WHITE, BLACK);
show();
sleep_ms(2000);
// testování obrázku
printf("Obrázek maliny.\n");
clear_screen(BLACK);
drawImage(0,0,&malina);
show();
drawImage(66,0,&malina);
show();
drawImage(132,0,&malina);
drawString(200,0,&font_spleen_8x16,"maliny",IndianRed,BLACK);
show();
drawImage(240,20,&malina);
show();
sleep_ms(10000);
printf("Čáry.\n");
clear_screen(BLACK);
for( x=0; x<SCREEN_W; x++) {
drawPixel(x,0,YELLOW);
drawPixel(x,1,Gold);
drawPixel(x,2,YELLOW);
drawPixel(x,SCREEN_H-1,YELLOW);
drawPixel(x,SCREEN_H-2,Gold);
drawPixel(x,SCREEN_H-3,YELLOW);
}
for( y=0; y<SCREEN_H; y++) {
drawPixel(0,y,MediumSeaGreen);
drawPixel(1,y,SeaGreen);
drawPixel(2,y,ForestGreen);
drawPixel(SCREEN_W-1,y,MediumSeaGreen);
drawPixel(SCREEN_W-2,y,SeaGreen);
drawPixel(SCREEN_W-3,y,ForestGreen);
}
show();
drawString(4,4,&font_spleen_16x32,"(c) Jirka Chráska 2026.",DodgerBlue,BLACK);
show();
for( x=SCREEN_W/2; x<SCREEN_W; x++) {
drawLine(SCREEN_W/2, SCREEN_H/2, x-3, SCREEN_H-3,OrangeRed);
show();
}
for( y=SCREEN_H; y>=SCREEN_H/2; y--) {
drawLine(SCREEN_W/2, SCREEN_H/2, SCREEN_W-3, y-3, Coral);
show();
}
sleep_ms(500);
for( x=SCREEN_W/2; x<SCREEN_W; x++) {
drawLine(SCREEN_W/2, SCREEN_H/2, x-3, SCREEN_H-3, BLACK);
show();
}
for( y=SCREEN_H; y>=SCREEN_H/2; y--) {
drawLine(SCREEN_W/2, SCREEN_H/2, SCREEN_W-3, y-3, BLACK);
show();
}
sleep_ms(2000);
#if SINUS_TEST
// -----------------------------------------------------------
// sinusovka
printf("Sinusovka a kosinusovka.\n");
clear_screen(BLACK);
show();
drawString(10,76-16,&font_spleen_8x16,"y=sin(x)", OrangeRed,BLACK);
drawString(100,10,&font_spleen_8x16,"y=cos(x)", DodgerBlue,BLACK);
// popisky os
drawString(3,2,&font_spleen_6x12,"y",Gainsboro,BLACK);
drawString(278,38,&font_spleen_6x12,"x",Gainsboro,BLACK);
// osy
drawLine(0,0,0,127,Gainsboro);
drawLine(0,38,283,38,Gainsboro);
// sinusovka a kosinusovka
for( x=0; x<284; x++) {
y = (-(sin(x*3.1415926/100) * 38)+38);
drawPixel(x,y,OrangeRed);
y = (-(cos(x*3.1415926/100) * 38)+38);
drawPixel(x,y,DodgerBlue);
if(x==50) {
drawLine(x,36,x,40,Gray);
drawString(x+1,42,&font_spleen_8x16,"π/2",Gray,BLACK);
}
if(x==100) {
drawLine(x,36,x,40,Gray);
drawString(x+10,42,&font_spleen_8x16,"π",Gray,BLACK);
}
if(x==150) {
drawLine(x,36,x,40,Gray);
drawString(x+1,42,&font_spleen_8x16,"3π/2",Gray,BLACK);
}
if(x==200) {
drawLine(x,36,x,40,Gray);
drawString(x+1,42,&font_spleen_8x16,"2π",Gray,BLACK);
}
if(x==250) {
drawLine(x,36,x,40,Gray);
drawString(x+1,42,&font_spleen_8x16,"5π/2",Gray,BLACK);
}
// občerstvujeme displej po 4 nakreslených bodech
x%4==0?show():sleep_us(1);
}
sleep_ms(5000);
#endif
// -----------------------------------------------------------
// test fontu font_10x20
#if FONT_10x20_TEST
printf("font_10x20 test.\n");
for( k=0; k<font_10x20.Chars; k++) {
t = time_us_64();
clear_screen(BLACK);
drawChar(1,1,&font_10x20,k,Gold,DarkSlateBlue);
sprintf(buf,"font_10x20", k);
drawString(40,1,&font_10x20,buf,Gold,BLACK);
sprintf(buf,"Character index: %d", k);
drawString(0,26,&font_spleen_6x12,buf,WHITE,BLACK);
sprintf(buf,"UTF-8 codepoint: %04lx", font_10x20.Index[k]);
drawString(0,38,&font_spleen_6x12,buf,WHITE,BLACK);
sprintf(buf,"Počet znaků fontu: %d", font_10x20.Chars);
drawString(136,38,&font_spleen_6x12,buf,DodgerBlue,BLACK);
if( k < font_10x20.Chars) {
for(i=0; i<27; i++) {
drawChar(0+i*10,76-20,&font_10x20,k+i,Gold,DarkSlateBlue);
}
}
show();
t = time_us_64() - t;
sprintf(buf,"Kreslení trvalo %5.2f ms",(float)t/1000.0);
drawString(136, 26, &font_spleen_6x12, buf, DodgerBlue,BLACK);
show();
sleep_ms(500);
}
#endif
// test kružnic
printf("Kružnice\n");
clear_screen(BLACK);
drawString(180,0,&font_spleen_8x16,"Kružnice",Gold,BLACK);
// kružnice zvětšuje svůj poloměr
for(int r=10; r<64; r++) {
drawCircle(127,64,r,RoyalBlue);
sprintf(buf,"drawCircle(%d, %d, %d, RoyalBlue); ",127,64,r);
drawString(0,115,&font_spleen_6x12,buf, Gold, BLACK);
show();
}
sleep_ms(3000);
// kružnice zmenšuje svůj poloměr
for(int r=63; r>10; r--) {
drawCircle(127,64,r,BLACK);
sprintf(buf,"drawCircle(%d, %d, %d, BLACK); ",127,64,r);
drawString(0,115,&font_spleen_6x12,buf, Gold, BLACK);
show();
}
sleep_ms(3000);
t = time_us_64();
srand((unsigned int) t);
uint8_t r,g,b;
for(int i=0; i<256; i++) {
for(int j=0; j<256; j++) {
for(int k=0; k<256; k++) {
t = time_us_64();
r = rand() % (RANDOM_MAX + 1 - RANDOM_MIN) + RANDOM_MIN;
g = rand() % (RANDOM_MAX + 1 - RANDOM_MIN) + RANDOM_MIN;
b = rand() % (RANDOM_MAX + 1 - RANDOM_MIN) + RANDOM_MIN;
clear_screen(RGB(r,g,b));
drawString(2,2,&font_10x20,"Sláva Bohu za všechno!",
RGB(255-r,255-g,255-b),RGB(r,g,b));
show();
t = time_us_64() - t;
sprintf(buf,"R=%3u,G=%3u,B=%3u trvalo %lld μs.",r,g,b,t);
drawString(2, 76-16, &font_spleen_8x16, buf,
RGB(255-r,255-g,255-b),RGB(r,g,b));
show();
sleep_ms(800);
}
}
}
}
utf8.h
/**
* @file utf8.h
* @brief simple library for working with UTF-8 encoded strings
*
* @code
* #include "utf8.h"
* #include <stdio.h>
*
* int main() {
* const char* str = "Hello, こんにちは, Здравствуйте";
* utf8_string ustr = make_utf8_string(str);
* utf8_string_slice slice = make_utf8_string_slice(ustr, 2, 11);
* utf8_char_iter iter = make_utf8_char_iter(ustr);
*
* printf("string: %s\n", ustr.str);
* printf("slice: %.*s\n", (int)slice.byte_len, slice.str);
*
* utf8_char ch;
* while ((ch = next_utf8_char(&iter)).byte_len > 0) {
* printf("character: %.*s\t", (int)ch.byte_len, ch.str);
* printf("unicode code point: U+%04X\n", unicode_code_point(ch));
* }
*
* return 0;
* }
* @endcode
*/
#ifndef ZAHASH_UTF8_H
#define ZAHASH_UTF8_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* @brief Represents the validity of a UTF-8 encoded string.
*
* @details The `utf8_validity` struct indicates whether a given UTF-8 encoded string is valid or not,
* along with the position up to which it is valid.
*
* - Invalid case: "hello\xC0\xC0" => { .valid = false, .valid_upto = 5 }
* - Valid case: "hello world" => { .valid = true, .valid_upto = 11 }
*/
typedef struct {
bool valid; ///< Flag indicating the validity of the UTF-8 string.
size_t valid_upto; ///< The position up to which the string is valid.
} utf8_validity;
/**
* @brief Represents a non-owning UTF-8 encoded string. (just a wrapper type).
*
* @details The `utf8_string` struct holds a pointer to a UTF-8 encoded string along with its byte length,
*/
typedef struct {
const char* str; ///< Pointer to the UTF-8 encoded string.
size_t byte_len; ///< Byte length of the UTF-8 string ('\0' not counted).
} utf8_string;
/**
* @brief Represents a UTF-8 encoded string that fully owns its data.
*
* @details The `owned_utf8_string` struct holds a pointer to a UTF-8 encoded string that is dynamically allocated
* and therefore is owned by the struct, which means the caller is responsible for freeing the memory when
* it is no longer needed using the `free_owned_utf8_string` function.
*/
typedef struct {
char* str; ///< Pointer to the UTF-8 encoded string (owned). This memory is dynamically allocated.
size_t byte_len; ///< Byte length of the UTF-8 string ('\0' not counted).
} owned_utf8_string;
/**
* @brief Represents an iterator for traversing UTF-8 characters in a string.
*
* @details The `utf8_char_iter` struct serves as an iterator for traversing UTF-8 characters
* within a UTF-8 encoded string.
*/
typedef struct {
const char* str; ///< Pointer to the current position of the iterator.
} utf8_char_iter;
/**
* @brief Represents a UTF-8 character.
*
* @details The `utf8_char` struct encapsulates a UTF-8 character, including its pointer and byte length.
* The byte length represents the number of bytes occupied by the UTF-8 character.
*/
typedef struct {
const char* str; ///< Pointer to the UTF-8 character.
uint8_t byte_len; ///< Byte length of the UTF-8 character.
} utf8_char;
/**
* @brief Validates whether a given string is UTF-8 compliant in O(n) time.
*
* @param str The input string to validate.
* @return The validity of the UTF-8 string along with the position up to which it is valid.
*/
utf8_validity validate_utf8(const char* str);
/**
* @brief Wraps a C-style string in a UTF-8 string structure after verifying its UTF-8 compliance.
*
* @param str The input C-style string to wrap.
* @return A UTF-8 string structure containing the wrapped string if valid; otherwise, a structure with NULL string pointer.
*
* @code
* // Example usage:
* const char *str = "definitely utf8 string こんにちは नमस्ते Здравствуйте";
* utf8_string ustr = make_utf8_string(str);
* assert( ustr.str != NULL );
*
* const char *s = "non-utf8 sequence \xC0\xC0";
* utf8_string ustr = make_utf8_string(str);
* assert( ustr.str == NULL );
* @endcode
*/
utf8_string make_utf8_string(const char* str);
/**
* @brief Converts a C-style string to a UTF-8 string, replacing invalid sequences with U+FFFD REPLACEMENT CHARACTER (�).
*
* @details It takes a C-style string as input and converts it to a UTF-8 encoded string.
* Any invalid UTF-8 sequences in the input string are replaced with the U+FFFD REPLACEMENT CHARACTER (�) to ensure
* that the resulting string is valid UTF-8. The resulting string is dynamically allocated and the caller
* is responsible for freeing the memory when no longer needed using `free_owned_utf8_string`.
*
* @param str The input C-style string to convert. The string can contain invalid UTF-8 sequences.
* @return An `owned_utf8_string` structure containing the resulting UTF-8 string. If memory allocation fails, the structure
* will contain a `NULL` pointer and a `byte_len` of 0.
*
* @code
* // Example usage:
* const char* str = "hello\xC0\xC0 world!";
* owned_utf8_string owned_ustr = make_utf8_string_lossy(str);
* @endcode
*/
owned_utf8_string make_utf8_string_lossy(const char* str);
/**
* @brief Creates the non-owning UTF-8 encoded string `utf8_string` from an `owned_utf8_string`.
*
* @details The resulting `utf8_string` will point to the same underlying string without taking ownership.
* The caller must ensure the original `owned_utf8_string` remains valid as long as the reference is used.
*
* @param owned_str The owned UTF-8 string from which to create a non-owning reference.
* @return utf8_string A non-owning UTF-8 string reference (`utf8_string`) pointing to the same data.
*
* @note This function does not free or transfer ownership of the `owned_utf8_string`.
* The caller is responsible for managing the lifetime of the owned string.
*/
utf8_string as_utf8_string(const owned_utf8_string* owned_str);
/**
* @brief Frees the memory allocated for an `owned_utf8_string`.
*
* @details The `free_owned_utf8_string` function deallocates the memory used by an `owned_utf8_string`
* and sets the `str` pointer to `NULL` and `byte_len` to 0.
*
* @param owned_str A pointer to the `owned_utf8_string` structure to be freed.
*
* @code
* // Example usage:
* owned_utf8_string owned_ustr = make_utf8_string_lossy("hello\xC0\xC0 world!");
* free_owned_utf8_string(&owned_ustr);
* @endcode
*/
void free_owned_utf8_string(owned_utf8_string* owned_str);
/**
* @brief Creates a UTF-8 string slice from a specified range of bytes in the original string.
*
* @param ustr The original UTF-8 string.
* @param byte_index The starting byte index of the slice.
* @param byte_len The byte length of the slice.
* @return A UTF-8 string representing the specified byte range [offset, offset + byte_len) if valid (range between UTF-8 char boundaries);
* otherwise { .str = NULL, .byte_len = 0 }
*
* @note if `byte_index` >= strlen(ustr.str) then returns terminating '\0' of ustr.str { .str = '\0', .byte_len = 0 }
* @note if `byte_index` + `byte_len` >= strlen(ustr.str) then only chars till terminating '\0' are considered.
*/
utf8_string slice_utf8_string(utf8_string ustr, size_t byte_index, size_t byte_len);
/**
* @brief Creates an iterator for traversing UTF-8 characters within a string. (see next_utf8_char( .. ) for traversal)
*
* @param ustr The UTF-8 string to iterate over.
* @return An iterator structure initialized to the start of the string.
*/
utf8_char_iter make_utf8_char_iter(utf8_string ustr);
/**
* @brief Retrieves the next UTF-8 character from the iterator.
*
* @param iter Pointer to the UTF-8 character iterator.
* @return The next UTF-8 character from the iterator.
* @note If the iterator reaches the end, it keeps returning terminating '\0' of iter.str { .str = '\0', .byte_len = 0 }
*/
utf8_char next_utf8_char(utf8_char_iter* iter);
/**
* @brief Retrieves the UTF-8 character at the specified character index within a UTF-8 string in O(n) time.
*
* @details The `nth_utf8_char` function returns the UTF-8 character located at the specified character index
* within the given UTF-8 string. The character index is zero-based, indicating the position of
* the character in the string. If the index is out of bounds or invalid, the function returns
* { .str = NULL, .byte_len = 0 }
*
* @param ustr The UTF-8 string from which to retrieve the character.
* @param char_index The zero-based index of the character to retrieve.
* @return The UTF-8 character at the specified index within the string.
*
* @code
* // Example usage:
* utf8_string str = make_utf8_string("Hello Здравствуйте こんにちは");
* utf8_char char_at_index = nth_utf8_char(str, 7); // д
* @endcode
*/
utf8_char nth_utf8_char(utf8_string ustr, size_t char_index);
/**
* @brief Counts the number of UTF-8 characters in the given utf8_string.
*
* @param ustr The UTF-8 string whose characters are to be counted.
* @return The total number of characters in the UTF-8 string.
*/
size_t utf8_char_count(utf8_string ustr);
/**
* @brief Checks if a given byte is the start of a UTF-8 character. ('\0' is also a valid character boundary)
*
* @param str Pointer to the byte to check.
* @return `true` if the byte is the start of a UTF-8 character; otherwise, `false`.
*/
bool is_utf8_char_boundary(const char* str);
/**
* @brief Converts a UTF-8 character to its corresponding Unicode code point (which is the same as a UTF-32 value).
*
* @param uchar The UTF-8 character to convert.
* @return The Unicode code point.
*/
uint32_t unicode_code_point(utf8_char uchar);
#endif
utf8.c
#include "utf8.h"
#include <stdlib.h>
#include <string.h>
typedef struct {
bool valid;
size_t next_offset;
} utf8_char_validity;
utf8_char_validity validate_utf8_char(const char* str, size_t offset) {
// Single-byte UTF-8 characters have the form 0xxxxxxx
if (((uint8_t)str[offset] & 0b10000000) == 0b00000000)
return (utf8_char_validity) { .valid = true, .next_offset = offset + 1 };
// Two-byte UTF-8 characters have the form 110xxxxx 10xxxxxx
if (((uint8_t)str[offset + 0] & 0b11100000) == 0b11000000 &&
((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000) {
// Check for overlong encoding
// 0(xxxxxxx)
// 0(1111111)
// 110(xxxxx) 10(xxxxxx)
// 110(00001) 10(111111)
// 110(00010) 10(000000)
if (((uint8_t)str[offset] & 0b00011111) < 0b00000010)
return (utf8_char_validity) { .valid = false, .next_offset = offset };
return (utf8_char_validity) { .valid = true, .next_offset = offset + 2 };
}
// Three-byte UTF-8 characters have the form 1110xxxx 10xxxxxx 10xxxxxx
if (((uint8_t)str[offset + 0] & 0b11110000) == 0b11100000 &&
((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000 &&
((uint8_t)str[offset + 2] & 0b11000000) == 0b10000000) {
// Check for overlong encoding
// 110(xxxxx) 10(xxxxxx)
// 110(11111) 10(111111)
// 1110(xxxx) 10(xxxxxx) 10(xxxxxx)
// 1110(0000) 10(011111) 10(111111)
// 1110(0000) 10(100000) 10(000000)
if (((uint8_t)str[offset + 0] & 0b00001111) == 0b00000000 &&
((uint8_t)str[offset + 1] & 0b00111111) < 0b00100000)
return (utf8_char_validity) { .valid = false, .next_offset = offset };
// Reject UTF-16 surrogates
// U+D800 to U+DFFF
// 1110(1101) 10(100000) 10(000000) ED A0 80 to 1110(1101) 10(111111) 10(111111) ED BF BF
if ((uint8_t)str[offset + 0] == 0b11101101 &&
(uint8_t)str[offset + 1] >= 0b10100000 &&
(uint8_t)str[offset + 1] <= 0b10111111)
return (utf8_char_validity) { .valid = false, .next_offset = offset };
return (utf8_char_validity) { .valid = true, .next_offset = offset + 3 };
}
// Four-byte UTF-8 characters have the form 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
if (((uint8_t)str[offset + 0] & 0b11111000) == 0b11110000 &&
((uint8_t)str[offset + 1] & 0b11000000) == 0b10000000 &&
((uint8_t)str[offset + 2] & 0b11000000) == 0b10000000 &&
((uint8_t)str[offset + 3] & 0b11000000) == 0b10000000) {
// Check for overlong encoding
// 1110(xxxx) 10(xxxxxx) 10(xxxxxx)
// 1110(1111) 10(111111) 10(111111)
// 11110(xxx) 10(xxxxxx) 10(xxxxxx) 10(xxxxxx)
// 11110(000) 10(001111) 10(111111) 10(111111)
// 11110(000) 10(010000) 10(000000) 10(000000)
if (((uint8_t)str[offset + 0] & 0b00000111) == 0b00000000 &&
((uint8_t)str[offset + 1] & 0b00111111) < 0b00010000)
return (utf8_char_validity) { .valid = false, .next_offset = offset };
return (utf8_char_validity) { .valid = true, .next_offset = offset + 4 };
}
return (utf8_char_validity) { .valid = false, .next_offset = offset };
}
utf8_validity validate_utf8(const char* str) {
if (str == NULL) return (utf8_validity) { .valid = false, .valid_upto = 0 };
size_t offset = 0;
utf8_char_validity char_validity;
while (str[offset] != '\0') {
char_validity = validate_utf8_char(str, offset);
if (char_validity.valid) offset = char_validity.next_offset;
else return (utf8_validity) { .valid = false, .valid_upto = offset };
}
return (utf8_validity) { .valid = true, .valid_upto = offset };
}
utf8_string make_utf8_string(const char* str) {
utf8_validity validity = validate_utf8(str);
if (validity.valid) return (utf8_string) { .str = str, .byte_len = validity.valid_upto };
return (utf8_string) { .str = NULL, .byte_len = 0 };
}
owned_utf8_string make_utf8_string_lossy(const char* str) {
if (str == NULL) return (owned_utf8_string) { .str = NULL, .byte_len = 0 };
size_t len = strlen(str);
// Worst case scenario: every byte is invalid and is replaced with 3 bytes for U+FFFD
size_t worst_case_size = len * 3 + 1;
// Allocate buffer for the lossy UTF-8 string
char* buffer = (char*)malloc(worst_case_size);
if (!buffer) return (owned_utf8_string) { .str = NULL, .byte_len = 0 }; // failed allocation
size_t buffer_offset = 0;
size_t offset = 0;
utf8_char_validity char_validity;
while (offset < len) {
char_validity = validate_utf8_char(str, offset);
if (char_validity.valid) {
// Copy valid UTF-8 character sequence to the buffer
size_t char_len = char_validity.next_offset - offset;
memcpy(buffer + buffer_offset, str + offset, char_len);
buffer_offset += char_len;
offset = char_validity.next_offset;
} else {
// Insert the UTF-8 bytes for U+FFFD (�)
// FFFD = 1111111111111101
// = (1111) (111111) (111101)
// = 1110(1111) 10(111111) 10(111101)
// = EF BF BD
buffer[buffer_offset++] = 0xEF;
buffer[buffer_offset++] = 0xBF;
buffer[buffer_offset++] = 0xBD;
offset++;
}
}
buffer[buffer_offset] = '\0';
return (owned_utf8_string) { .str = buffer, .byte_len = buffer_offset };
}
utf8_string as_utf8_string(const owned_utf8_string* owned_str) {
return (utf8_string) { .str = owned_str->str, .byte_len = owned_str->byte_len };
}
void free_owned_utf8_string(owned_utf8_string* owned_str) {
if (owned_str->str) {
free(owned_str->str);
owned_str->str = NULL;
owned_str->byte_len = 0;
}
}
utf8_char_iter make_utf8_char_iter(utf8_string ustr) {
return (utf8_char_iter) { .str = ustr.str };
}
bool is_utf8_char_boundary(const char* str) {
return (uint8_t)*str <= 0b01111111 || (uint8_t)*str >= 0b11000000;
}
utf8_string slice_utf8_string(utf8_string ustr, size_t start_byte_index, size_t byte_len) {
if (start_byte_index > ustr.byte_len) start_byte_index = ustr.byte_len;
size_t excl_end_byte_index = start_byte_index + byte_len;
if (excl_end_byte_index > ustr.byte_len) excl_end_byte_index = ustr.byte_len;
if (is_utf8_char_boundary(ustr.str + start_byte_index) && is_utf8_char_boundary(ustr.str + excl_end_byte_index))
return (utf8_string) { .str = ustr.str + start_byte_index, .byte_len = excl_end_byte_index - start_byte_index };
return (utf8_string) { .str = NULL, .byte_len = 0 };
}
utf8_char next_utf8_char(utf8_char_iter* iter) {
if (*iter->str == '\0') return (utf8_char) { .str = iter->str, .byte_len = 0 };
// iter->str is at the current char's starting byte (char boundary).
const char* curr_boundary = iter->str;
iter->str++;
uint8_t byte_len = 1;
// find the next char's starting byte (next char boundary) and set the iter->str to that.
while (!is_utf8_char_boundary(iter->str)) {
iter->str++;
byte_len++;
}
return (utf8_char) { .str = curr_boundary, .byte_len = byte_len };
}
utf8_char nth_utf8_char(utf8_string ustr, size_t char_index) {
utf8_char_iter iter = make_utf8_char_iter(ustr);
utf8_char ch;
while ((ch = next_utf8_char(&iter)).byte_len != 0 && char_index-- != 0) {}
if (ch.byte_len == 0) return (utf8_char) { .str = NULL, .byte_len = 0 };
return ch;
}
size_t utf8_char_count(utf8_string ustr) {
utf8_char_iter iter = make_utf8_char_iter(ustr);
size_t count = 0;
while (next_utf8_char(&iter).byte_len > 0) count++;
return count;
}
uint32_t unicode_code_point(utf8_char uchar) {
switch (uchar.byte_len) {
case 1: return uchar.str[0] & 0b01111111;
case 2: return
(uchar.str[0] & 0b00011111) << 6 |
(uchar.str[1] & 0b00111111);
case 3: return
(uchar.str[0] & 0b00001111) << 12 |
(uchar.str[1] & 0b00111111) << 6 |
(uchar.str[2] & 0b00111111);
case 4: return
(uchar.str[0] & 0b00000111) << 18 |
(uchar.str[1] & 0b00111111) << 12 |
(uchar.str[2] & 0b00111111) << 6 |
(uchar.str[3] & 0b00111111);
}
return 0; // unreachable
}