Konstrukce generátoru náhody pro výběr maturitních otázek pro studenty.

Displej SH1106 na sběrnici SPI.

Čekání na náhodu

IMG 20260408 054200

Vygenerovaná otázka

IMG 20260408 054214

Zapojení displeje

pin na displeji pin na Picu

GND (bílo oranžová)

GND

VCC (oranžová)

3V3

SCK (černá)

SPI0 SCK (GP18)

SDA (červená)

SPI0 TX (GP19) MOSI

RES (zelená)

GP20 (reset displeje)

DC (bílá)

GP16

CS (žlutá)

GP17 (chip select)

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_w)

project(nahoda C CXX ASM)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()

add_executable(nahoda
		main.c
		sh1106_spi.c
		utf8.c
		font_spleen_8x16.c
		font_spleen_16x32.c
    )

target_link_libraries(nahoda pico_stdlib hardware_spi)

# create map/bin/hex file etc.
pico_enable_stdio_usb(nahoda 1)
pico_enable_stdio_uart(nahoda 0)
pico_add_extra_outputs(nahoda)
main.c
/* Generator nahody s  SH1106 OLED displejem (spi sběrnice)
 * (c) Jirka Chráska 2026; jirka@lixis.cz
 */
#include <stdlib.h>
#include <string.h>
#include <pico/stdlib.h>
#include "sh1106_spi.h"
#include "font.h"

sh1106_t disp;

extern bitmapFONT font_spleen_6x12;
extern bitmapFONT font_spleen_8x16;
extern bitmapFONT font_spleen_16x32;

#define WHITE 1
#define BLACK 0


void screen( const char *str1, const char *str2 )
{
    SH1106_clear(&disp);
    SH1106_drawString(&disp, 0,  0, &font_spleen_8x16, str1, WHITE);
    SH1106_drawString(&disp, 0, 32, &font_spleen_8x16, str2, WHITE);
    SH1106_draw(&disp);
}



#define BAUD  1000000
#define MOSI  19
#define CLK   18
#define DC    16
#define CS    17
#define RST   20
#define SIRKA 128
#define VYSKA 64

#define RANDOM_MIN 1
#define RANDOM_MAX 25
#define TL_PIN 15

int main() {
char buf1[128];
int x;
uint64_t t;
    memset(buf1,'\0',128);
    stdio_init_all();
    gpio_init(TL_PIN);
    gpio_set_dir(TL_PIN, GPIO_OUT);
    gpio_pull_down(TL_PIN);
    // inicializace SPI displeje
    //                       
    SH1106_init(&disp, spi0, BAUD, MOSI, CLK, DC, CS, RST, SIRKA, VYSKA);
    screen("Výběr maturitní otázky","Stiskněte       tlačítko");
    sleep_ms(1500);
    while( !gpio_get(TL_PIN) ) {
        sleep_ms(10);
    }
    t = time_us_64();
    SH1106_clear(&disp);
    srand((unsigned int) t);
    // x bude náhodné v rozsahu RANDOM_MIN až RANDOM_MAX
    x = rand() % (RANDOM_MAX + 1 - RANDOM_MIN) + RANDOM_MIN;
    screen("Máte            otázku:", "Můžete se jít   připravit.");
    sprintf(buf1,"%d",x);
    SH1106_drawString(&disp,96,0,&font_spleen_16x32, buf1, WHITE);
    SH1106_draw(&disp); 

    return 0;
}
sh1106_spi.c
/* sh1106_spi.c
 * Display driver for SH1106 spi bus
 * (c) Jirka Chráska 2026; <jirka@lixis.cz>
 */




#include "sh1106_spi.h"
#include "utf8.h"
#include "font.h"
#include <math.h>
#include <stdlib.h>

/*
Příkazy pro SH1102 z datasheetu (nejsou použité)
#define SH1106_DISPLAYOFF           0xAE
#define SH1106_DISPLAYCLKDIVIDE     0xD5
#define SH1106_OSCILLATORFREQ       0x80
#define SH1106_MULTIPLEXRATIO_1     0xA8
#define SH1106_MULTIPLEXRATIO_2     0x3F
#define SH1106_DISPLAYOFFSET_1      0xD3
#define SH1106_DISPLAYOFFSET_2      0x00
#define SH1106_DISPLAYSTARTLINE     0x40
#define SH1106_CHARGEPUMP_1         0xAD
#define SH1106_CHARGEPUMP_2         0x8B
#define SH1106_SEGMENTREMAP         0xA1
#define SH1106_COMOUTSCANDIR        0xC8
#define SH1106_COMPINHWCONFIG_1     0xDA
#define SH1106_COMPINHWCONFIG_2     0x12
#define SH1106_CONTRASTCTRL_1       0x81
#define SH1106_CONTRASTCTRL_2       0xFF
#define SH1106_PRECHARGEPERIOD_1    0xD9
#define SH1106_PRECHARGEPERIOD_2    0x1F
#define SH1106_VCOMHDESELLVL_1      0xDB
#define SH1106_VCOMHDESELLVL_2      0x40
#define SH1106_VPP                  0x33
#define SH1106_NORMALINVDISPL       0xA6
#define SH1106_DISPLAYON            0xAF

#define SH1106_PAGE0                0xB0
#define SH1106_PAGE_OFFSET(x)       (SH1106_PAGE0 + x)
---
#define SET_DISP        0xAE
#define SET_SCAN_DIR    0xC0
#define SET_SEG_REMAP   0xA0
#define LOW_COL_ADDR    0x00
#define HIGH_COL_ADDR   0x10
#define SET_PAGE_ADDR   0xB0
---
*/

static uint8_t pageBuffer[8][128+4];

inline static void swap(int32_t *a, int32_t *b) 
{
    int32_t *t=a;
    *a=*b;
    *b=*t;
};


// inicializace SPI a displeje v jednom
uint SH1106_init(sh1106_t *sh1106, spi_inst_t *spi, uint32_t baud, uint8_t mosi, uint8_t clk, uint8_t dc_pin, uint8_t cs_pin, uint8_t rst_pin, uint8_t width, uint8_t height) 
{
uint res;

    sh1106->width   = width;
    sh1106->height  = height;
    sh1106->pages   = height / 8;
    sh1106->spi     = spi;
    sh1106->baud    = baud;
    sh1106->mosi    = mosi;
    sh1106->clk     = clk;
    sh1106->dc_pin  = dc_pin;
    sh1106->cs_pin  = cs_pin;
    sh1106->rst_pin = rst_pin;
    sh1106->buffer  = (uint8_t **) pageBuffer;
    // inicializace reset pinu
    gpio_init(sh1106->rst_pin);
    gpio_set_dir(sh1106->rst_pin, GPIO_OUT);
    // inicializace CS pinu
    gpio_init(sh1106->cs_pin);
    gpio_set_dir(sh1106->cs_pin, GPIO_OUT);
    gpio_put(sh1106->cs_pin, 1); // CS->1, nelze komunikovat s displejem
    // inicializace SPI
    gpio_set_function(sh1106->mosi, GPIO_FUNC_SPI);
    gpio_set_function(sh1106->clk,  GPIO_FUNC_SPI);
    res = spi_init(sh1106->spi, sh1106->baud);
    // inicializace DC pinu
    gpio_init(sh1106->dc_pin);
    gpio_set_dir(sh1106->dc_pin, GPIO_OUT);
    gpio_put(sh1106->dc_pin,0);

    // reset displeje
    gpio_put(sh1106->rst_pin,0);
    sleep_ms(10);
    gpio_put(sh1106->rst_pin,1);
    sleep_ms(100);

    for(uint8_t i = 0; i < 8; i++) { // vymazání displeje (černá)
        for(uint8_t j = 0; j < 128+4; j++) {
            pageBuffer[i][j] = 0x00;
        }
    }
    SH1106_Write_CMD(sh1106, SET_SEG_REMAP | 0x01); // prohodit vodorovně 
    SH1106_Write_CMD(sh1106, SET_SCAN_DIR | 0x08);  // prohodit svisle 
    SH1106_Write_CMD(sh1106, SET_DISP | 0x01);
    sleep_ms(100);
return res;
}


// poslání příkazu do displeje
void SH1106_Write_CMD(sh1106_t *sh1106, uint8_t command) 
{
    uint8_t buffer[2];
    buffer[0] = 0x80;
    buffer[1] = command;
    gpio_put(sh1106->dc_pin, 0); // DC->0, bude se posílat příkaz
    gpio_put(sh1106->cs_pin, 0); // počátek komunikace s displejem
    spi_write_blocking(sh1106->spi, buffer, 2);
    gpio_put(sh1106->cs_pin, 1); // vypnutí komunikace s displejem
}

// poslání dat do displeje
void SH1106_Write_Data(sh1106_t *sh1106, uint8_t* data) 
{
    size_t bufsize = sh1106->width+1;
    uint8_t broadCastBuffer[bufsize];
    broadCastBuffer[0] = 0x40;
    for (int i = 0; i < sh1106->width; i++) {
        broadCastBuffer[i+1] = data[i];
    }
    gpio_put(sh1106->dc_pin, 1); // DC->1, budou se posílat data
    gpio_put(sh1106->cs_pin, 0); // počátek komunikace
    spi_write_blocking(sh1106->spi, broadCastBuffer, bufsize);
    gpio_put(sh1106->cs_pin, 1); // konec komunikace
}

// vykreslení celého pageBufferu na displeji
void SH1106_draw(sh1106_t *sh1106)
{
    for(uint8_t page = 0; page < sh1106->pages; page++){
        SH1106_Write_CMD(sh1106, SET_PAGE_ADDR | page);
        SH1106_Write_CMD(sh1106, LOW_COL_ADDR  | 0x01);
        SH1106_Write_CMD(sh1106, HIGH_COL_ADDR | 0x00);
        SH1106_Write_Data(sh1106, pageBuffer[page]);
    }
}

// kreslení bodu do pageBufferu
void SH1106_drawPixel(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t color)
{
    if(x > sh1106->width || y > sh1106->height){
        return;
    }
    if(color == 0){
        pageBuffer[y/8][x] &= ~(1 << (y % 8));
    }else{
        pageBuffer[y/8][x] |= (1 << (y % 8));
    }

}

// kreslení čáry -- dole je lepší funkce
// void SH1106_drawLine(sh1106_t *sh1106, int32_t x1, int32_t y1, int32_t x2, int32_t y2)
// {
//     if(x1 > x2) {
//         swap(&x1, &x2);
//         swap(&y1, &y2);
//     }
//     if(x1 == x2) {
//         if(y1 > y2) {
//             swap(&y1, &y2);
//         }
//         for( int32_t i=y1; i<=y2; ++i) {
//             SH1106_drawPixel(sh1106, x1, i, 1);
//         }
//         return;
//     }
//     
//     float m = (float) (y2-y1) / (float) (x2-x1);
//     for(int32_t i=x1; i<=x2; ++i) {
//         float y = m * (float) (i-x1) + (float) y1;
//         SH1106_drawPixel( sh1106, i, (uint32_t) y, 1);
//     }
// }


// kreslení čáry (Bresenham)
void SH1106_drawLine(sh1106_t *sh1106, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint8_t color)
{
   int dx =  abs(x1-x0), sx = x0<x1 ? 1 : -1;
   int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1;
   int err = dx+dy, e2; /* error value e_xy */

   for(;;){  /* loop */
      SH1106_drawPixel(sh1106, 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é linky do pageBufferu
void SH1106_drawHLine(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t w, uint8_t color)
{
    if(x > sh1106->width || y > sh1106->height){
        return;
    }
    if((x + w) > sh1106->width){
        w = sh1106->width - x;
    }
    for(uint8_t i = 0; i < w; i++){
        SH1106_drawPixel(sh1106, x + i, y, color);
    }
};

// kreslení svislé linky do pageBufferu
void SH1106_drawVLine(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t w, uint8_t color)
{
    if(x > sh1106->width || y > sh1106->height){
        return;
    }
    if((x + w) > sh1106->height){
        w = sh1106->height - x;
    }
    for(uint8_t i = 0; i < w; i++){
        SH1106_drawPixel(sh1106, x, y+i, color);
    }
};

// kreslení kružnice

void SH1106_drawCircle(sh1106_t *sh1106, int xm, int ym, int r, uint8_t color)
{
   int x = -r, y = 0, err = 2-2*r; /* II. Quadrant */ 
   do {
      SH1106_drawPixel(sh1106, xm-x, ym+y, color); /*   I. Quadrant */
      SH1106_drawPixel(sh1106, xm-y, ym-x, color); /*  II. Quadrant */
      SH1106_drawPixel(sh1106, xm+x, ym-y, color); /* III. Quadrant */
      SH1106_drawPixel(sh1106, 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);
}
  

// vymazání pageBufferu
void SH1106_clear(sh1106_t *sh1106)
{
    for(uint8_t i = 0; i < 8; i++){ //dark screen
        for(uint8_t j = 0; j < 128; j++){
            pageBuffer[i][j] = 0x00;
        }
    }
};

// kreslení obdélníku: x,y jsou souřadnice pravého horního rohu, width je výška a height je šířka obdélníku
void SH1106_drawRectangle(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color) 
{
    for(uint8_t i=0; i<width; ++i)
        for(uint8_t j=0; j<height; ++j)
            SH1106_drawPixel(sh1106, x+i, y+j, color);

};

/*
kreslení jednoho znaku 
function: SH1106_drawChar
parameters:
    p		: ukazatel na strukturu sh1106_t
    x		: souřadnice x
    y		: souřadnice y
    Font	: ukazatel na strukturu bitmapFONT
    Index	: index znaku ve fontu
vrací šířku znaku v pixelech
*/
uint16_t SH1106_drawChar(sh1106_t * p, uint32_t x, uint32_t y, const bitmapFONT* Font, uint32_t Index, uint8_t color) 
{
uint8_t row, column, fcolor;

    if( x > p->width || y > p->height ) {
	return 0;
    }
    
uint8_t  col    = Font->Width/8; // sloupce v bytech
uint8_t  fcol   = col;
uint8_t  zb     = Font->Width%8;

    if( zb != 0) {
        col ++;
    }

uint8_t chwidth = Font->Widths[Index]; // šířka znaku
uint16_t chsize = Font->Height * col;
uint32_t bindex = Index*chsize;

    for (row = 0; row < Font->Height; row++ ) {
        // lezeme po bytech (po osmičkách)
        uint8_t chw = chwidth;
        for (column = 0; column < col; column++, chw-- ) {
            for( int j = 0; j<8; j++ ) {
                if( chw == 0 ) break;
                fcolor = ((uint8_t) Font->Bitmap[bindex+row*col+column]) & (0x80 >> j);
                if( fcolor ) {
                    SH1106_drawPixel(p, x + column*8 + j, y + row, color);
                } else {
                    SH1106_drawPixel(p, x + column*8 + j, y + row, !color);
                }
            }
        } 
    }
return chwidth;
}

/**
 * kreslení UTF-8 řetězce
parameters:
    p                : ukazatel na strukturu sh1106_t
    x                : souřadnice x 
    y                : souřadnice y
    Font             : ukazatel na strukturu bitmapFONT
    pString          : řetězec
returns:
    šířka v pixelech zabíraná řetězcem
*/
uint16_t SH1106_drawString(sh1106_t *p, uint32_t x, uint32_t y, bitmapFONT * Font, const char * pString, uint8_t color )
{
uint32_t Xpoint = x;
uint32_t Ypoint = y;
uint16_t index; 
uint16_t str_width  = 0;
uint16_t char_width = 0;

    if (x > p->width || y > p->height) {
        return 0;
    }

    utf8_string    ustr = make_utf8_string(pString);
    utf8_char_iter iter = make_utf8_char_iter(ustr);
    utf8_char c;
    uint32_t codepoint;
    while ((c = next_utf8_char(&iter)).byte_len > 0 ) {
        codepoint = unicode_code_point(c);
        index = 0;
        for(int i=0; i < Font->Chars; i++) { 
            if(Font->Index[i] == codepoint ) {
                index = i;
                break;
                }
            }
        if((Xpoint + Font->Width ) > p->width ) {
            Xpoint = x;
            Ypoint += Font->Height;
        }
        if ((Ypoint  + Font->Height ) > p->height ) {
            Xpoint = x;
            Ypoint = y;
        }
        char_width = SH1106_drawChar(p, Xpoint, Ypoint, Font, index, color);
        Xpoint    += char_width;
        str_width += char_width;
    }
return str_width;
}
sh1106_spi.h
/* sh1106_spi.h
 * (c) Jirka Chráska 2026, <jirka@lixis.cz>
 */

#ifndef SH1106_SH1106_SPI_H
#define SH1106_SH1106_SPI_H


#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/malloc.h"
#include <malloc.h>
#include "hardware/spi.h"
#include "font.h"


#define SET_DISP        0xAE
#define SET_SCAN_DIR    0xC0
#define SET_SEG_REMAP   0xA0
#define LOW_COL_ADDR    0x00
#define HIGH_COL_ADDR   0x10
#define SET_PAGE_ADDR   0xB0

typedef struct sh1106 {
    uint8_t     width;   // šířka displeje (128)
    uint8_t     height;  // výška displeje (64)
    uint8_t     pages;   // počet stránek
    uint8_t   **buffer;  // displej bufer
    spi_inst_t *spi;     // spi instance (spi0 nebo spi1)
    uint32_t    baud;    // rychlost spi sběrnice
    uint8_t     mosi;    // MOSI pin na Picu
    uint8_t     clk;     // hodinový pin na Picu
    uint8_t     dc_pin;  // DC pin na Picu
    uint8_t     cs_pin;  // CS pin na Picu
    uint8_t     rst_pin; // reset pin
} sh1106_t;

// poslání dat na displej
void SH1106_Write_Data(sh1106_t *sh1106, uint8_t* data);

// poslání příkazu na displej
void SH1106_Write_CMD(sh1106_t *sh1106, uint8_t command);

// inicializace SPI a displeje
// spi je použitá SPI sběrnice (spi0 nebo spi1)
// baud je rychlost přenosu dat ( obvykle 1000000
// mosi je Pico pin SPIx TX
// clk je Pico pin SPIx CSn 
// dc_pin je GPIO pin pro signalizaci přenosu dat
// cs_pin je GPIO pin pro výběr čipu (chip select)
// rts_pin je GPIO pin pro reset displeje
// width je šířka displeje (obvykle 128)
// height je výška displeje (obvykle 64)
uint SH1106_init(sh1106_t *sh1106, spi_inst_t *spi, uint32_t baud, uint8_t mosi, uint8_t clk, uint8_t dc_pin, uint8_t cs_pin, uint8_t rst_pin, uint8_t width, uint8_t height);

// aktualizace displeje - přenos dat z pageBufferu na displej
void SH1106_draw(sh1106_t *sh1106);

// vymazání pageBufferu (pro vymazání displeje je potřeba potom zavolat funkci SH1106_draw)
void SH1106_clear(sh1106_t *sh1106);

// kreslení bodu
// x,y jsou souřadnice bodu
// color je barva bodu (0 - černá, 1 - bílá)
void SH1106_drawPixel(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t color);

// kreslení úsečky z bodu [x0,y0] do bodu [x1,y1]
// barva (color): 0 - černá, 1 - bílá
void SH1106_drawLine(sh1106_t *sh1106, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint8_t color);

// kreslení vodorovné úsečky 
// x,y jsou souřadnice krajního bodu,
// w je délka úsečky
// color je barva úsečky (0 - černá, 1 - bílá )
void SH1106_drawHLine(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t w, uint8_t color);

// kreslení svislé úsečky
// x,y jsou souřadnice krajního bodu,
// w je délka úsečky
// color je barva úsečky (0 - černá, 1 - bílá )
void SH1106_drawVLine(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t w, uint8_t color);// kreslení kružnice

// kreslení kružnice: 
// xm a ym jsou souřadnice středu kružnice, 
// r je poloměr kružnice, 
// color je barva kružnice (0 - černá, 1-bílá)
void SH1106_drawCircle(sh1106_t *sh1106, int xm, int ym, int r, uint8_t color);

// kreslení nevyplněného obdélníku
// x,y jsou souřadnice levého horního bodu obdélníku
// width a height je šířka a výška displeje v bodech (max 0 až 128)
// color je barva obdélníku (0 - černá, 1-bílá)
void SH1106_drawRectangle(sh1106_t *sh1106, uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color);

// kreslení UTF8 řetězce 
// x, y jsou rouřadnice levého horního rohu,
// *Font je použitý bitmapový font
// *pstring je C řetězec s UTF8 znaky
// color je barva (0 - černé písmo na bílém pozadí, 1 - bílé písmo na černém pozací)
// Funkce vrací šířku v bodech displeje, kterou zabírá řetězec
uint16_t SH1106_drawString(sh1106_t *p, uint32_t x, uint32_t y, bitmapFONT * Font, const char * pString, uint8_t color);

// kreslení UTF8 znaku
uint16_t SH1106_drawChar(sh1106_t * p, uint32_t x, uint32_t y, const bitmapFONT* Font, uint32_t Index, uint8_t color);

#endif 
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
}
font.h
/* font.h
 * pro utf8 bitmapové fonty
 */
#ifndef __FONT_H
#define __FONT_H

#include "pico/stdlib.h"

/// bitmap font structure
typedef struct bitmap_font {
	unsigned char 		Width;		///< max. character width
	unsigned char 		Height;		///< character height
	unsigned int        Chars;		///< number of characters in font
	const unsigned char *Widths;	///< width of each character
	const uint32_t      *Index;		///< encoding to character index
	const unsigned char *Bitmap;	///< bitmap of all characters
} bitmapFONT;

#define ________ 0x00
#define _______X 0x01
#define ______X_ 0x02
#define ______XX 0x03
#define _____X__ 0x04
#define _____X_X 0x05
#define _____XX_ 0x06
#define _____XXX 0x07
#define ____X___ 0x08
#define ____X__X 0x09
#define ____X_X_ 0x0A
#define ____X_XX 0x0B
#define ____XX__ 0x0C
#define ____XX_X 0x0D
#define ____XXX_ 0x0E
#define ____XXXX 0x0F
#define ___X____ 0x10
#define ___X___X 0x11
#define ___X__X_ 0x12
#define ___X__XX 0x13
#define ___X_X__ 0x14
#define ___X_X_X 0x15
#define ___X_XX_ 0x16
#define ___X_XXX 0x17
#define ___XX___ 0x18
#define ___XX__X 0x19
#define ___XX_X_ 0x1A
#define ___XX_XX 0x1B
#define ___XXX__ 0x1C
#define ___XXX_X 0x1D
#define ___XXXX_ 0x1E
#define ___XXXXX 0x1F
#define __X_____ 0x20
#define __X____X 0x21
#define __X___X_ 0x22
#define __X___XX 0x23
#define __X__X__ 0x24
#define __X__X_X 0x25
#define __X__XX_ 0x26
#define __X__XXX 0x27
#define __X_X___ 0x28
#define __X_X__X 0x29
#define __X_X_X_ 0x2A
#define __X_X_XX 0x2B
#define __X_XX__ 0x2C
#define __X_XX_X 0x2D
#define __X_XXX_ 0x2E
#define __X_XXXX 0x2F
#define __XX____ 0x30
#define __XX___X 0x31
#define __XX__X_ 0x32
#define __XX__XX 0x33
#define __XX_X__ 0x34
#define __XX_X_X 0x35
#define __XX_XX_ 0x36
#define __XX_XXX 0x37
#define __XXX___ 0x38
#define __XXX__X 0x39
#define __XXX_X_ 0x3A
#define __XXX_XX 0x3B
#define __XXXX__ 0x3C
#define __XXXX_X 0x3D
#define __XXXXX_ 0x3E
#define __XXXXXX 0x3F
#define _X______ 0x40
#define _X_____X 0x41
#define _X____X_ 0x42
#define _X____XX 0x43
#define _X___X__ 0x44
#define _X___X_X 0x45
#define _X___XX_ 0x46
#define _X___XXX 0x47
#define _X__X___ 0x48
#define _X__X__X 0x49
#define _X__X_X_ 0x4A
#define _X__X_XX 0x4B
#define _X__XX__ 0x4C
#define _X__XX_X 0x4D
#define _X__XXX_ 0x4E
#define _X__XXXX 0x4F
#define _X_X____ 0x50
#define _X_X___X 0x51
#define _X_X__X_ 0x52
#define _X_X__XX 0x53
#define _X_X_X__ 0x54
#define _X_X_X_X 0x55
#define _X_X_XX_ 0x56
#define _X_X_XXX 0x57
#define _X_XX___ 0x58
#define _X_XX__X 0x59
#define _X_XX_X_ 0x5A
#define _X_XX_XX 0x5B
#define _X_XXX__ 0x5C
#define _X_XXX_X 0x5D
#define _X_XXXX_ 0x5E
#define _X_XXXXX 0x5F
#define _XX_____ 0x60
#define _XX____X 0x61
#define _XX___X_ 0x62
#define _XX___XX 0x63
#define _XX__X__ 0x64
#define _XX__X_X 0x65
#define _XX__XX_ 0x66
#define _XX__XXX 0x67
#define _XX_X___ 0x68
#define _XX_X__X 0x69
#define _XX_X_X_ 0x6A
#define _XX_X_XX 0x6B
#define _XX_XX__ 0x6C
#define _XX_XX_X 0x6D
#define _XX_XXX_ 0x6E
#define _XX_XXXX 0x6F
#define _XXX____ 0x70
#define _XXX___X 0x71
#define _XXX__X_ 0x72
#define _XXX__XX 0x73
#define _XXX_X__ 0x74
#define _XXX_X_X 0x75
#define _XXX_XX_ 0x76
#define _XXX_XXX 0x77
#define _XXXX___ 0x78
#define _XXXX__X 0x79
#define _XXXX_X_ 0x7A
#define _XXXX_XX 0x7B
#define _XXXXX__ 0x7C
#define _XXXXX_X 0x7D
#define _XXXXXX_ 0x7E
#define _XXXXXXX 0x7F
#define X_______ 0x80
#define X______X 0x81
#define X_____X_ 0x82
#define X_____XX 0x83
#define X____X__ 0x84
#define X____X_X 0x85
#define X____XX_ 0x86
#define X____XXX 0x87
#define X___X___ 0x88
#define X___X__X 0x89
#define X___X_X_ 0x8A
#define X___X_XX 0x8B
#define X___XX__ 0x8C
#define X___XX_X 0x8D
#define X___XXX_ 0x8E
#define X___XXXX 0x8F
#define X__X____ 0x90
#define X__X___X 0x91
#define X__X__X_ 0x92
#define X__X__XX 0x93
#define X__X_X__ 0x94
#define X__X_X_X 0x95
#define X__X_XX_ 0x96
#define X__X_XXX 0x97
#define X__XX___ 0x98
#define X__XX__X 0x99
#define X__XX_X_ 0x9A
#define X__XX_XX 0x9B
#define X__XXX__ 0x9C
#define X__XXX_X 0x9D
#define X__XXXX_ 0x9E
#define X__XXXXX 0x9F
#define X_X_____ 0xA0
#define X_X____X 0xA1
#define X_X___X_ 0xA2
#define X_X___XX 0xA3
#define X_X__X__ 0xA4
#define X_X__X_X 0xA5
#define X_X__XX_ 0xA6
#define X_X__XXX 0xA7
#define X_X_X___ 0xA8
#define X_X_X__X 0xA9
#define X_X_X_X_ 0xAA
#define X_X_X_XX 0xAB
#define X_X_XX__ 0xAC
#define X_X_XX_X 0xAD
#define X_X_XXX_ 0xAE
#define X_X_XXXX 0xAF
#define X_XX____ 0xB0
#define X_XX___X 0xB1
#define X_XX__X_ 0xB2
#define X_XX__XX 0xB3
#define X_XX_X__ 0xB4
#define X_XX_X_X 0xB5
#define X_XX_XX_ 0xB6
#define X_XX_XXX 0xB7
#define X_XXX___ 0xB8
#define X_XXX__X 0xB9
#define X_XXX_X_ 0xBA
#define X_XXX_XX 0xBB
#define X_XXXX__ 0xBC
#define X_XXXX_X 0xBD
#define X_XXXXX_ 0xBE
#define X_XXXXXX 0xBF
#define XX______ 0xC0
#define XX_____X 0xC1
#define XX____X_ 0xC2
#define XX____XX 0xC3
#define XX___X__ 0xC4
#define XX___X_X 0xC5
#define XX___XX_ 0xC6
#define XX___XXX 0xC7
#define XX__X___ 0xC8
#define XX__X__X 0xC9
#define XX__X_X_ 0xCA
#define XX__X_XX 0xCB
#define XX__XX__ 0xCC
#define XX__XX_X 0xCD
#define XX__XXX_ 0xCE
#define XX__XXXX 0xCF
#define XX_X____ 0xD0
#define XX_X___X 0xD1
#define XX_X__X_ 0xD2
#define XX_X__XX 0xD3
#define XX_X_X__ 0xD4
#define XX_X_X_X 0xD5
#define XX_X_XX_ 0xD6
#define XX_X_XXX 0xD7
#define XX_XX___ 0xD8
#define XX_XX__X 0xD9
#define XX_XX_X_ 0xDA
#define XX_XX_XX 0xDB
#define XX_XXX__ 0xDC
#define XX_XXX_X 0xDD
#define XX_XXXX_ 0xDE
#define XX_XXXXX 0xDF
#define XXX_____ 0xE0
#define XXX____X 0xE1
#define XXX___X_ 0xE2
#define XXX___XX 0xE3
#define XXX__X__ 0xE4
#define XXX__X_X 0xE5
#define XXX__XX_ 0xE6
#define XXX__XXX 0xE7
#define XXX_X___ 0xE8
#define XXX_X__X 0xE9
#define XXX_X_X_ 0xEA
#define XXX_X_XX 0xEB
#define XXX_XX__ 0xEC
#define XXX_XX_X 0xED
#define XXX_XXX_ 0xEE
#define XXX_XXXX 0xEF
#define XXXX____ 0xF0
#define XXXX___X 0xF1
#define XXXX__X_ 0xF2
#define XXXX__XX 0xF3
#define XXXX_X__ 0xF4
#define XXXX_X_X 0xF5
#define XXXX_XX_ 0xF6
#define XXXX_XXX 0xF7
#define XXXXX___ 0xF8
#define XXXXX__X 0xF9
#define XXXXX_X_ 0xFA
#define XXXXX_XX 0xFB
#define XXXXXX__ 0xFC
#define XXXXXX_X 0xFD
#define XXXXXXX_ 0xFE
#define XXXXXXXX 0xFF

#endif

Celý projekt ke stažení ../generator_otazek.tar.gz