From 0e95a0a1bb95d5aeec35599e0391af3e8a6865f3 Mon Sep 17 00:00:00 2001 From: CrispyPin Date: Fri, 12 Apr 2024 22:26:24 +0200 Subject: [PATCH] implement ch32v003 decoder --- .vscode/c_cpp_properties.json | 20 + ch32_decoder/Makefile | 11 + ch32_decoder/bad_apple.c | 248 ++++++++++ ch32_decoder/data.h | 836 ++++++++++++++++++++++++++++++++++ ch32_decoder/funconfig.h | 7 + ch32_decoder/gdbinit | 2 + ch32_decoder/ssd1306.h | 586 ++++++++++++++++++++++++ ch32_decoder/ssd1306_i2c.h | 363 +++++++++++++++ encoder/src/main.rs | 13 +- 9 files changed, 2082 insertions(+), 4 deletions(-) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 ch32_decoder/Makefile create mode 100644 ch32_decoder/bad_apple.c create mode 100644 ch32_decoder/data.h create mode 100644 ch32_decoder/funconfig.h create mode 100644 ch32_decoder/gdbinit create mode 100644 ch32_decoder/ssd1306.h create mode 100644 ch32_decoder/ssd1306_i2c.h diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..ea6fd34 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../ch32v003fun/ch32v003fun" + ], + "defines": [], + "compilerPath": "/usr/bin/clang", + "cppStandard": "c++14", + "intelliSenseMode": "linux-clang-x64", + "compilerArgs": [ + "-DCH32V003FUN_BASE" + ], + "configurationProvider": "ms-vscode.makefile-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/ch32_decoder/Makefile b/ch32_decoder/Makefile new file mode 100644 index 0000000..fad28f2 --- /dev/null +++ b/ch32_decoder/Makefile @@ -0,0 +1,11 @@ +all : flash + +TARGET:=bad_apple +CH32V003FUN:=../../ch32v003fun/ch32v003fun + +include ../../ch32v003fun/ch32v003fun/ch32v003fun.mk + +flash : build + gdb-multiarch -x gdbinit -ex 'load' -ex 'detach' -ex 'quit' $(TARGET).elf +clean : cv_clean + diff --git a/ch32_decoder/bad_apple.c b/ch32_decoder/bad_apple.c new file mode 100644 index 0000000..7497819 --- /dev/null +++ b/ch32_decoder/bad_apple.c @@ -0,0 +1,248 @@ + +#define SSD1306_128X32 +#include "ch32v003fun.h" +#include +#include "ssd1306_i2c.h" +#include "ssd1306.h" + +#include "data.h" + +#define PIN_LED PC4 +#define PIN_BTN PC7 + +#define WIDTH 40 +#define HEIGHT 32 +#define X_OFFSET ((128 - WIDTH) / 2) + +void decode_next_frame(); +void refresh_screen(); +u32 reader = 0; + +u32 frame[WIDTH]; + +int main() +{ + SystemInit(); + // funGpioInitAll(); + // funPinMode(PIN_LED, GPIO_CFGLR_OUT_10Mhz_PP); + // funPinMode(PIN_BTN, GPIO_CFGLR_IN_PUPD); + + // initialize OLED + ssd1306_i2c_init(); + // not using ssd1306_init because it does more than i want + uint8_t *cmd_list = (uint8_t *)ssd1306_init_array; + while (*cmd_list != SSD1306_TERMINATE_CMDS) + ssd1306_cmd(*cmd_list++); + + while (1) + { + decode_next_frame(); + refresh_screen(); + Delay_Ms(300); + if (reader >= sizeof(video)) + reader = 0; + } +} + +void refresh_screen() +{ + ssd1306_cmd(SSD1306_COLUMNADDR); + ssd1306_cmd(X_OFFSET); // Column start address (0 = reset) + ssd1306_cmd(X_OFFSET + WIDTH - 1); // Column end address (127 = reset) + + ssd1306_cmd(SSD1306_PAGEADDR); + ssd1306_cmd(0); // Page start address (0 = reset) + ssd1306_cmd(7); // Page end address + + u8 packet[SSD1306_PSZ]; + u8 x; + u8 packet_index; + u8 page = 0; + while (page < 8) + { + /* odd rows are unused so each nybble is expanded */ + packet[packet_index] = expand[(frame[x] >> (page * 4)) & 0xf]; + x++; + packet_index++; + if (x == WIDTH) + { + x = 0; + page++; + } + if (packet_index == SSD1306_PSZ) + { + ssd1306_data(packet, SSD1306_PSZ); + packet_index = 0; + } + } +} + +u8 next_byte() +{ + u8 byte = video[reader]; + reader += 1; + return byte; +} + +void fill_frame(u8 color) +{ + memset(frame, color ? 0xFFFFFFFF : 0x00, sizeof(frame)); +} + +void set_pixel(u8 x, u8 y, u8 color) +{ + if (color) + frame[x] |= (1 << (y & 31)); + else + frame[x] &= ~(1 << (y & 31)); +} + +void rle_horizontal(); +void rle_vertical(); +void bg_strips_h(); +void cell_diff_8v(); + +void decode_next_frame() +{ + Encoding_t encoding = next_byte(); + switch (encoding) + { +#ifdef USE_FillBlack + case Encoding_FillBlack: + fill_frame(0); + break; +#endif +#ifdef USE_FillWhite + case Encoding_FillWhite: + fill_frame(1); + break; +#endif +#ifdef USE_RLEHorizontal + case Encoding_RLEHorizontal: + rle_horizontal(); + break; +#endif +#ifdef USE_RLEVertical + case Encoding_RLEVertical: + rle_vertical(); + break; +#endif +#ifdef USE_BGStripsH + case Encoding_BGStripsH: + bg_strips_h(); + break; +#endif +#ifdef USE_CellDiff8V + case Encoding_CellDiff8V: + cell_diff_8v(); + break; +#endif + default: + memset(frame, 0b10101010, sizeof(frame)); + break; + } +} + +#ifdef USE_RLEHorizontal +void rle_horizontal() +{ + u8 x = 0; + u8 y = 0; + u8 color = 0; + while (y < HEIGHT) + { + u8 run = next_byte(); + for (int i = 0; i < run; i++) + { + set_pixel(x, y, color); + x += 1; + if (x == WIDTH) + { + x = 0; + y += 1; + } + } + color = 1 - color; + } +} +#endif + +#ifdef USE_RLEVertical +void rle_vertical() +{ + u8 x = 0; + u8 y = 0; + u8 color = 0; + while (x < WIDTH) + { + u8 run = next_byte(); + for (int i = 0; i < run; i++) + { + set_pixel(x, y, color); + y += 1; + if (y == HEIGHT) + { + y = 0; + x += 1; + } + } + color = 1 - color; + } +} +#endif + +#ifdef USE_BGStripsH +void bg_strips_h() +{ + u8 head = next_byte(); + u8 bg = head >> 7; + u8 fg = 1 - bg; + fill_frame(bg); + u8 strip_count = head & 127; + for (u8 i = 0; i < strip_count; i++) + { + u16 packed = (next_byte() << 8) | next_byte(); + u8 y = packed >> 11; + u8 x_start = (packed >> 5) & 0x3f; + u8 width = packed & 0x1f; + for (int x = x_start; x < (x_start + width); x++) + { + set_pixel(x, y, fg); + } + } +} +#endif + +#ifdef USE_CellDiff8V +void cell_diff_8v() +{ + u32 bitmap = (next_byte() << 16) | (next_byte() << 8) | next_byte(); + + u8 run_remaining = next_byte(); + u8 color = 0; + for (u8 x = 0; x < WIDTH; x++) + { + u8 cellx = x >> 3; + for (u8 y = 0; y < HEIGHT; y++) + { + u8 celly = y >> 3; + if (bitmap >> (cellx + (WIDTH / 8) * celly) & 1) + { + // advance rle once + if (run_remaining == 0) + { + run_remaining = next_byte(); + color = 1 - color; + if (run_remaining == 0) + { + run_remaining = next_byte(); + color = 1 - color; + } + } + set_pixel(x, y, color); + run_remaining--; + } + } + } +} +#endif diff --git a/ch32_decoder/data.h b/ch32_decoder/data.h new file mode 100644 index 0000000..bad7e89 --- /dev/null +++ b/ch32_decoder/data.h @@ -0,0 +1,836 @@ +// Generated by the `encoder` rust app +#define USE_FillBlack +#define USE_FillWhite +#define USE_RLEHorizontal +#define USE_RLEVertical +#define USE_BGStripsH +#define USE_CellDiff8V + + +typedef enum Encoding { + Encoding_FillBlack = 0, + Encoding_FillWhite = 1, + Encoding_RLEHorizontal = 2, + Encoding_RLEVertical = 3, + Encoding_BGStripsH = 6, + Encoding_CellDiff8V = 7, +} Encoding_t; + +const unsigned char video[] = { + 0,0,7,8,99,152,57,1,13,3,19,5,18,6,16,8, + 3,1,10,15,9,15,9,16,8,17,7,18,14,19,13,28, + 4,29,3,29,3,30,2,64,3,0,255,0,30,3,27,5, + 19,2,3,8,19,13,15,17,1,3,9,23,8,28,2,255, + 0,49,4,25,9,68,4,25,15,16,18,14,22,10,30,2, + 224,7,13,41,78,0,146,9,6,2,13,5,3,8,1,4, + 9,5,4,9,1,7,3,29,1,7,1,7,1,7,1,39, + 5,23,2,1,7,18,17,15,20,12,23,9,25,7,128,7, + 5,41,206,0,119,8,22,11,18,14,16,16,1,5,1,1, + 5,19,1,15,2,14,2,14,2,14,3,13,3,13,2,14, + 3,4,7,2,3,3,10,16,19,12,193,7,5,41,206,0, + 213,4,10,1,15,9,6,4,9,1,2,5,7,2,2,14, + 2,14,2,14,2,14,1,15,1,15,1,31,1,4,8,19, + 16,16,20,12,22,9,97,3,0,255,0,158,2,28,5,1, + 1,18,1,3,14,10,25,6,81,1,31,1,31,1,31,2, + 30,2,30,1,31,1,22,4,5,1,6,1,2,6,8,9, + 6,11,14,1,4,14,13,22,10,24,8,26,6,28,4,224, + 7,15,123,142,0,85,4,19,6,18,6,3,3,12,12,14, + 10,23,9,24,8,24,8,24,8,8,4,13,7,7,6,11, + 8,6,10,6,9,6,13,3,11,2,30,2,30,2,30,3, + 29,4,28,4,28,4,28,5,39,7,5,4,8,7,1,3, + 1,144,3,0,255,0,180,5,26,7,5,2,18,14,18,14, + 19,13,22,10,8,4,11,9,6,7,12,7,5,10,8,9, + 4,12,7,11,2,16,2,106,1,31,2,30,2,30,2,30, + 3,29,4,28,7,14,8,3,10,11,8,1,31,1,130,3, + 0,255,0,242,5,26,7,4,5,16,16,16,16,7,4,5, + 16,5,7,4,16,4,11,6,10,4,14,5,10,3,17,3, + 9,2,222,1,31,2,30,2,30,4,21,4,3,7,15,9, + 1,10,12,9,1,21,1,8,1,65,3,0,255,0,119,3, + 28,6,25,8,24,8,24,12,20,12,7,6,10,10,4,9, + 10,11,1,10,11,23,10,29,2,59,3,27,3,11,1,18, + 1,12,1,127,1,31,2,30,5,20,1,6,7,14,3,2, + 17,10,171,3,0,177,5,26,7,24,9,23,9,24,8,24, + 7,27,5,28,3,29,5,1,7,18,14,17,15,18,14,18, + 14,18,14,18,14,19,13,19,13,19,13,19,13,19,13,19, + 13,19,13,19,5,3,5,18,5,7,2,17,7,1,7,18, + 14,19,13,18,14,17,15,11,3,3,15,5,10,2,15,4, + 28,3,29,2,30,2,30,6,139,122,131,130,102,138,72,146, + 72,154,42,162,42,170,42,178,42,186,72,194,103,202,132,6, + 142,58,130,66,70,74,40,75,65,82,41,90,10,98,11,106, + 11,114,11,122,11,130,11,138,41,146,71,154,99,6,33,1, + 94,10,86,18,55,26,24,34,24,42,55,50,101,51,140,58, + 100,59,202,66,100,67,233,74,100,76,8,82,100,84,7,90, + 101,92,37,98,102,100,67,106,72,108,98,114,41,122,10,130, + 11,137,236,146,11,154,11,162,11,170,42,178,73,186,134,194, + 194,6,159,2,117,10,179,18,148,26,179,34,210,42,241,50, + 210,58,179,65,225,66,179,74,179,82,179,89,162,90,179,97, + 193,98,179,106,179,114,179,122,179,130,241,136,161,139,233,144, + 130,148,8,152,99,154,164,155,199,160,68,163,3,168,67,176, + 35,3,255,0,97,1,31,3,29,3,29,5,27,6,26,7, + 25,8,10,2,12,9,8,5,10,10,6,7,10,11,4,7, + 10,12,2,9,9,23,9,27,5,31,1,18,5,27,3,1, + 1,255,0,143,2,24,2,255,0,6,3,33,2,1,6,30, + 8,1,2,30,7,34,8,34,7,32,9,30,2,1,8,30, + 10,31,5,2,1,32,9,31,9,31,2,1,7,30,1,2, + 10,11,3,12,1,2,18,4,12,2,2,1,19,7,33,12, + 3,5,20,20,20,25,3,4,8,25,1,1,1,10,2,200, + 6,14,58,66,58,161,66,99,74,131,82,101,90,132,98,132, + 106,164,113,165,114,107,122,1,122,139,130,194,131,99,6,14, + 58,33,58,99,66,36,74,100,82,69,90,100,98,100,106,133, + 107,67,113,146,122,100,123,6,130,162,131,98,6,14,58,65, + 58,161,66,68,74,100,82,100,90,132,98,132,106,164,113,132, + 114,107,121,226,122,108,130,194,131,69,6,19,51,99,59,36, + 67,38,75,100,83,70,91,101,99,99,99,226,107,133,115,133, + 116,101,123,109,130,179,138,226,139,78,147,163,148,39,155,194, + 156,163,3,25,7,25,7,22,10,20,12,20,12,22,10,23, + 9,23,9,25,7,25,7,25,7,25,7,25,7,25,7,26, + 6,22,10,23,9,208,1,23,2,5,2,22,11,21,10,24, + 2,1,7,25,6,27,5,27,5,28,4,255,0,15,3,25, + 7,25,7,21,11,21,11,19,13,20,12,21,11,25,7,22, + 10,23,9,25,7,25,7,25,7,25,7,25,7,25,7,23, + 1,2,6,22,10,170,1,6,2,22,6,1,3,21,11,23, + 9,27,6,27,5,27,5,28,3,255,0,46,3,26,6,26, + 6,22,10,21,11,19,13,19,13,21,11,26,6,25,7,23, + 9,24,8,26,6,26,6,26,6,26,6,26,6,26,6,27, + 5,23,2,2,5,22,10,104,1,6,2,22,6,1,4,20, + 13,21,10,22,2,2,7,26,6,26,6,26,5,28,4,255, + 0,15,6,29,3,233,12,8,68,163,76,132,84,132,90,69, + 92,70,98,40,100,39,105,237,107,233,113,227,114,98,114,210, + 121,196,122,97,122,241,129,194,130,34,131,16,137,228,139,41, + 140,132,146,3,147,100,154,34,162,34,170,34,178,34,6,6, + 114,2,121,228,129,197,137,135,145,132,153,162,6,132,3,39, + 11,70,19,101,27,131,7,1,136,200,0,57,3,10,2,5, + 7,10,2,2,8,12,15,8,15,8,11,2,2,6,8,16, + 6,18,5,20,2,124,7,1,140,194,0,113,4,7,4,5, + 3,4,9,3,8,4,28,3,29,3,29,3,29,4,28,9, + 23,10,6,7,9,3,1,60,7,7,189,206,0,255,0,209, + 1,1,14,5,27,4,28,4,28,4,28,4,28,4,15,3, + 10,7,1,2,8,8,6,11,2,1,4,12,2,15,2,30, + 1,112,3,0,205,1,31,1,31,5,27,4,28,4,28,4, + 28,4,28,4,28,4,28,4,28,3,28,4,22,1,5,6, + 2,2,9,1,5,4,3,10,4,6,3,29,2,30,2,30, + 2,30,1,31,2,30,3,29,4,2,5,21,12,8,24,3, + 30,1,255,0,51,7,15,57,236,0,226,4,19,3,28,6, + 24,9,11,1,10,9,11,1,10,8,9,7,7,13,4,11, + 2,15,4,28,4,28,4,28,4,28,4,28,5,27,6,26, + 10,22,18,14,20,15,61,3,0,255,0,233,1,29,6,2, + 3,21,11,23,2,1,7,26,6,8,2,17,6,1,11,16, + 17,15,17,6,1,8,17,6,3,2,1,2,18,6,26,5, + 27,4,28,4,28,3,29,2,30,4,28,4,28,5,27,6, + 8,8,10,26,6,30,2,96,3,0,255,0,178,5,27,7, + 24,10,22,12,20,13,1,2,13,7,3,9,12,2,11,7, + 9,2,15,6,8,3,15,6,8,4,14,6,7,6,10,9, + 4,28,1,31,1,49,4,29,2,255,0,76,1,31,3,0, + 255,0,86,1,27,6,26,7,23,11,20,14,18,16,16,18, + 5,7,2,18,6,26,7,25,11,21,15,17,16,16,19,13, + 26,6,27,5,28,4,29,3,29,3,30,2,31,1,255,0, + 33,2,0,101,6,3,2,28,12,27,14,26,15,25,13,27, + 13,26,14,25,13,27,13,28,11,30,10,30,11,30,9,33, + 4,24,2,8,7,22,4,6,11,18,6,4,12,17,9,1, + 13,16,25,7,2,5,35,4,5,3,28,16,14,2,8,17, + 12,5,6,17,12,6,2,20,12,28,12,30,9,31,8,31, + 10,30,10,12,2,0,177,5,34,8,32,10,29,12,28,12, + 28,12,27,12,29,10,30,9,31,9,32,8,34,5,20,2, + 12,5,20,5,8,9,9,1,7,7,6,11,6,4,5,9, + 5,11,4,7,4,36,4,2,1,34,3,2,4,6,2,24, + 11,2,4,10,2,1,2,1,4,4,16,10,11,4,14,11, + 14,1,15,10,31,7,32,8,32,9,31,9,31,9,16,2, + 0,177,7,3,2,28,12,27,14,26,14,26,13,26,14,25, + 12,29,11,30,10,30,10,30,10,21,2,8,8,22,2,10, + 4,23,4,6,7,21,7,4,11,12,2,3,9,2,13,9, + 4,2,25,7,6,4,2,2,32,9,1,1,29,12,14,2, + 12,14,12,4,10,14,11,10,1,17,12,29,11,31,9,31, + 8,31,9,31,9,16,6,171,4,39,11,202,19,171,27,140, + 35,109,43,109,51,78,59,47,66,210,74,241,82,241,91,47, + 99,78,107,109,115,109,123,140,131,140,137,39,140,7,145,41, + 147,230,153,73,155,229,161,74,163,105,169,106,171,105,178,5, + 179,44,180,225,186,6,187,13,188,225,194,52,196,194,202,55, + 210,86,218,86,226,117,234,143,236,194,243,72,251,72,6,149, + 2,170,4,70,10,111,12,132,18,20,20,163,25,246,28,225, + 34,8,35,78,42,68,43,140,51,202,59,233,68,39,76,101, + 217,198,225,106,233,75,241,106,249,106,6,142,33,195,41,199, + 49,201,57,204,65,206,73,238,81,236,83,129,89,234,91,129, + 97,237,105,232,113,231,122,67,6,49,24,97,32,100,40,132, + 48,133,56,164,64,227,73,33,138,225,147,2,152,193,155,35, + 160,194,168,194,169,65,170,193,176,229,178,194,179,195,180,65, + 185,6,186,163,187,165,193,39,194,164,195,193,201,133,202,164, + 203,163,209,134,210,133,211,101,217,165,218,165,219,102,225,167, + 226,196,227,98,227,197,233,199,234,197,235,193,236,3,241,199, + 243,4,243,225,244,34,249,228,251,35,252,2,6,9,90,194, + 98,132,106,101,114,101,122,70,130,70,138,36,146,34,154,2, + 6,15,67,65,75,34,83,3,90,226,98,98,98,194,106,69, + 114,69,122,39,130,39,137,233,145,170,153,99,154,37,162,98, + 7,7,57,128,127,1,2,5,11,5,2,14,1,80,1,6, + 2,15,11,13,15,2,22,3,22,3,1,1,21,2,124,7, + 7,56,196,57,1,22,2,22,2,21,3,22,3,11,1,9, + 4,6,6,14,6,3,9,15,17,8,5,1,18,6,26,6, + 26,6,14,2,10,7,10,6,9,8,6,10,3,3,2,8, + 1,119,7,7,57,196,154,5,8,3,6,7,5,4,5,6, + 5,16,4,28,5,27,5,27,5,27,5,27,6,26,7,7, + 1,9,1,7,4,1,3,5,4,6,11,2,6,5,19,3, + 123,3,255,0,255,0,125,1,30,3,8,1,1,3,16,6, + 3,8,15,6,2,10,15,5,2,20,5,5,2,30,2,30, + 2,30,3,29,3,29,3,29,4,9,2,17,6,7,2,17, + 10,1,6,13,20,5,2,5,18,6,3,5,3,4,2,1, + 6,7,16,14,21,10,23,8,15,6,10,107,193,115,162,123, + 162,124,129,130,21,138,103,139,132,140,98,147,162,155,193,3, + 17,10,21,2,2,3,25,2,29,2,30,2,1,3,26,10, + 21,2,1,6,22,2,30,2,31,2,2,5,24,11,22,6, + 27,2,31,2,1,5,25,10,23,5,27,1,32,1,1,4, + 26,9,7,4,1,7,4,1,1,4,9,13,4,1,14,11, + 6,1,7,3,4,9,6,13,3,10,4,15,3,12,2,15, + 2,13,1,16,2,30,2,30,2,30,2,30,2,13,1,16, + 3,10,3,16,4,9,4,15,9,2,7,14,20,9,1,2, + 20,2,30,2,30,2,1,3,26,10,22,8,4,3,15,1, + 31,1,31,1,1,7,23,7,24,2,1,2,27,2,30,1, + 2,1,27,9,23,7,24,2,30,2,31,2,1,7,23,7, + 26,2,32,1,2,7,23,8,25,1,1,1,29,1,21,2, + 9,8,10,4,10,6,5,1,5,6,9,1,6,5,3,9, + 6,14,2,11,4,15,2,13,1,16,2,30,1,31,2,30, + 2,30,2,12,1,17,3,11,2,16,4,9,4,15,9,2, + 8,12,11,1,8,8,24,1,2,2,27,1,31,1,1,2, + 28,8,24,1,1,3,58,6,8,3,240,1,29,3,27,4, + 28,3,30,2,32,2,23,1,6,2,19,2,1,3,5,2, + 14,1,3,8,4,3,9,5,2,10,2,18,2,10,2,18, + 2,30,2,30,2,30,2,30,3,29,3,29,3,9,4,1, + 12,3,4,4,8,4,11,1,5,2,9,3,30,1,31,1, + 30,3,29,2,29,1,31,2,29,3,29,2,30,2,31,3, + 29,3,30,4,28,1,1,3,11,3,18,3,28,4,27,2, + 29,5,26,1,2,2,27,1,32,5,7,1,19,3,9,1, + 30,2,20,1,1,2,5,3,21,4,5,1,30,2,22,1, + 6,2,23,4,2,3,23,1,1,6,15,2,7,7,5,3, + 4,7,4,8,3,6,3,10,1,18,3,10,1,18,3,29, + 3,29,3,10,1,18,3,10,1,18,4,7,4,17,5,8, + 3,2,2,12,5,10,2,1,1,3,3,7,6,8,3,4, + 9,2,17,1,63,4,27,1,1,4,25,1,31,3,28,6, + 26,1,31,4,29,5,28,2,1,1,29,3,30,3,11,3, + 17,6,25,1,3,4,23,3,28,7,25,1,3,3,25,2, + 12,1,18,5,8,1,18,2,1,3,7,1,19,1,9,3, + 20,4,5,3,20,1,1,4,3,2,21,1,7,3,21,5, + 3,2,22,9,15,1,7,1,4,4,11,5,7,8,4,3, + 3,8,4,9,1,7,2,11,1,18,2,11,1,18,2,30, + 2,30,2,11,1,18,2,11,1,18,3,9,3,9,2,6, + 4,5,1,1,6,8,4,3,4,10,3,1,5,3,11,10, + 2,6,1,2,12,2,2,4,3,4,4,2,22,1,7,2, + 22,1,1,4,3,2,21,5,4,2,20,1,9,3,19,1, + 1,1,9,1,18,6,7,1,18,1,30,2,3,3,25,1, + 1,4,27,3,30,1,2,4,26,5,9,3,17,1,30,5, + 26,1,1,6,24,1,31,1,1,3,9,1,18,6,7,2, + 17,1,12,2,18,1,9,3,19,7,3,3,20,4,5,2, + 30,2,21,5,3,2,21,6,2,3,21,1,6,3,12,2, + 8,9,9,8,5,9,4,3,2,10,3,17,1,12,2,17, + 1,12,1,18,1,31,1,31,1,12,1,18,1,12,2,17, + 2,10,3,17,3,8,6,8,4,3,4,9,4,3,2,4, + 9,12,2,1,6,3,9,10,3,6,2,3,22,4,4,2, + 31,2,20,6,4,2,20,6,4,3,18,1,10,3,18,1, + 1,1,10,1,17,7,7,1,17,1,2,1,10,1,16,1, + 31,2,1,5,25,5,28,2,14,3,0,10,5,14,3,9, + 2,1,4,13,3,9,1,5,2,10,5,9,2,1,2,13, + 5,10,6,12,4,10,8,11,3,11,1,17,3,11,7,10, + 4,12,8,7,4,13,1,14,4,13,1,13,4,14,6,6, + 5,15,17,5,3,7,2,1,12,7,4,8,11,6,10,5, + 28,3,30,2,158,2,31,1,30,3,28,3,13,5,8,5, + 2,1,13,5,19,2,7,4,16,9,4,2,15,11,4,2, + 8,1,4,14,4,13,7,9,3,12,6,11,4,11,1,17, + 3,10,1,1,5,12,3,9,7,12,4,9,5,13,5,8, + 2,16,1,1,4,9,1,2,5,11,4,9,6,13,4,2, + 7,1,23,2,7,1,38,2,3,1,30,1,255,0,69,1, + 38,1,80,1,38,2,37,3,9,126,23,1,255,0,70,1, + 39,1,38,2,28,1,9,2,28,1,5,2,79,1,38,2, + 38,2,32,1,5,2,32,1,5,2,33,1,4,2,34,6, + 36,2,111,2,36,4,30,1,4,6,13,26,32,9,34,6, + 35,5,34,6,35,5,35,5,35,5,35,5,35,5,27,2, + 6,5,29,2,4,5,4,3,24,2,1,12,26,11,30,8, + 31,8,27,15,23,19,4,2,11,5,5,4,26,6,4,5, + 27,4,4,6,34,4,1,2,39,2,39,1,60,2,39,3, + 39,2,41,1,3,4,35,6,21,1,13,5,20,2,14,4, + 20,2,14,5,19,3,12,5,1,2,17,3,13,3,21,3, + 13,3,21,3,12,4,1,2,18,3,12,6,19,3,7,4, + 1,4,21,3,10,6,19,4,12,10,28,8,30,3,1,7, + 28,2,3,8,32,9,31,9,30,9,30,2,1,6,31,1, + 7,1,30,2,7,1,15,3,86,1,31,1,25,1,5,1, + 21,11,21,10,26,1,99,1,30,1,31,1,31,1,29,3, + 27,5,2,10,8,2,2,4,2,10,11,20,12,20,11,21, + 11,4,2,4,1,12,9,3,3,3,2,1,2,12,14,2, + 1,1,2,1,1,4,21,3,5,2,23,2,3,1,27,2, + 15,1,31,1,31,3,28,5,27,5,27,5,28,3,30,2, + 219,2,37,3,103,1,35,5,34,6,34,6,34,6,15,1, + 17,7,14,2,18,5,13,2,21,3,13,1,22,8,3,6, + 9,1,11,18,9,3,10,7,1,2,1,1,1,4,10,2, + 11,7,5,3,12,3,9,8,3,3,14,3,4,2,3,1, + 2,4,3,2,16,4,5,1,1,2,1,5,1,3,17,3, + 7,3,1,7,20,2,8,2,1,6,20,3,8,8,21,3, + 8,8,21,3,8,8,21,3,6,11,18,5,4,3,2,9, + 16,1,2,1,3,4,4,11,18,5,5,10,1,2,15,4, + 8,10,3,2,13,1,10,12,4,1,23,12,28,12,21,1, + 6,12,15,2,10,16,23,18,4,2,16,18,3,1,18,18, + 8,3,11,18,2,1,5,2,12,21,5,3,10,22,4,1, + 2,1,10,21,4,2,2,1,10,19,2,1,1,1,4,3, + 9,18,3,2,6,3,7,19,2,1,8,3,7,21,6,6, + 8,19,5,8,7,21,1,3,3,4,7,27,2,2,8,31, + 9,30,9,30,10,30,11,28,12,28,13,19,2,7,12,28, + 13,16,3,8,17,11,4,8,32,7,33,8,32,8,31,9, + 31,9,31,9,31,9,6,2,167,8,31,10,30,11,28,13, + 27,13,27,13,25,15,25,15,25,15,27,14,25,14,26,13, + 26,14,25,16,23,16,26,14,28,12,27,13,27,13,27,14, + 25,15,25,15,25,16,24,15,24,17,18,2,3,17,17,3, + 3,17,15,5,3,17,15,4,2,7,10,148,167,107,1,30, + 3,29,3,5,2,17,15,4,8,4,28,4,4,3,5,3, + 5,2,6,2,6,3,5,3,5,4,4,4,28,5,27,7, + 25,16,4,1,11,25,7,28,4,128,2,51,1,34,6,32, + 9,31,11,28,12,28,13,27,13,26,14,24,16,24,16,25, + 15,27,14,26,13,27,13,26,14,26,15,25,15,25,15,27, + 9,2,2,26,12,26,14,3,7,16,15,1,10,14,15,1, + 12,12,28,12,16,1,10,13,27,13,26,13,18,1,7,14, + 26,14,25,15,25,15,2,15,1,38,2,35,7,32,9,30, + 11,29,12,27,13,27,14,26,14,26,13,25,16,24,16,25, + 15,26,14,26,14,26,14,25,15,25,15,25,15,25,15,27, + 12,28,13,27,13,26,14,26,15,24,16,24,16,24,16,24, + 22,18,25,14,27,12,30,5,2,90,1,38,3,32,8,30, + 11,28,12,28,13,26,14,26,15,25,17,24,15,25,14,27, + 13,12,1,13,13,13,1,13,12,28,12,28,12,27,12,28, + 11,29,14,28,13,27,13,26,14,3,7,17,25,14,28,12, + 29,11,30,10,31,8,32,8,33,8,32,7,2,52,1,39, + 2,34,7,32,9,29,11,28,13,27,16,24,16,24,15,25, + 15,25,15,26,13,27,12,28,12,28,12,29,11,29,11,29, + 8,32,10,30,13,26,14,26,23,17,25,15,27,13,28,12, + 29,11,30,9,32,9,31,9,31,8,13,1,16,6,3,0, + 32,1,31,2,30,3,29,4,28,5,27,6,18,1,7,7, + 16,2,7,8,15,2,7,8,8,1,3,5,7,8,7,11, + 6,8,2,16,6,29,3,202,1,255,0,255,0,151,6,7, + 106,71,114,9,121,233,129,201,137,200,145,230,153,229,6,15, + 89,193,90,33,97,193,98,33,98,229,105,162,106,33,106,107, + 113,208,121,177,129,51,137,50,145,47,153,106,161,132,6,20, + 66,195,74,226,82,226,90,227,99,1,106,227,114,227,122,196, + 123,225,130,165,131,194,137,10,138,202,145,23,153,54,161,84, + 169,207,177,238,186,34,186,226,2,8,3,7,7,24,2,6, + 9,23,1,5,11,22,2,4,11,24,1,4,11,24,1,3, + 12,24,1,3,12,28,11,28,12,14,2,15,9,10,6,19, + 5,6,10,20,3,3,14,19,21,9,255,0,15,1,38,2, + 37,3,36,4,35,5,33,7,31,9,30,12,26,16,21,23, + 10,102,2,3,6,7,8,19,4,2,3,4,8,19,4,5, + 3,1,8,18,4,9,8,19,4,10,6,20,4,10,9,17, + 3,13,6,1,6,11,3,13,6,4,6,8,3,13,6,5, + 10,3,3,13,13,5,4,2,3,12,12,10,1,2,3,12, + 9,16,3,12,6,19,3,13,5,19,3,14,5,18,4,13, + 5,19,3,12,6,19,3,11,8,18,3,9,11,18,2,7, + 13,18,2,6,14,18,1,6,15,18,1,5,16,18,1,4, + 17,18,1,4,18,17,2,3,18,12,1,5,2,2,18,8, + 5,8,18,6,8,7,19,2,12,7,33,9,31,5,35,3, + 43,14,14,21,11,25,8,9,6,9,8,4,15,5,8,2, + 31,1,32,1,31,1,31,1,32,1,31,1,31,1,32,1, + 29,3,18,1,9,5,15,3,7,8,4,2,7,4,6,9, + 2,5,5,5,6,14,1,2,2,7,6,26,6,26,6,26, + 6,5,3,18,8,1,5,18,15,1,2,4,3,7,15,1, + 4,2,5,5,15,1,3,3,6,2,18,1,2,2,27,1, + 2,2,27,5,27,4,28,3,30,1,31,1,31,1,31,2, + 31,1,31,1,31,2,12,2,182,5,34,7,16,3,13,8, + 17,4,10,8,18,7,6,10,16,5,3,3,2,11,16,5, + 6,12,17,5,9,10,16,4,12,10,14,4,12,12,11,5, + 13,9,2,8,3,5,15,8,5,4,3,5,14,9,5,4, + 3,5,14,9,3,5,4,5,13,17,6,4,13,2,1,12, + 8,4,14,11,11,4,15,7,14,4,16,6,14,4,16,7, + 14,3,16,7,14,4,14,8,14,4,12,11,14,4,10,13, + 13,4,8,15,14,3,7,16,14,3,4,19,14,3,3,21, + 6,3,255,0,164,22,7,255,0,234,21,6,255,0,61,3, + 255,0,187,1,30,2,30,6,25,8,23,9,23,9,23,32, + 9,23,9,23,9,24,8,25,6,27,1,31,1,255,0,166, + 7,7,48,128,62,2,1,5,4,8,1,5,1,23,1,47, + 24,1,23,1,23,1,6,3,14,4,3,7,2,112,7,7, + 49,128,76,1,7,4,5,2,3,14,2,5,1,16,2,24, + 22,1,23,1,23,2,7,1,14,2,7,2,13,5,2,10, + 3,20,3,20,3,102,7,7,57,132,103,3,1,2,8,11, + 6,2,9,15,5,27,13,40,11,21,11,22,10,24,1,231, + 7,7,25,198,107,1,28,5,16,10,7,8,10,7,11,3, + 11,7,25,6,26,8,24,9,23,12,1,2,17,13,9,4, + 6,12,4,5,3,5,3,14,2,6,2,22,2,7,2,13, + 2,1,2,11,2,14,2,14,2,72,6,3,116,5,124,8, + 132,194,7,2,115,132,5,3,26,7,3,3,7,10,3,29, + 4,28,4,28,6,26,9,23,11,8,18,4,247,7,7,57, + 198,57,1,29,3,28,4,27,4,28,3,29,2,15,4,11, + 2,13,7,5,7,12,9,1,19,2,30,3,29,5,27,6, + 26,7,25,12,18,1,1,7,10,175,7,3,60,198,49,1, + 7,2,22,2,31,1,31,2,30,2,30,3,30,2,30,2, + 18,2,8,4,15,6,5,5,16,8,1,19,3,29,2,30, + 2,30,4,28,7,25,10,30,2,1,53,3,210,1,27,2, + 2,1,26,6,26,6,27,2,2,2,30,3,29,3,30,2, + 29,3,14,1,11,6,8,3,3,6,4,7,2,1,2,7, + 3,29,3,29,3,29,2,30,2,30,1,31,2,3,2,17, + 1,7,12,12,1,7,14,14,27,4,255,0,166,7,5,125, + 238,118,1,3,1,18,6,25,7,25,5,1,2,27,1,2, + 3,29,3,30,2,28,6,24,7,6,4,9,14,4,21,2, + 22,2,22,3,21,4,20,4,20,3,7,3,9,5,6,6, + 7,6,4,16,6,9,1,20,2,30,3,29,3,30,2,30, + 1,30,2,14,1,59,7,2,125,238,167,1,16,2,1,2, + 2,2,14,11,13,7,1,5,13,5,2,5,20,4,6,3, + 7,7,2,1,3,4,3,23,3,29,3,29,4,28,4,28, + 4,28,3,29,3,3,1,2,1,14,1,7,3,3,8,8, + 20,4,19,5,12,5,2,4,12,10,15,8,17,3,2,2, + 94,7,7,111,255,30,6,17,8,3,2,11,14,8,17,6, + 19,3,23,1,51,5,30,2,64,1,31,1,22,4,37,2, + 1,1,28,6,18,3,2,1,48,3,3,2,13,6,1,4, + 10,14,10,4,6,3,11,3,21,3,188,1,28,4,24,8, + 46,2,19,1,1,3,10,2,5,7,11,2,1,10,11,61, + 1,7,3,156,227,58,1,8,2,19,4,7,5,11,1,3, + 5,6,14,1,2,2,8,6,26,6,29,4,27,10,21,12, + 22,11,18,20,12,24,8,5,5,20,1,4,11,21,12,20, + 5,9,6,4,6,7,9,2,7,4,133,7,3,156,227,46, + 6,16,3,5,8,7,2,7,18,3,4,8,15,3,7,7, + 12,3,10,7,27,7,1,1,23,10,21,12,21,11,19,13, + 19,11,21,2,2,5,22,3,28,4,29,3,22,2,23,1, + 144,3,48,3,27,5,8,2,15,6,8,3,8,1,5,6, + 4,1,2,5,7,13,1,3,2,7,6,15,3,9,6,27, + 5,27,5,26,11,22,11,20,13,2,3,14,22,9,255,0, + 157,3,23,9,14,19,12,20,11,23,4,26,6,28,4,12, + 1,13,6,25,6,1,5,7,3,10,5,1,7,7,4,7, + 15,6,6,5,16,3,30,3,16,3,222,2,28,4,18,4, + 4,6,11,21,8,24,7,25,6,26,6,26,4,11,1,11, + 3,2,5,9,3,10,9,9,7,2,1,3,9,5,1,4, + 7,2,1,2,26,2,1,2,25,6,26,6,28,2,1,2, + 12,2,3,4,7,2,1,2,12,8,7,2,1,3,12,8, + 3,9,12,9,1,11,11,26,6,26,7,25,8,24,11,21, + 26,6,27,5,27,5,27,5,28,4,29,3,30,2,64,3, + 255,0,30,3,27,5,2,1,23,9,2,1,3,23,1,3, + 1,27,2,62,2,1,1,28,4,1,2,21,3,1,8,19, + 15,1,3,9,25,6,26,6,24,9,12,24,12,21,10,26, + 6,26,7,25,7,25,4,1,2,25,4,4,18,6,3,2, + 21,6,4,1,22,5,27,5,28,4,29,3,30,2,128,3, + 10,5,26,7,24,9,22,11,20,14,17,18,13,21,10,24, + 8,26,5,29,2,31,1,10,2,192,1,30,7,25,9,23, + 10,24,8,26,7,27,5,28,4,28,4,28,4,28,4,27, + 5,25,6,9,2,3,3,7,7,10,4,1,16,10,21,10, + 20,2,2,8,19,1,4,7,18,1,6,6,27,5,30,2, + 31,1,31,1,5,2,0,22,18,22,18,22,18,22,18,22, + 18,22,18,22,18,22,17,23,16,24,17,23,17,22,19,21, + 19,21,19,20,20,19,21,18,22,18,22,16,24,15,27,11, + 32,5,229,1,39,1,39,1,38,2,38,2,38,2,3,0, + 13,19,13,19,13,19,13,19,13,19,13,19,13,19,12,20, + 12,20,11,21,10,22,8,24,7,255,0,193,3,19,3,7, + 4,14,7,7,5,1,5,2,12,7,25,7,25,8,24,10, + 22,15,8,1,8,26,6,29,3,128,7,14,115,8,85,1, + 14,2,9,1,3,1,7,5,6,5,4,5,2,10,7,5, + 1,19,7,25,7,25,8,24,11,21,20,4,2,6,28,4, + 22,2,168,7,14,49,204,0,4,4,3,5,2,25,1,8, + 1,7,1,15,2,31,1,31,1,31,1,31,2,29,3,30, + 2,13,4,6,5,2,4,8,7,7,25,7,25,8,24,9, + 23,14,18,15,17,21,4,2,5,29,3,64,7,6,33,200, + 0,4,4,3,5,2,35,1,7,1,7,1,15,2,14,2, + 14,2,14,2,14,2,14,2,6,5,3,3,1,7,7,5, + 1,6,1,12,7,25,7,25,8,24,11,21,15,17,27,5, + 29,3,7,14,115,220,0,4,4,3,5,2,255,0,76,3, + 27,5,25,7,14,5,4,9,10,22,8,24,7,25,7,25, + 7,5,2,18,7,4,5,3,3,10,27,5,160,6,13,1, + 22,9,22,17,22,25,22,33,53,41,52,49,52,57,83,65, + 82,73,112,81,142,89,172,97,232,7,2,19,222,139,2,29, + 4,3,3,3,9,9,23,9,23,10,22,13,19,19,13,31, + 1,128,6,10,8,8,9,7,11,5,11,5,12,4,13,3, + 13,3,7,2,18,144,26,3,20,4,4,15,1,23,1,23, + 2,22,3,21,9,15,16,6,10,8,8,9,7,10,6,11, + 5,12,4,12,4,7,6,51,156,40,2,11,4,14,4,6, + 15,6,8,2,16,5,27,5,27,5,27,5,27,6,26,7, + 25,9,23,255,0,33,6,31,58,133,66,73,74,43,82,12, + 90,13,98,13,106,13,114,13,122,44,130,75,138,43,146,43, + 154,33,154,105,162,12,170,12,177,237,185,238,193,208,201,209, + 209,179,217,149,225,99,225,243,233,68,233,244,241,67,241,246, + 249,36,249,204,251,138,6,18,4,194,12,194,20,225,138,132, + 146,103,154,74,162,44,170,44,178,13,185,238,193,238,201,206, + 209,175,217,175,225,144,233,144,241,113,249,81,1,7,15,128, + 0,0,42,1,3,2,2,6,1,7,2,2,2,2,73,1, + 2,20,4,4,74,3,1,2,1,7,1,7,2,2,2,2, + 40,7,15,220,0,0,11,4,13,3,13,4,12,4,11,5, + 11,1,1,3,5,11,4,12,5,11,14,2,148,12,3,13, + 4,6,2,4,8,3,2,3,9,2,4,1,68,3,1,1, + 5,2,3,13,4,12,5,1,1,9,7,2,6,1,8,2, + 14,2,14,2,6,7,15,255,192,2,11,1,2,2,11,2, + 1,3,10,6,10,3,1,3,9,3,1,3,10,2,1,4, + 10,6,10,1,1,3,1,10,4,1,3,49,8,7,4,4, + 12,3,7,1,15,1,141,1,4,2,16,5,2,2,5,6, + 2,5,5,2,4,14,3,3,5,14,2,4,5,3,14,2, + 5,3,15,1,32,1,23,2,23,1,5,2,6,1,9,2, + 3,4,4,3,7,13,1,3,1,1,2,14,3,5,1,23, + 2,3,1,1,6,2,5,4,120,3,0,12,2,30,2,10, + 2,17,4,9,2,18,3,8,4,17,4,8,4,16,5,8, + 4,14,6,8,5,7,2,3,2,1,5,8,5,5,16,2, + 4,1,4,6,21,2,3,8,1,5,5,2,6,3,1,17, + 2,5,2,141,1,31,3,6,2,21,3,4,5,3,1,16, + 17,13,14,3,3,12,6,3,5,3,3,26,4,252,1,29, + 4,1,4,19,10,2,2,1,2,10,14,2,5,10,22,10, + 7,3,3,7,2,11,2,1,2,15,2,14,2,14,2,14, + 2,15,1,15,1,20,3,0,8,2,3,4,9,4,15,4, + 10,2,15,2,1,3,26,1,2,3,25,2,1,4,13,1, + 11,1,1,4,13,2,11,5,13,3,10,6,11,5,2,5, + 2,7,10,6,2,15,6,9,2,18,2,10,1,63,2,30, + 3,29,3,6,2,21,6,1,5,1,5,3,2,9,5,1, + 24,2,5,1,255,0,255,0,188,3,0,5,27,6,26,7, + 25,8,1,2,6,4,1,1,9,13,1,13,5,30,2,170, + 2,30,3,29,5,27,7,25,1,2,4,25,1,3,3,18, + 3,3,1,2,4,19,4,1,2,1,4,14,3,4,11,8, + 9,5,27,1,177,4,10,1,14,9,8,2,6,3,2,14, + 5,29,3,6,1,23,2,6,1,62,2,217,3,0,23,9, + 6,2,15,9,6,6,6,14,6,9,2,15,5,10,1,16, + 5,27,2,30,3,29,3,2,1,26,7,25,8,1,2,4, + 2,15,28,4,29,3,255,0,255,0,63,3,17,1,5,1, + 1,7,13,19,9,23,8,24,7,25,6,26,8,14,4,6, + 9,4,3,6,8,2,9,2,5,6,10,6,164,26,226,34, + 227,42,228,50,196,58,104,66,105,74,105,82,135,90,104,98, + 105,106,105,114,105,122,104,130,135,138,165,145,68,146,104,153, + 38,154,74,161,38,162,12,169,143,177,174,185,163,186,73,194, + 73,202,72,208,4,210,72,216,6,218,72,224,9,226,41,232, + 25,240,25,248,25,6,147,114,8,121,203,128,243,131,102,136, + 250,144,250,153,24,161,24,169,55,177,56,185,84,193,114,201, + 114,209,115,217,115,225,114,233,145,241,114,249,114,3,189,2, + 29,2,30,1,30,2,29,3,19,14,16,18,12,20,12,20, + 13,19,14,18,15,17,16,16,17,15,19,13,19,13,17,15, + 16,16,15,17,14,18,13,19,12,20,12,20,14,18,16,16, + 26,3,30,2,31,1,31,2,31,2,161,6,147,146,69,154, + 9,161,115,169,54,177,24,184,250,192,220,200,190,208,190,216, + 159,220,97,224,159,228,97,232,102,233,121,240,102,241,122,248, + 101,249,122,3,255,0,97,3,29,6,17,18,13,23,8,75, + 1,31,9,23,18,14,18,14,18,14,18,14,18,14,12,20, + 10,22,8,11,1,12,7,7,8,10,7,5,11,9,6,5, + 14,7,6,5,26,6,26,5,27,5,26,6,26,6,26,5, + 27,4,29,3,56,3,255,0,247,5,25,7,24,4,27,13, + 12,21,10,22,7,25,6,2,1,21,8,1,1,24,7,2, + 2,7,7,7,6,2,6,4,9,1,1,3,6,1,8,2, + 21,1,255,0,154,6,28,60,193,68,132,76,194,84,194,92, + 194,100,194,107,193,108,194,112,212,115,195,116,194,123,233,132, + 39,140,194,148,194,156,194,164,194,172,194,180,194,188,163,196, + 163,204,163,212,163,220,162,228,162,236,130,244,130,252,129,2, + 14,1,39,1,39,1,40,1,39,1,39,1,39,2,39,1, + 39,1,39,1,39,2,7,3,29,1,1,10,28,13,27,12, + 11,40,7,4,7,7,33,6,34,6,32,8,31,9,30,10, + 30,10,30,11,30,11,29,11,29,11,30,9,32,9,33,3, + 2,1,35,2,3,1,34,3,2,1,36,1,3,1,13,2, + 10,18,5,7,12,15,7,6,12,14,9,5,11,13,13,3, + 11,10,31,8,33,7,33,8,32,8,30,11,29,12,28,12, + 16,3,9,14,4,1,8,4,4,2,3,15,3,2,6,6, + 2,4,2,21,1,91,1,38,3,23,3,3,1,7,3,22, + 9,6,4,9,2,10,10,5,4,4,7,4,18,3,5,1, + 8,5,19,2,14,5,34,7,32,9,30,11,28,11,16,2, + 9,12,17,1,11,10,31,9,18,3,11,10,12,3,1,3, + 0,13,15,19,12,20,7,1,3,26,2,30,2,255,0,89, + 1,34,3,29,4,26,13,19,15,17,17,22,1,2,7,31, + 1,255,0,125,2,30,2,30,2,29,2,30,2,13,4,12, + 2,4,7,14,182,224,0,255,0,2,1,24,2,25,4,20, + 5,16,2,1,16,4,20,4,21,4,20,2,5,5,6,12, + 4,13,3,15,1,192,7,15,115,140,0,62,1,28,5,23, + 9,16,1,1,7,3,4,12,8,8,4,5,13,3,5,2, + 4,5,3,7,4,1,12,18,14,9,2,8,13,8,8,2, + 14,7,25,6,26,6,26,6,26,6,26,7,25,7,49,2, + 10,7,5,144,7,11,120,204,0,49,1,23,2,29,3,20, + 5,3,3,14,4,1,9,11,15,19,13,20,12,20,12,16, + 15,9,21,11,1,12,7,25,11,8,7,4,13,7,9,6, + 10,6,10,6,10,6,10,6,10,6,10,7,41,96,3,0, + 248,2,28,6,25,8,22,10,10,1,10,11,6,2,1,1, + 2,1,6,13,6,7,4,15,4,29,2,61,1,30,5,11, + 2,17,8,25,6,26,6,26,6,26,6,26,6,26,6,26, + 6,26,7,25,8,24,11,21,255,0,65,3,0,255,0,255, + 0,108,2,1,10,19,13,23,1,4,2,25,1,31,1,70, + 2,24,1,4,3,10,3,3,10,3,2,4,15,4,8,2, + 18,5,6,1,60,1,14,2,30,3,29,6,26,8,24,9, + 4,4,15,10,2,9,1,21,2,31,2,18,7,15,123,216, + 0,114,2,16,9,1,1,2,1,3,2,1,13,1,26,1, + 23,2,1,3,4,1,10,1,2,19,1,255,0,255,0,126, + 7,3,24,194,0,55,9,7,1,5,2,3,14,6,4,1, + 21,6,26,6,26,7,4,4,3,2,12,9,1,14,8,23, + 1,168,7,5,41,66,0,31,1,22,10,13,2,2,15,6, + 4,2,20,6,26,6,26,7,4,2,1,2,1,3,12,8, + 2,14,8,8,2,19,1,1,2,4,3,4,2,6,6,1, + 2,1,3,2,3,6,9,2,9,5,19,6,18,6,2,4, + 11,7,1,6,3,8,7,15,121,64,0,20,4,12,12,1, + 1,3,3,1,90,11,10,1,2,16,5,119,2,15,2,4, + 3,14,3,3,3,14,8,4,5,6,19,5,19,6,18,7, + 3,3,3,2,5,9,1,5,2,6,1,7,3,14,2,103, + 7,15,249,66,0,61,3,22,10,14,2,2,14,7,4,2, + 19,6,26,6,26,7,25,8,4,4,3,6,7,197,2,15, + 1,5,12,6,18,6,18,5,19,4,6,3,3,5,2,6, + 3,1,1,142,7,7,249,198,0,115,3,6,4,4,9,5, + 14,3,10,4,15,3,29,3,29,3,29,3,29,3,22,2, + 5,4,20,1,7,4,27,7,23,11,4,1,19,13,19,10, + 20,1,1,8,108,6,6,1,10,15,1,4,4,21,3,21, + 3,14,2,61,6,129,128,107,6,146,97,227,98,195,105,164, + 106,229,113,101,115,7,121,8,123,10,128,140,131,12,136,233, + 139,9,145,71,147,6,153,164,154,229,162,2,162,195,3,0, + 6,26,5,27,4,28,4,28,3,29,2,30,1,31,1,50, + 2,28,5,26,7,25,8,22,1,1,8,20,12,18,12,20, + 11,20,12,20,14,17,15,17,15,18,14,18,14,18,14,19, + 12,9,1,11,10,10,1,11,10,9,2,13,6,10,3,28, + 4,28,4,27,5,26,6,25,7,24,8,22,10,21,11,19, + 13,18,14,15,17,11,53,3,255,0,243,2,14,4,11,3, + 12,7,9,4,6,5,1,9,5,8,1,8,1,10,3,29, + 4,26,6,17,2,6,8,4,4,3,2,3,16,4,29,2, + 30,2,255,0,207,7,2,57,132,77,3,16,3,9,4,16, + 5,7,5,6,4,5,6,4,19,3,6,3,18,5,4,5, + 20,3,2,8,5,3,1,2,6,16,4,8,3,9,4,12, + 3,14,1,91,7,3,17,196,117,3,1,2,17,4,7,5, + 15,5,3,9,13,19,12,22,11,19,1,1,11,11,4,4, + 1,1,12,5,27,4,21,3,5,3,50,3,255,0,113,2, + 30,3,13,1,14,5,12,2,13,6,11,3,12,7,5,3, + 2,3,6,1,5,14,3,3,6,4,2,13,4,5,3,5, + 2,255,0,62,3,30,9,24,13,20,16,17,16,16,16,17, + 15,17,15,19,13,20,12,21,11,7,1,13,11,2,132,1, + 38,3,37,4,35,6,34,7,32,8,32,9,30,11,29,12, + 28,12,28,13,27,14,2,16,8,32,7,33,7,33,7,21, + 9,1,10,18,22,14,1,2,23,13,27,12,28,12,29,10, + 30,9,31,9,32,7,33,6,23,1,11,5,22,2,11,4, + 22,3,12,2,23,3,7,11,127,198,255,0,84,3,1,1, + 16,1,3,10,18,14,18,15,1,1,18,4,1,6,28,3, + 255,0,140,7,6,49,132,27,1,29,5,15,5,3,1,1, + 9,11,21,9,22,8,24,7,24,9,4,1,22,2,6,1, + 13,6,2,6,8,20,3,123,2,216,1,38,5,35,6,34, + 5,36,5,35,6,32,6,2,2,29,8,31,10,30,7,2, + 2,32,4,3,6,27,6,33,7,33,7,33,8,31,9,30, + 10,29,13,25,17,21,21,19,21,18,22,20,19,23,16,28, + 10,30,2,3,1,34,2,3,2,19,3,255,0,202,1,12, + 1,15,1,1,2,9,13,4,6,9,13,5,27,5,27,5, + 27,5,27,5,27,5,27,5,27,5,27,5,27,4,5,10, + 13,7,2,12,11,7,2,18,5,7,1,255,0,89,2,183, + 1,30,1,2,7,30,10,31,12,26,13,27,12,28,11,30, + 10,16,2,13,9,16,1,15,9,15,3,13,9,15,4,13, + 8,15,4,13,8,16,3,13,8,17,3,8,13,16,3,6, + 15,16,4,4,16,17,4,2,18,16,4,1,19,17,9,3, + 12,17,5,6,12,17,3,8,12,28,12,28,11,29,11,29, + 11,29,12,24,16,12,3,255,0,89,2,27,7,25,10,20, + 6,3,4,9,1,1,2,6,5,6,2,7,6,5,14,5, + 27,4,28,4,28,4,28,4,28,4,28,4,28,5,27,6, + 26,7,2,2,1,6,14,21,7,2,2,255,0,161,3,250, + 4,26,8,22,10,7,1,2,2,9,6,3,2,4,8,9, + 5,11,7,8,12,6,10,3,13,6,26,6,26,6,26,6, + 26,6,26,6,26,6,26,6,26,5,27,4,7,10,11,7, + 4,11,10,8,1,16,1,14,1,255,0,184,3,0,183,2, + 30,3,6,1,21,5,5,2,18,8,4,3,11,2,3,11, + 2,5,7,19,1,5,5,28,2,255,0,158,4,26,7,23, + 10,21,12,20,13,18,16,3,3,9,24,5,29,1,231,3, + 0,7,1,2,25,5,29,3,31,1,76,1,28,3,2,1, + 25,2,3,3,3,5,16,2,1,6,1,6,16,16,8,1, + 7,24,9,22,11,21,16,17,17,15,20,13,18,14,20,13, + 21,11,22,10,22,10,22,10,22,10,22,11,21,11,21,12, + 20,14,9,7,2,15,8,25,8,25,7,27,5,28,4,29, + 4,29,2,168,3,0,255,0,112,1,31,3,29,7,27,6, + 25,7,25,8,24,9,25,8,12,5,10,6,3,1,6,6, + 11,10,2,11,5,14,1,13,3,15,2,13,1,16,2,30, + 3,29,4,28,4,28,6,9,1,7,1,1,2,5,9,5, + 2,7,7,2,11,1,3,8,23,8,22,10,1,1,20,16, + 17,16,17,18,15,17,16,16,17,15,17,15,3,0,255,0, + 113,1,31,3,30,6,28,5,27,5,26,7,25,8,24,9, + 27,6,10,4,13,9,5,6,9,12,2,11,4,15,1,13, + 2,16,1,13,1,17,1,31,3,29,3,29,4,22,1,5, + 5,10,1,7,7,2,8,6,2,7,8,1,15,8,22,9, + 22,12,20,16,18,15,18,17,15,17,16,16,17,15,3,0, + 255,0,22,1,31,2,30,3,30,4,29,5,28,4,29,3, + 18,2,9,4,17,3,9,3,17,12,1,2,18,11,1,3, + 23,4,2,3,22,5,2,5,19,13,5,4,1,1,6,15, + 4,9,3,16,3,10,2,17,3,29,2,30,1,31,1,31, + 1,23,5,3,2,22,7,1,5,18,22,10,21,15,17,17, + 16,18,16,17,15,17,16,16,17,15,6,138,194,162,202,163, + 210,195,219,2,227,3,227,162,235,4,235,162,242,201,250,201, + 6,148,97,162,105,196,114,4,122,36,130,68,138,101,146,133, + 154,165,162,198,171,6,179,38,187,70,195,103,203,136,211,200, + 219,233,228,8,236,39,244,70,252,132,3,0,219,4,27,6, + 25,7,22,10,21,11,21,11,21,11,20,11,21,9,14,2, + 10,6,2,1,5,3,1,5,8,6,2,2,6,10,3,13, + 7,25,6,26,1,63,6,26,6,26,7,15,2,8,9,2, + 16,4,255,0,194,3,0,201,3,27,7,24,8,23,10,22, + 10,1,1,20,14,19,17,15,17,4,1,1,2,9,8,4, + 3,18,7,4,2,19,7,4,7,3,2,9,6,4,13,10, + 5,4,13,10,22,8,24,8,24,8,24,7,18,6,1,8, + 17,6,1,8,19,3,2,7,25,6,3,1,6,4,12,5, + 3,1,6,9,8,4,3,2,5,14,4,3,4,27,2,1, + 1,28,1,253,3,0,233,1,28,6,25,8,23,9,23,9, + 2,2,19,19,14,18,6,1,7,10,5,3,16,8,5,3, + 17,8,4,12,9,6,5,12,9,6,5,12,11,5,1,15, + 10,22,9,23,9,18,4,1,9,17,15,23,9,23,9,23, + 8,11,6,7,8,9,9,5,8,3,1,6,10,3,8,3, + 2,4,23,3,3,1,24,3,28,2,1,1,27,1,188,3, + 0,255,0,39,6,25,7,24,9,2,2,19,9,1,7,15, + 20,1,4,1,2,4,12,1,7,13,11,5,2,16,9,5, + 11,9,7,5,11,9,7,5,11,10,5,5,12,13,4,1, + 14,12,20,10,17,15,17,15,19,13,22,10,22,10,22,10, + 10,8,4,10,2,1,6,10,3,9,3,1,5,23,2,2, + 4,23,2,3,3,23,4,27,4,28,1,30,1,122,3,0, + 255,0,96,1,30,5,4,2,20,8,1,16,4,4,2,5, + 1,11,8,5,2,9,3,6,6,17,6,3,5,6,3,10, + 6,3,2,8,3,10,7,25,5,1,2,14,4,7,2,19, + 5,4,3,20,11,21,10,22,10,22,10,22,11,21,11,12, + 4,5,10,12,19,3,1,8,19,3,2,6,21,1,4,2, + 24,2,4,2,30,2,209,3,0,255,0,69,1,25,1,4, + 7,7,1,1,3,1,3,1,4,4,15,1,1,15,14,15, + 2,1,4,1,10,13,9,7,4,10,13,6,5,6,15,7, + 5,3,18,7,5,1,20,4,28,3,27,4,28,4,20,3, + 3,3,21,11,21,10,22,10,22,11,21,11,21,12,15,17, + 15,17,14,18,14,18,13,18,2,1,3,1,7,18,1,2, + 3,1,6,22,2,4,2,23,3,29,3,29,4,15,3,0, + 27,5,28,4,29,3,29,3,30,2,255,0,127,2,29,7, + 25,8,7,1,16,9,5,2,16,9,23,9,22,9,22,10, + 21,11,20,7,21,11,19,13,18,14,18,14,18,14,18,14, + 17,15,17,15,16,16,13,19,9,23,7,25,6,26,6,26, + 6,135,154,129,162,99,170,68,178,38,186,38,194,68,202,129, + 7,2,16,128,0,39,3,16,1,2,6,12,14,10,14,14, + 9,23,1,28,7,6,49,140,0,14,5,1,4,20,12,21, + 1,1,9,24,8,7,1,5,3,9,2,8,5,4,5,5, + 6,2,10,4,6,2,20,3,29,3,29,4,28,6,5,1, + 3,6,11,23,9,26,6,28,4,30,2,32,7,7,57,204, + 0,107,6,4,2,8,17,7,17,7,17,20,12,20,4,1, + 7,21,3,5,2,22,4,25,8,3,3,4,2,9,17,3, + 8,3,18,2,9,2,19,1,31,1,31,1,20,3,8,2, + 16,9,5,2,12,14,4,3,10,17,2,6,2,56,3,0, + 255,0,123,6,21,11,20,9,23,12,20,12,26,6,27,5, + 27,5,26,5,26,5,27,5,26,5,18,3,5,6,1,2, + 11,21,2,1,7,28,3,30,1,138,5,24,11,21,13,15, + 19,13,21,3,125,7,15,123,156,0,165,5,26,7,25,8, + 25,8,27,6,26,8,24,9,23,10,22,11,25,8,24,9, + 23,10,22,10,22,9,23,9,23,7,25,5,15,1,12,1, + 2,1,2,2,11,1,13,8,10,1,11,10,10,1,10,11, + 18,14,16,16,10,6,149,105,226,113,196,121,196,129,200,137, + 229,146,4,154,6,162,8,170,43,178,75,186,105,194,136,202, + 138,210,134,211,100,218,132,219,130,226,161,227,66,235,34,243, + 33,7,6,56,196,0,113,2,10,3,9,2,8,5,13,1, + 3,2,7,6,11,10,3,8,1,2,8,23,2,1,6,19, + 1,2,2,1,8,18,5,1,13,4,1,10,2,1,2,1, + 15,3,1,10,2,1,4,5,4,1,1,1,5,3,106,6, + 160,42,101,50,102,58,41,66,42,74,43,82,76,90,75,98, + 73,106,105,113,163,114,137,121,164,122,169,129,226,130,139,137, + 227,138,108,146,16,154,17,162,3,162,142,170,142,178,143,186, + 174,194,174,202,173,210,203,218,202,226,203,234,172,242,172,250, + 172,6,156,4,194,9,194,12,225,17,195,20,225,25,196,33, + 197,41,230,49,231,58,71,66,104,74,138,82,171,84,162,90, + 210,99,16,107,109,115,171,123,233,131,230,132,225,139,201,147, + 202,155,202,163,202,172,8,180,101,188,132,6,152,3,228,11, + 168,19,138,20,225,27,109,35,109,43,109,51,109,59,140,67, + 109,75,109,83,171,91,202,100,8,107,231,115,230,123,167,131, + 135,139,72,147,8,154,200,162,105,170,67,178,97,7,0,115, + 222,0,104,2,6,4,4,5,3,18,6,17,1,2,4,12, + 2,2,8,8,16,5,19,3,255,0,198,6,7,0,9,3, + 101,4,101,8,1,8,164,11,162,12,225,6,5,50,130,99, + 97,121,129,137,193,232,97,7,3,205,103,13,4,8,5,13, + 8,6,5,11,11,4,6,10,12,4,6,10,12,3,7,9, + 23,9,25,7,23,9,25,7,13,1,5,3,2,8,12,21, + 8,11,2,12,6,202,1,158,1,7,7,3,220,231,107,3, + 15,2,9,8,10,5,7,10,10,4,7,11,10,5,5,10, + 11,6,4,11,10,7,4,11,10,8,2,10,10,10,2,10, + 5,2,2,11,1,31,1,31,1,31,1,9,3,19,1,6, + 1,1,6,17,1,4,11,3,15,2,13,1,31,1,206,3, + 219,2,13,7,9,4,10,9,8,5,8,11,8,6,6,11, + 8,7,5,11,9,8,3,10,10,9,3,10,10,9,2,11, + 9,10,2,18,1,11,2,30,2,30,2,4,1,4,2,19, + 2,2,4,1,5,18,17,2,11,2,16,3,30,1,136,1, + 100,1,255,0,65,6,30,50,129,58,100,66,69,74,38,82, + 37,90,36,98,68,106,68,114,69,122,69,130,69,138,70,146, + 68,146,226,147,65,154,106,162,78,170,80,178,38,179,41,186, + 7,187,34,193,201,201,170,209,139,217,109,225,78,233,109,241, + 109,249,140,7,7,113,140,52,2,5,4,27,5,26,7,24, + 8,16,3,4,9,7,5,3,6,1,10,6,12,2,12,4, + 63,4,3,3,19,14,2,3,13,25,7,29,3,192,6,33, + 52,34,59,229,67,199,75,199,83,200,91,200,99,231,108,6, + 116,37,122,46,124,5,128,72,129,216,136,20,139,46,144,82, + 147,232,154,97,156,8,162,97,164,8,170,34,172,39,180,39, + 188,39,196,39,204,38,212,70,220,70,228,39,236,8,244,8, + 252,8,3,22,4,28,4,28,4,28,4,28,4,28,4,28, + 4,28,5,27,5,3,1,23,9,18,2,1,8,20,15,15, + 16,15,17,13,17,13,19,4,1,5,20,6,2,2,22,6, + 20,1,5,6,15,6,5,6,13,8,5,6,10,11,5,6, + 8,14,4,6,6,16,4,6,2,20,5,23,2,2,5,21, + 3,3,5,20,2,5,5,18,4,5,5,15,17,13,19,12, + 21,9,23,8,24,6,20,1,5,4,16,8,4,4,16,8, + 4,4,15,9,5,3,12,13,4,3,9,16,4,3,3,18, + 14,18,14,18,14,18,2,5,7,18,2,11,1,17,2,30, + 2,29,2,30,2,30,2,30,1,31,1,31,1,31,2,30, + 2,30,2,30,2,30,2,31,1,31,2,30,2,30,2,31, + 2,30,2,30,4,8,2,1,1,6,1,1,3,2,8,3, + 6,1,1,4,255,0,9,4,29,3,15,2,2,2,14,2, + 10,9,10,5,8,14,4,8,6,28,3,3,42,3,30,13, + 19,17,15,19,13,21,11,21,11,2,16,3,11,2,30,1, + 30,2,30,1,31,1,31,1,31,1,31,1,31,1,20,1, + 7,2,1,2,15,5,4,9,12,7,4,9,1,1,8,9, + 3,29,3,29,3,29,3,29,4,6,2,10,2,8,4,1, + 8,8,24,8,25,7,25,2,2,3,26,6,27,5,27,3, + 30,3,31,2,31,2,31,3,31,3,31,2,31,3,29,3, + 3,2,138,3,35,8,31,8,32,8,32,9,31,9,30,9, + 32,8,31,9,25,3,3,8,22,18,18,6,6,9,19,4, + 12,6,18,2,15,8,32,9,31,10,30,12,28,9,1,4, + 26,9,3,3,25,9,1,5,26,15,25,12,2,2,24,5, + 4,3,3,2,23,5,11,2,21,6,12,1,21,6,12,2, + 19,7,13,2,17,8,13,2,17,8,14,2,1,2,54,3, + 35,10,29,10,29,11,29,11,29,12,28,12,28,11,28,12, + 27,13,29,11,19,20,20,20,27,13,29,10,31,10,31,11, + 31,12,28,14,26,14,27,15,25,16,24,12,1,5,22,13, + 2,4,22,11,3,5,21,19,21,20,20,17,1,3,19,16, + 3,3,18,7,6,3,4,2,18,7,14,2,2,2,22,3, + 34,7,32,9,30,14,26,13,27,12,26,13,26,14,28,13, + 27,12,14,9,4,12,13,27,13,4,3,19,14,1,11,14, + 29,11,31,8,33,7,33,8,32,11,28,13,28,14,26,8, + 1,5,26,15,25,16,24,13,1,3,23,13,2,4,21,13, + 2,5,20,13,1,6,20,21,19,21,19,17,2,3,18,8, + 5,4,3,2,3,0,177,15,12,20,7,25,3,255,0,255, + 0,255,0,32,2,29,5,25,7,24,9,1,4,16,28,2, + 72,3,255,0,65,1,31,4,28,6,26,8,24,9,23,11, + 22,11,27,6,30,3,31,2,255,0,255,0,147,6,1,98, + 98,6,10,122,38,130,8,138,8,146,8,154,8,161,234,169, + 204,177,204,185,234,194,68,6,17,194,98,201,234,209,138,210, + 229,217,99,218,68,219,36,225,67,226,68,227,68,233,67,234, + 67,235,99,241,36,243,99,249,68,251,68,7,7,16,128,85, + 3,8,1,6,9,8,16,7,17,7,17,8,16,14,10,21, + 3,64,7,7,16,128,62,2,14,3,2,5,3,2,6,13, + 3,21,3,21,3,21,3,21,3,2,6,13,14,3,2,5, + 6,2,56,7,7,56,192,89,2,21,3,20,5,18,5,5, + 4,4,2,1,7,2,28,1,100,3,16,10,14,13,11,6, + 3,6,1,7,2,15,1,87,6,26,57,129,58,6,65,138, + 73,139,81,170,89,169,97,200,105,200,113,199,121,200,129,232, + 137,233,145,234,153,234,161,235,169,235,177,203,185,204,193,205, + 201,174,209,173,217,142,225,111,233,112,241,80,249,81,7,5, + 57,206,128,75,1,31,1,31,1,13,4,6,1,1,1,10, + 7,4,3,10,16,8,16,8,7,3,5,10,4,6,1,1, + 1,11,3,7,1,23,1,31,1,84,128,3,0,255,0,55, + 1,31,2,27,4,26,5,24,7,24,5,26,4,27,4,17, + 3,7,4,18,13,5,4,10,12,5,4,11,12,2,1,1, + 4,12,20,12,20,12,20,12,21,11,21,11,8,2,11,11, + 6,3,4,2,6,11,4,5,3,4,5,26,5,27,1,255, + 0,49,3,0,104,3,29,6,18,2,6,2,1,4,17,9, + 2,4,17,15,17,16,16,16,16,17,17,15,20,1,3,3, + 2,2,26,2,255,0,245,254,2,23,9,23,9,23,9,3, + 0,72,1,3,1,19,10,1,2,19,14,18,14,18,4,2, + 6,20,3,30,1,255,0,127,255,0,152,3,28,5,25,7, + 1,4,19,13,19,2,1,10,24,3,2,3,24,2,38,7, + 12,64,99,0,32,4,2,1,4,2,3,10,1,2,3,15, + 1,14,2,5,4,1,1,2,4,3,5,1,2,1,4,2, + 7,1,117,56,1,13,3,13,5,8,8,8,13,2,14,6, + 10,6,2,24,7,14,80,231,0,3,4,13,4,12,4,13, + 4,12,4,13,4,12,4,13,3,13,3,14,2,14,2,15, + 1,186,12,12,167,1,6,3,9,1,2,4,9,1,2,5, + 8,9,7,9,7,9,8,8,9,7,9,6,2,3,0,52, + 12,18,14,16,16,14,18,13,3,6,10,12,1,13,6,11, + 1,17,3,10,1,19,2,31,9,24,8,24,7,8,4,13, + 7,8,4,13,7,9,3,13,7,9,2,14,8,24,8,24, + 9,23,11,21,13,3,6,10,24,8,25,7,15,2,9,6, + 15,3,8,6,14,5,8,5,15,4,8,6,26,6,26,7, + 24,9,23,10,26,7,16,1,10,7,12,3,10,13,1,6, + 12,19,13,17,15,15,17,12,52,3,0,11,10,19,17,12, + 22,9,24,6,28,3,30,1,120,5,25,10,20,12,19,13, + 18,14,17,15,17,15,16,16,16,8,4,4,16,8,5,3, + 4,4,8,8,5,3,3,5,8,8,5,3,3,5,8,9, + 3,4,4,4,8,16,16,16,15,17,15,17,14,18,14,18, + 13,19,11,23,7,119,1,30,3,28,6,24,9,22,12,18, + 17,12,10,2,0,2,38,2,38,1,187,2,37,4,35,5, + 35,5,35,5,18,4,14,3,16,10,29,12,26,16,20,1, + 2,17,20,1,2,18,18,2,1,20,16,3,1,21,14,15, + 1,12,11,14,4,13,7,16,5,34,6,34,6,35,5,36, + 3,186,1,38,2,38,2,2,187,3,36,5,35,6,34,6, + 34,6,14,6,15,4,13,10,28,14,25,16,23,18,22,19, + 21,20,20,20,20,21,19,21,19,22,18,23,16,10,4,12, + 12,11,6,13,8,13,6,34,7,33,6,34,6,35,4,187, + 3,0,13,6,26,5,255,0,47,81,2,30,2,31,1,31, + 1,31,1,31,1,25,1,5,1,24,4,3,1,24,5,2, + 1,24,5,1,2,23,10,22,11,22,10,22,10,22,11,23, + 9,27,4,28,4,28,3,29,3,28,5,27,5,27,5,27, + 5,28,3,76,3,0,14,4,255,0,47,241,1,31,2,31, + 1,31,2,31,1,31,1,31,1,63,2,22,3,4,4,22, + 10,22,12,21,1,1,9,22,1,3,5,29,3,29,4,29, + 3,29,4,28,4,28,4,29,3,71,3,105,2,31,2,58, + 11,19,15,16,17,12,21,11,21,11,21,11,21,11,21,11, + 21,11,21,11,21,11,21,11,21,11,21,11,22,10,22,3, + 2,5,20,5,25,8,24,8,24,8,24,7,26,5,29,1, + 255,0,107,3,0,255,0,170,2,2,2,23,10,21,12,7, + 8,4,28,5,27,5,27,5,27,5,9,2,16,5,27,5, + 7,7,13,5,3,10,14,17,5,1,9,17,4,4,7,29, + 2,255,0,162,0,0, +}; diff --git a/ch32_decoder/funconfig.h b/ch32_decoder/funconfig.h new file mode 100644 index 0000000..998cf76 --- /dev/null +++ b/ch32_decoder/funconfig.h @@ -0,0 +1,7 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define CH32V003 1 + +#endif + diff --git a/ch32_decoder/gdbinit b/ch32_decoder/gdbinit new file mode 100644 index 0000000..a0bb3de --- /dev/null +++ b/ch32_decoder/gdbinit @@ -0,0 +1,2 @@ +#set debug remote 1 +target extended-remote /dev/ttyACM1 diff --git a/ch32_decoder/ssd1306.h b/ch32_decoder/ssd1306.h new file mode 100644 index 0000000..4e43133 --- /dev/null +++ b/ch32_decoder/ssd1306.h @@ -0,0 +1,586 @@ +/* + * Single-File-Header for using SPI OLED + * 05-05-2023 E. Brombaugh + */ + +#ifndef _SSD1306_H +#define _SSD1306_H + +#include +#include + +// comfortable packet size for this OLED +#define SSD1306_PSZ 32 + +// characteristics of each type +#if !defined(SSD1306_64X32) && !defined(SSD1306_128X32) && !defined(SSD1306_128X64) +#error "Please define the SSD1306_WXH resolution used in your application" +#endif + +#ifdef SSD1306_64X32 +#define SSD1306_W 64 +#define SSD1306_H 32 +#define SSD1306_FULLUSE +#define SSD1306_OFFSET 32 +#endif + +#ifdef SSD1306_128X32 +#define SSD1306_W 128 +#define SSD1306_H 32 +#define SSD1306_OFFSET 0 +#endif + +#ifdef SSD1306_128X64 +#define SSD1306_W 128 +#define SSD1306_H 64 +#define SSD1306_FULLUSE +#define SSD1306_OFFSET 0 +#endif + +/* + * send OLED command byte + */ +uint8_t ssd1306_cmd(uint8_t cmd) +{ + ssd1306_pkt_send(&cmd, 1, 1); + return 0; +} + +/* + * send OLED data packet (up to 32 bytes) + */ +uint8_t ssd1306_data(uint8_t *data, uint8_t sz) +{ + ssd1306_pkt_send(data, sz, 0); + return 0; +} + +#define SSD1306_SETCONTRAST 0x81 +#define SSD1306_SEGREMAP 0xA0 +#define SSD1306_DISPLAYALLON_RESUME 0xA4 +#define SSD1306_DISPLAYALLON 0xA5 +#define SSD1306_NORMALDISPLAY 0xA6 +#define SSD1306_INVERTDISPLAY 0xA7 +#define SSD1306_DISPLAYOFF 0xAE +#define SSD1306_DISPLAYON 0xAF +#define SSD1306_SETDISPLAYOFFSET 0xD3 +#define SSD1306_SETCOMPINS 0xDA +#define SSD1306_SETVCOMDETECT 0xDB +#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 +#define SSD1306_SETPRECHARGE 0xD9 +#define SSD1306_SETMULTIPLEX 0xA8 +#define SSD1306_SETLOWCOLUMN 0x00 +#define SSD1306_SETHIGHCOLUMN 0x10 +#define SSD1306_SETSTARTLINE 0x40 +#define SSD1306_MEMORYMODE 0x20 +#define SSD1306_COLUMNADDR 0x21 +#define SSD1306_PAGEADDR 0x22 +#define SSD1306_COMSCANINC 0xC0 +#define SSD1306_COMSCANDEC 0xC8 +#define SSD1306_CHARGEPUMP 0x8D +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 +#define SSD1306_TERMINATE_CMDS 0xFF + +/* choose VCC mode */ +#define SSD1306_EXTERNALVCC 0x1 +#define SSD1306_SWITCHCAPVCC 0x2 +// #define vccstate SSD1306_EXTERNALVCC +#define vccstate SSD1306_SWITCHCAPVCC + +// OLED initialization commands for 128x32 +const uint8_t ssd1306_init_array[] = + { + SSD1306_DISPLAYOFF, // 0xAE + SSD1306_SETDISPLAYCLOCKDIV, // 0xD5 + 0x80, // the suggested ratio 0x80 + SSD1306_SETMULTIPLEX, // 0xA8 +#ifdef SSD1306_64X32 + 0x1F, // for 64-wide displays +#else + 0x3F, // for 128-wide displays +#endif + SSD1306_SETDISPLAYOFFSET, // 0xD3 + 0x00, // no offset + SSD1306_SETSTARTLINE | 0x0, // 0x40 | line + SSD1306_CHARGEPUMP, // 0x8D + 0x14, // enable? + SSD1306_MEMORYMODE, // 0x20 + 0x00, // 0x0 act like ks0108 + SSD1306_SEGREMAP | 0x1, // 0xA0 | bit + SSD1306_COMSCANDEC, + SSD1306_SETCOMPINS, // 0xDA + 0x12, // + SSD1306_SETCONTRAST, // 0x81 + 0x8F, + SSD1306_SETPRECHARGE, // 0xd9 + 0xF1, + SSD1306_SETVCOMDETECT, // 0xDB + 0x40, + SSD1306_DISPLAYALLON_RESUME, // 0xA4 + SSD1306_NORMALDISPLAY, // 0xA6 + SSD1306_DISPLAYON, // 0xAF --turn on oled panel + SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end +}; + +// the display buffer +uint8_t ssd1306_buffer[SSD1306_W * SSD1306_H / 8]; + +/* + * set the buffer to a color + */ +void ssd1306_setbuf(uint8_t color) +{ + memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer)); +} + +#ifndef SSD1306_FULLUSE +/* + * expansion array for OLED with every other row unused + */ +const uint8_t expand[16] = + { + 0x00, // 0000 0000 + 0x02, // 0000 0010 + 0x08, // 0000 1000 + 0x0a, // 0000 1010 + 0x20, // 0010 0000 + 0x22, // 0010 0010 + 0x28, // 0010 1000 + 0x2a, // 0010 1010 + 0x80, // 1000 0000 + 0x82, // 1000 0010 + 0x88, // 1000 1000 + 0x8a, // 1000 1010 + 0xa0, // 1010 0000 + 0xa2, // 1010 0010 + 0xa8, // 1010 1000 + 0xaa, // 1010 1010 +}; +#endif + +/* + * Send the frame buffer + */ +void ssd1306_refresh(void) +{ + uint16_t i; + + ssd1306_cmd(SSD1306_COLUMNADDR); + ssd1306_cmd(SSD1306_OFFSET); // Column start address (0 = reset) + ssd1306_cmd(SSD1306_OFFSET + SSD1306_W - 1); // Column end address (127 = reset) + + ssd1306_cmd(SSD1306_PAGEADDR); + ssd1306_cmd(0); // Page start address (0 = reset) + ssd1306_cmd(7); // Page end address + +#ifdef SSD1306_FULLUSE + /* for fully used rows just plow thru everything */ + for (i = 0; i < sizeof(ssd1306_buffer); i += SSD1306_PSZ) + { + /* send PSZ block of data */ + ssd1306_data(&ssd1306_buffer[i], SSD1306_PSZ); + } +#else + /* for displays with odd rows unused expand bytes */ + uint8_t tbuf[SSD1306_PSZ], j, k; + for (i = 0; i < sizeof(ssd1306_buffer); i += 128) // for each page + { + /* low nybble */ + for (j = 0; j < 128; j += SSD1306_PSZ) + { + for (k = 0; k < SSD1306_PSZ; k++) + tbuf[k] = expand[ssd1306_buffer[i + j + k] & 0xf]; + + /* send PSZ block of data */ + ssd1306_data(tbuf, SSD1306_PSZ); + } + + /* high nybble */ + for (j = 0; j < 128; j += SSD1306_PSZ) + { + for (k = 0; k < SSD1306_PSZ; k++) + tbuf[k] = expand[(ssd1306_buffer[i + j + k] >> 4) & 0xf]; + + /* send PSZ block of data */ + ssd1306_data(tbuf, SSD1306_PSZ); + } + } +#endif +} + +/* + * plot a pixel in the buffer + */ +void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color) +{ + uint16_t addr; + + /* clip */ + if (x >= SSD1306_W) + return; + if (y >= SSD1306_H) + return; + + /* compute buffer address */ + addr = x + SSD1306_W * (y / 8); + + /* set/clear bit in buffer */ + if (color) + ssd1306_buffer[addr] |= (1 << (y & 7)); + else + ssd1306_buffer[addr] &= ~(1 << (y & 7)); +} + +/* + * plot a pixel in the buffer + */ +void ssd1306_xorPixel(uint8_t x, uint8_t y) +{ + uint16_t addr; + + /* clip */ + if (x >= SSD1306_W) + return; + if (y >= SSD1306_H) + return; + + /* compute buffer address */ + addr = x + SSD1306_W * (y / 8); + + ssd1306_buffer[addr] ^= (1 << (y & 7)); +} + +/* + * draw a an image from an array, directly into to the display buffer + * the color modes allow for overwriting and even layering (sprites!) + */ +void ssd1306_drawImage(uint8_t x, uint8_t y, const unsigned char *input, uint8_t width, uint8_t height, uint8_t color_mode) +{ + uint8_t x_absolute; + uint8_t y_absolute; + uint8_t pixel; + uint8_t bytes_to_draw = width / 8; + uint16_t buffer_addr; + + for (uint8_t line = 0; line < height; line++) + { + y_absolute = y + line; + if (y_absolute >= SSD1306_H) + { + break; + } + + // SSD1306 is in vertical mode, yet we want to draw horizontally, which necessitates assembling the output bytes from the input data + // bitmask for current pixel in vertical (output) byte + uint8_t v_mask = 1 << (y_absolute & 7); + + for (uint8_t byte = 0; byte < bytes_to_draw; byte++) + { + uint8_t input_byte = input[byte + line * bytes_to_draw]; + + for (pixel = 0; pixel < 8; pixel++) + { + x_absolute = x + 8 * (bytes_to_draw - byte) + pixel; + if (x_absolute >= SSD1306_W) + { + break; + } + // looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8 + buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8); + // state of current pixel + uint8_t input_pixel = input_byte & (1 << pixel); + + switch (color_mode) + { + case 0: + // write pixels as they are + ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0); + break; + case 1: + // write pixels after inversion + ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0); + break; + case 2: + // 0 clears pixel + ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask; + break; + case 3: + // 1 sets pixel + ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0; + break; + case 4: + // 0 sets pixel + ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0; + break; + case 5: + // 1 clears pixel + ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF; + break; + } + } +#if SSD1306_LOG_IMAGE == 1 + printf("%02x ", input_byte); +#endif + } +#if SSD1306_LOG_IMAGE == 1 + printf("\n\r"); +#endif + } +} + +/* + * fast vert line + */ +void ssd1306_drawFastVLine(uint8_t x, uint8_t y, uint8_t h, uint8_t color) +{ + // clipping + if ((x >= SSD1306_W) || (y >= SSD1306_H)) + return; + if ((y + h - 1) >= SSD1306_H) + h = SSD1306_H - y; + while (h--) + { + ssd1306_drawPixel(x, y++, color); + } +} + +/* + * fast horiz line + */ +void ssd1306_drawFastHLine(uint8_t x, uint8_t y, uint8_t w, uint8_t color) +{ + // clipping + if ((x >= SSD1306_W) || (y >= SSD1306_H)) + return; + if ((x + w - 1) >= SSD1306_W) + w = SSD1306_W - x; + + while (w--) + { + ssd1306_drawPixel(x++, y, color); + } +} + +/* + * abs() helper function for line drawing + */ +int16_t gfx_abs(int16_t x) +{ + return (x < 0) ? -x : x; +} + +/* + * swap() helper function for line drawing + */ +void gfx_swap(uint16_t *z0, uint16_t *z1) +{ + uint16_t temp = *z0; + *z0 = *z1; + *z1 = temp; +} + +/* + * Bresenham line draw routine swiped from Wikipedia + */ +void ssd1306_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t color) +{ + int16_t steep; + int16_t deltax, deltay, error, ystep, x, y; + + /* flip sense 45deg to keep error calc in range */ + steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0)); + + if (steep) + { + gfx_swap(&x0, &y0); + gfx_swap(&x1, &y1); + } + + /* run low->high */ + if (x0 > x1) + { + gfx_swap(&x0, &x1); + gfx_swap(&y0, &y1); + } + + /* set up loop initial conditions */ + deltax = x1 - x0; + deltay = gfx_abs(y1 - y0); + error = deltax / 2; + y = y0; + if (y0 < y1) + ystep = 1; + else + ystep = -1; + + /* loop x */ + for (x = x0; x <= x1; x++) + { + /* plot point */ + if (steep) + /* flip point & plot */ + ssd1306_drawPixel(y, x, color); + else + /* just plot */ + ssd1306_drawPixel(x, y, color); + + /* update error */ + error = error - deltay; + + /* update y */ + if (error < 0) + { + y = y + ystep; + error = error + deltax; + } + } +} + +/* + * draws a circle + */ +void ssd1306_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color) +{ + /* Bresenham algorithm */ + int16_t x_pos = -radius; + int16_t y_pos = 0; + int16_t err = 2 - 2 * radius; + int16_t e2; + + do + { + ssd1306_drawPixel(x - x_pos, y + y_pos, color); + ssd1306_drawPixel(x + x_pos, y + y_pos, color); + ssd1306_drawPixel(x + x_pos, y - y_pos, color); + ssd1306_drawPixel(x - x_pos, y - y_pos, color); + e2 = err; + if (e2 <= y_pos) + { + err += ++y_pos * 2 + 1; + if (-x_pos == y_pos && e2 <= x_pos) + { + e2 = 0; + } + } + if (e2 > x_pos) + { + err += ++x_pos * 2 + 1; + } + } while (x_pos <= 0); +} + +/* + * draws a filled circle + */ +void ssd1306_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color) +{ + /* Bresenham algorithm */ + int16_t x_pos = -radius; + int16_t y_pos = 0; + int16_t err = 2 - 2 * radius; + int16_t e2; + + do + { + ssd1306_drawPixel(x - x_pos, y + y_pos, color); + ssd1306_drawPixel(x + x_pos, y + y_pos, color); + ssd1306_drawPixel(x + x_pos, y - y_pos, color); + ssd1306_drawPixel(x - x_pos, y - y_pos, color); + ssd1306_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color); + ssd1306_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color); + e2 = err; + if (e2 <= y_pos) + { + err += ++y_pos * 2 + 1; + if (-x_pos == y_pos && e2 <= x_pos) + { + e2 = 0; + } + } + if (e2 > x_pos) + { + err += ++x_pos * 2 + 1; + } + } while (x_pos <= 0); +} + +/* + * draw a rectangle + */ +void ssd1306_drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + ssd1306_drawFastVLine(x, y, h, color); + ssd1306_drawFastVLine(x + w - 1, y, h, color); + ssd1306_drawFastHLine(x, y, w, color); + ssd1306_drawFastHLine(x, y + h - 1, w, color); +} + +/* + * fill a rectangle + */ +void ssd1306_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) +{ + uint8_t m, n = y, iw = w; + + /* scan vertical */ + while (h--) + { + m = x; + w = iw; + /* scan horizontal */ + while (w--) + { + /* invert pixels */ + ssd1306_drawPixel(m++, n, color); + } + n++; + } +} + +/* + * invert a rectangle in the buffer + */ +void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h) +{ + uint8_t m, n = y, iw = w; + + /* scan vertical */ + while (h--) + { + m = x; + w = iw; + /* scan horizontal */ + while (w--) + { + /* invert pixels */ + ssd1306_xorPixel(m++, n); + } + n++; + } +} + +/* + * initialize I2C and OLED + */ +uint8_t ssd1306_init(void) +{ + // pulse reset + ssd1306_rst(); + + // initialize OLED + uint8_t *cmd_list = (uint8_t *)ssd1306_init_array; + while (*cmd_list != SSD1306_TERMINATE_CMDS) + { + if (ssd1306_cmd(*cmd_list++)) + return 1; + } + + // clear display + ssd1306_setbuf(0); + ssd1306_refresh(); + + return 0; +} + +#endif diff --git a/ch32_decoder/ssd1306_i2c.h b/ch32_decoder/ssd1306_i2c.h new file mode 100644 index 0000000..023157f --- /dev/null +++ b/ch32_decoder/ssd1306_i2c.h @@ -0,0 +1,363 @@ +/* + * Single-File-Header for SSD1306 I2C interface + * 05-07-2023 E. Brombaugh + */ + +#ifndef _SSD1306_I2C_H +#define _SSD1306_I2C_H + +#include + +// SSD1306 I2C address +#define SSD1306_I2C_ADDR 0x3c + +// I2C Bus clock rate - must be lower the Logic clock rate +#define SSD1306_I2C_CLKRATE 1000000 + +// I2C Logic clock rate - must be higher than Bus clock rate +#define SSD1306_I2C_PRERATE 2000000 + +// uncomment this for high-speed 36% duty cycle, otherwise 33% +#define SSD1306_I2C_DUTY + +// I2C Timeout count +#define TIMEOUT_MAX 100000 + +// uncomment this to enable IRQ-driven operation +//#define SSD1306_I2C_IRQ + +#ifdef SSD1306_I2C_IRQ +// some stuff that IRQ mode needs +volatile uint8_t ssd1306_i2c_send_buffer[64], *ssd1306_i2c_send_ptr, ssd1306_i2c_send_sz, ssd1306_i2c_irq_state; + +// uncomment this to enable time diags in IRQ +//#define IRQ_DIAG +#endif + +/* + * init just I2C + */ +void ssd1306_i2c_setup(void) +{ + uint16_t tempreg; + + // Reset I2C1 to init all regs + RCC->APB1PRSTR |= RCC_APB1Periph_I2C1; + RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1; + + // set freq + tempreg = I2C1->CTLR2; + tempreg &= ~I2C_CTLR2_FREQ; + tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK/SSD1306_I2C_PRERATE)&I2C_CTLR2_FREQ; + I2C1->CTLR2 = tempreg; + + // Set clock config + tempreg = 0; +#if (SSD1306_I2C_CLKRATE <= 100000) + // standard mode good to 100kHz + tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(2*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR; +#else + // fast mode over 100kHz +#ifndef SSD1306_I2C_DUTY + // 33% duty cycle + tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(3*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR; +#else + // 36% duty cycle + tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(25*SSD1306_I2C_CLKRATE))&I2C_CKCFGR_CCR; + tempreg |= I2C_CKCFGR_DUTY; +#endif + tempreg |= I2C_CKCFGR_FS; +#endif + I2C1->CKCFGR = tempreg; + +#ifdef SSD1306_I2C_IRQ + // enable IRQ driven operation + NVIC_EnableIRQ(I2C1_EV_IRQn); + + // initialize the state + ssd1306_i2c_irq_state = 0; +#endif + + // Enable I2C + I2C1->CTLR1 |= I2C_CTLR1_PE; + + // set ACK mode + I2C1->CTLR1 |= I2C_CTLR1_ACK; +} + +/* + * error descriptions + */ +char *errstr[] = +{ + "not busy", + "master mode", + "transmit mode", + "tx empty", + "transmit complete", +}; + +/* + * error handler + */ +uint8_t ssd1306_i2c_error(uint8_t err) +{ + // report error + printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]); + + // reset & initialize I2C + ssd1306_i2c_setup(); + + return 1; +} + +// event codes we use +#define SSD1306_I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */ +#define SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */ +#define SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */ + +/* + * check for 32-bit event codes + */ +uint8_t ssd1306_i2c_chk_evt(uint32_t event_mask) +{ + /* read order matters here! STAR1 before STAR2!! */ + uint32_t status = I2C1->STAR1 | (I2C1->STAR2<<16); + return (status & event_mask) == event_mask; +} + +#ifdef SSD1306_I2C_IRQ +/* + * packet send for IRQ-driven operation + */ +uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz) +{ + int32_t timeout; + +#ifdef IRQ_DIAG + GPIOC->BSHR = (1<<(3)); +#endif + + // error out if buffer under/overflow + if((sz > sizeof(ssd1306_i2c_send_buffer)) || !sz) + return 2; + + // wait for previous packet to finish + while(ssd1306_i2c_irq_state); + +#ifdef IRQ_DIAG + GPIOC->BSHR = (1<<(16+3)); + GPIOC->BSHR = (1<<(4)); +#endif + + // init buffer for sending + ssd1306_i2c_send_sz = sz; + ssd1306_i2c_send_ptr = ssd1306_i2c_send_buffer; + memcpy((uint8_t *)ssd1306_i2c_send_buffer, data, sz); + + // wait for not busy + timeout = TIMEOUT_MAX; + while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--)); + if(timeout==-1) + return ssd1306_i2c_error(0); + + // Set START condition + I2C1->CTLR1 |= I2C_CTLR1_START; + + // wait for master mode select + timeout = TIMEOUT_MAX; + while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--)); + if(timeout==-1) + return ssd1306_i2c_error(1); + + // send 7-bit address + write flag + I2C1->DATAR = addr<<1; + + // wait for transmit condition + timeout = TIMEOUT_MAX; + while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--)); + if(timeout==-1) + return ssd1306_i2c_error(2); + + // Enable TXE interrupt + I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN; + ssd1306_i2c_irq_state = 1; + +#ifdef IRQ_DIAG + GPIOC->BSHR = (1<<(16+4)); +#endif + + // exit + return 0; +} + +/* + * IRQ handler for I2C events + */ +void I2C1_EV_IRQHandler(void) __attribute__((interrupt)); +void I2C1_EV_IRQHandler(void) +{ + uint16_t STAR1, STAR2 __attribute__((unused)); + +#ifdef IRQ_DIAG + GPIOC->BSHR = (1<<(4)); +#endif + + // read status, clear any events + STAR1 = I2C1->STAR1; + STAR2 = I2C1->STAR2; + + /* check for TXE */ + if(STAR1 & I2C_STAR1_TXE) + { + /* check for remaining data */ + if(ssd1306_i2c_send_sz--) + I2C1->DATAR = *ssd1306_i2c_send_ptr++; + + /* was that the last byte? */ + if(!ssd1306_i2c_send_sz) + { + // disable TXE interrupt + I2C1->CTLR2 &= ~(I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN); + + // reset IRQ state + ssd1306_i2c_irq_state = 0; + + // wait for tx complete + while(!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)); + + // set STOP condition + I2C1->CTLR1 |= I2C_CTLR1_STOP; + } + } + +#ifdef IRQ_DIAG + GPIOC->BSHR = (1<<(16+4)); +#endif +} +#else +/* + * low-level packet send for blocking polled operation via i2c + */ +uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz) +{ + int32_t timeout; + + // wait for not busy + timeout = TIMEOUT_MAX; + while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--)); + if(timeout==-1) + return ssd1306_i2c_error(0); + + // Set START condition + I2C1->CTLR1 |= I2C_CTLR1_START; + + // wait for master mode select + timeout = TIMEOUT_MAX; + while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--)); + if(timeout==-1) + return ssd1306_i2c_error(1); + + // send 7-bit address + write flag + I2C1->DATAR = addr<<1; + + // wait for transmit condition + timeout = TIMEOUT_MAX; + while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--)); + if(timeout==-1) + return ssd1306_i2c_error(2); + + // send data one byte at a time + while(sz--) + { + // wait for TX Empty + timeout = TIMEOUT_MAX; + while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--)); + if(timeout==-1) + return ssd1306_i2c_error(3); + + // send command + I2C1->DATAR = *data++; + } + + // wait for tx complete + timeout = TIMEOUT_MAX; + while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--)); + if(timeout==-1) + return ssd1306_i2c_error(4); + + // set STOP condition + I2C1->CTLR1 |= I2C_CTLR1_STOP; + + // we're happy + return 0; +} +#endif + +/* + * high-level packet send for I2C + */ +uint8_t ssd1306_pkt_send(uint8_t *data, uint8_t sz, uint8_t cmd) +{ + uint8_t pkt[33]; + + /* build command or data packets */ + if(cmd) + { + pkt[0] = 0; + pkt[1] = *data; + } + else + { + pkt[0] = 0x40; + memcpy(&pkt[1], data, sz); + } + return ssd1306_i2c_send(SSD1306_I2C_ADDR, pkt, sz+1); +} + +/* + * init I2C and GPIO + */ +uint8_t ssd1306_i2c_init(void) +{ + // Enable GPIOC and I2C + RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; + RCC->APB1PCENR |= RCC_APB1Periph_I2C1; + + // PC1 is SDA, 10MHz Output, alt func, open-drain + GPIOC->CFGLR &= ~(0xf<<(4*1)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*1); + + // PC2 is SCL, 10MHz Output, alt func, open-drain + GPIOC->CFGLR &= ~(0xf<<(4*2)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*2); + +#ifdef IRQ_DIAG + // GPIO diags on PC3/PC4 + GPIOC->CFGLR &= ~(0xf<<(4*3)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*3); + GPIOC->BSHR = (1<<(16+3)); + GPIOC->CFGLR &= ~(0xf<<(4*4)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); + GPIOC->BSHR = (1<<(16+4)); +#endif + + // load I2C regs + ssd1306_i2c_setup(); + +#if 0 + // test if SSD1306 is on the bus by sending display off command + uint8_t command = 0xAF; + return ssd1306_pkt_send(&command, 1, 1); +#else + return 0; +#endif +} + +/* + * reset is not used for SSD1306 I2C interface + */ +void ssd1306_rst(void) +{ +} +#endif diff --git a/encoder/src/main.rs b/encoder/src/main.rs index feeb739..7a381bf 100644 --- a/encoder/src/main.rs +++ b/encoder/src/main.rs @@ -10,7 +10,7 @@ pub use util::*; const INSPECT_ENC: bool = false; const INSPECT_DEC: bool = false; -const MAX_ERROR: usize = 4; // max wrong pixels +const MAX_ERROR: usize = 0; // max wrong pixels const MAX_LOSS: usize = 16; // highest "loss" value tried for all lossy encodings const LOSSLESS_ENCODINGS: &[FrameEncoder] = &[ @@ -43,7 +43,7 @@ fn main() { last_frame = decoder(&last_frame, &encoded[reader..], &mut reader); if INSPECT_DEC { println!( - "\n{frame_type:?}, error: {}", + "\n{frame_type:?}, error: {}, index: {frame_index}", frame_error(&frames[frame_index], &last_frame) ); render_images(&frames[frame_index], &last_frame); @@ -63,7 +63,12 @@ fn main() { ); let mut export_string = String::from("// Generated by the `encoder` rust app\n"); - export_string += "typedef enum Encoding {\n"; + for (encoding, count) in stats { + if count > 0 { + export_string += &format!("#define USE_{encoding:?}\n"); + } + } + export_string += "\n\ntypedef enum Encoding {\n"; for (encoding, count) in stats { if count > 0 { export_string += &format!("\tEncoding_{encoding:?} = {},\n", encoding as u8); @@ -117,7 +122,7 @@ fn encode(frames: &[Frame]) -> Vec { // render_images(&frame, &decoded); // panic!("error in 'loss 0' compression"); // } - if error < MAX_ERROR { + if error <= MAX_ERROR { options.push(encoded); } else { // higher loss value will mean more error so can be skipped