master
Ondřej Hruška 11 months ago
commit 758503a739
  1. 4
      .gitignore
  2. 12
      CMakeLists.txt
  3. 477
      src/cgram.c
  4. 25
      src/cgram.h
  5. 136
      src/cgrom.c
  6. 24
      src/cgrom.h
  7. 256
      src/lcdbuf.c
  8. 82
      src/lcdbuf.h
  9. 28
      src/main.c
  10. 136
      src/utf8.c
  11. 115
      src/utf8.h

4
.gitignore vendored

@ -0,0 +1,4 @@
.idea/
cmake-build*

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.25)
project(hd44780utf C)
set(CMAKE_C_STANDARD 99)
add_executable(hd44780utf
src/utf8.c
src/main.c
src/cgrom.c
src/cgram.c
src/lcdbuf.h
src/lcdbuf.c)

@ -0,0 +1,477 @@
/**
* TODO file description
*/
#include "cgram.h"
const struct cgram_pattern CGRAM_CZ[] = {
{
.symbol = "ě",
.fallback = 'e',
.data = {
0b01010,
0b00100,
0b01110,
0b10001,
0b11111,
0b10000,
0b01110
},
},
{
.symbol = "š",
.fallback = 's',
.data = {
0b01010,
0b00100,
0b01110,
0b10000,
0b01110,
0b00001,
0b11110
},
},
{
.symbol = "č",
.fallback = 'c',
.data = {
0b01010,
0b00100,
0b01110,
0b10000,
0b10000,
0b10001,
0b01110
},
},
{
.symbol = "ř",
.fallback = 'r',
.data = {
0b01010,
0b00100,
0b10110,
0b11001,
0b10000,
0b10000,
0b10000
},
},
{
.symbol = "ž",
.fallback = 'z',
.data = {
0b01010,
0b00100,
0b11111,
0b00010,
0b00100,
0b01000,
0b11111
},
},
{
.symbol = "ý",
.fallback = 'y',
.data = {
0b00010,
0b00100,
0b10001,
0b10001,
0b01111,
0b00001,
0b01110
},
},
{
.symbol = "á",
.fallback = 'a',
.data = {
0b00010,
0b00100,
0b01110,
0b00001,
0b01111,
0b10001,
0b01111
},
},
{
.symbol = "í",
.fallback = 'i',
.data = {
0b00110,
0b00000,
0b01100,
0b00100,
0b00100,
0b00100,
0b01110
},
},
{
.symbol = "é",
.fallback = 'e',
.data = {
0b00010,
0b00100,
0b01110,
0b10001,
0b11111,
0b10000,
0b01110
},
},
{
.symbol = "ú",
.fallback = 'u',
.data = {
0b00010,
0b00100,
0b10001,
0b10001,
0b10001,
0b10011,
0b01101
},
},
{
.symbol = "ů",
.fallback = 'u',
.data = {
0b00100,
0b01010,
0b10101,
0b10001,
0b10001,
0b10011,
0b01101
},
},
{
.symbol = "ď",
.fallback = 'd',
.data = {
0b01101,
0b00001,
0b01101,
0b10011,
0b10001,
0b10001,
0b01111
},
},
{
.symbol = "ť",
.fallback = 't',
.data = {
0b01010,
0b01001,
0b11100,
0b01000,
0b01000,
0b01001,
0b00110
},
},
{
.symbol = "ň",
.fallback = 'n',
.data = {
0b01010,
0b00100,
0b10110,
0b11001,
0b10001,
0b10001,
0b10001
},
},
// UPPERCASE
{
.symbol = "Ě",
.fallback = 'E',
.data = {
0b01010,
0b00100,
0b11111,
0b10000,
0b11100,
0b10000,
0b11111
},
},
{
.symbol = "Š",
.fallback = 'S',
.data = {
0b01010,
0b00100,
0b01111,
0b10000,
0b01110,
0b00001,
0b11110
},
},
{
.symbol = "Č",
.fallback = 'C',
.data = {
0b01010,
0b00100,
0b01110,
0b10001,
0b10000,
0b10001,
0b01110
},
},
{
.symbol = "Ř",
.fallback = 'R',
.data = {
0b01010,
0b00100,
0b11110,
0b10001,
0b11110,
0b10010,
0b10001
},
},
{
.symbol = "Ž",
.fallback = 'Z',
.data = {
0b01010,
0b00100,
0b11111,
0b00001,
0b01110,
0b10000,
0b11111
},
},
{
.symbol = "Ý",
.fallback = 'Y',
.data = {
0b00010,
0b00100,
0b10001,
0b10001,
0b01010,
0b00100,
0b00100
},
},
{
.symbol = "Á",
.fallback = 'A',
.data = {
0b00010,
0b00100,
0b01110,
0b10001,
0b11111,
0b10001,
0b10001
},
},
{
.symbol = "Í",
.fallback = 'I',
.data = {
0b00010,
0b00100,
0b01110,
0b00100,
0b00100,
0b00100,
0b01110
},
},
{
.symbol = "É",
.fallback = 'E',
.data = {
0b00010,
0b00100,
0b11111,
0b10000,
0b11100,
0b10000,
0b11111
},
},
{
.symbol = "Ú",
.fallback = 'U',
.data = {
0b00010,
0b00100,
0b10001,
0b10001,
0b10001,
0b10001,
0b01110
},
},
{
.symbol = "Ů",
.fallback = 'U',
.data = {
0b00100,
0b01010,
0b10101,
0b10001,
0b10001,
0b10001,
0b01110
},
},
{
.symbol = "Ď",
.fallback = 'D',
.data = {
0b01010,
0b00100,
0b11100,
0b10010,
0b10001,
0b10010,
0b11100
},
},
{
.symbol = "Ť",
.fallback = 'T',
.data = {
0b01010,
0b00100,
0b11111,
0b00100,
0b00100,
0b00100,
0b00100
},
},
{
.symbol = "Ň",
.fallback = 'N',
.data = {
0b01010,
0b00100,
0b10001,
0b11001,
0b10101,
0b10011,
0b10001
},
},
{}, /* end mark */
};
/*
// this should be more or less the default font
{{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000}}, //
{{0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00000, 0b00100}}, // !
{{0b01010, 0b01010, 0b01010, 0b00000, 0b00000, 0b00000, 0b00000}}, // "
{{0b01010, 0b01010, 0b11111, 0b01010, 0b11111, 0b01010, 0b01010}}, // #
{{0b00100, 0b01111, 0b10100, 0b01110, 0b00101, 0b11110, 0b00100}}, // $
{{0b11000, 0b11001, 0b00010, 0b00100, 0b01000, 0b10011, 0b00011}}, // %
{{0b01100, 0b10010, 0b10100, 0b01000, 0b10101, 0b10010, 0b01101}}, // &
{{0b01100, 0b00100, 0b01000, 0b00000, 0b00000, 0b00000, 0b00000}}, // '
{{0b00010, 0b00100, 0b01000, 0b01000, 0b01000, 0b00100, 0b00010}}, // (
{{0b01000, 0b00100, 0b00010, 0b00010, 0b00010, 0b00100, 0b01000}}, // )
{{0b00000, 0b00100, 0b10101, 0b01110, 0b10101, 0b00100, 0b00000}}, // *
{{0b00000, 0b00100, 0b00100, 0b11111, 0b00100, 0b00100, 0b00000}}, // +
{{0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b00100, 0b01000}}, // ,
{{0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000}}, // -
{{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b01100}}, // .
{{0b00000, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b00000}}, // /
{{0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110}}, // 0
{{0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110}}, // 1
{{0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111}}, // 2
{{0b11111, 0b00010, 0b00100, 0b00010, 0b00001, 0b10001, 0b01110}}, // 3
{{0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010}}, // 4
{{0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110}}, // 5
{{0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110}}, // 6
{{0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000}}, // 7
{{0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110}}, // 8
{{0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100}}, // 9
{{0b00000, 0b01100, 0b01100, 0b00000, 0b01100, 0b01100, 0b00000}}, // :
{{0b00000, 0b01100, 0b01100, 0b00000, 0b01100, 0b00100, 0b01000}}, // ;
{{0b00010, 0b00100, 0b01000, 0b10000, 0b01000, 0b00100, 0b00010}}, // <
{{0b00000, 0b00000, 0b11111, 0b00000, 0b11111, 0b00000, 0b00000}}, // =
{{0b01000, 0b00100, 0b00010, 0b00001, 0b00010, 0b00100, 0b01000}}, // >
{{0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b00000, 0b00100}}, // ?
{{0b01110, 0b10001, 0b00001, 0b01101, 0b10101, 0b10101, 0b01110}}, // @
{{0b01110, 0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001}}, // A
{{0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110}}, // B
{{0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110}}, // C
{{0b11100, 0b10010, 0b10001, 0b10001, 0b10001, 0b10010, 0b11100}}, // D
{{0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111}}, // E
{{0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000}}, // F
{{0b01110, 0b10001, 0b10000, 0b10111, 0b10001, 0b10001, 0b01111}}, // G
{{0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}}, // H
{{0b01110, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110}}, // I
{{0b00111, 0b00010, 0b00010, 0b00010, 0b00010, 0b10010, 0b01100}}, // J
{{0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001}}, // K
{{0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}}, // L
{{0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001}}, // M
{{0b10001, 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001}}, // N
{{0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}}, // O
{{0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000}}, // P
{{0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101}}, // Q
{{0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001}}, // R
{{0b01111, 0b10000, 0b10000, 0b01110, 0b00001, 0b00001, 0b11110}}, // S
{{0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100}}, // T
{{0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}}, // U
{{0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100}}, // V
{{0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b10101, 0b01010}}, // W
{{0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001}}, // X
{{0b10001, 0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100}}, // Y
{{0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111}}, // Z
{{0b01110, 0b01000, 0b01000, 0b01000, 0b01000, 0b01000, 0b01110}}, // [
{{0b00000, 0b10000, 0b01000, 0b00100, 0b00010, 0b00001, 0b00000}}, // \
{{0b01110, 0b00010, 0b00010, 0b00010, 0b00010, 0b00010, 0b01110}}, // ]
{{0b00100, 0b01010, 0b10001, 0b00000, 0b00000, 0b00000, 0b00000}}, // ^
{{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}}, // _
{{0b01000, 0b00100, 0b00010, 0b00000, 0b00000, 0b00000, 0b00000}}, // `
{{0b00000, 0b00000, 0b01110, 0b00001, 0b01111, 0b10001, 0b01111}}, // a
{{0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b11110}}, // b
{{0b00000, 0b00000, 0b01110, 0b10000, 0b10000, 0b10001, 0b01110}}, // c
{{0b00001, 0b00001, 0b01101, 0b10011, 0b10001, 0b10001, 0b01111}}, // d
{{0b00000, 0b00000, 0b01110, 0b10001, 0b11111, 0b10000, 0b01110}}, // e
{{0b00110, 0b01001, 0b01000, 0b11100, 0b01000, 0b01000, 0b01000}}, // f
{{0b00000, 0b01111, 0b10001, 0b10001, 0b01111, 0b00001, 0b01110}}, // g
{{0b10000, 0b10000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001}}, // h
{{0b00100, 0b00000, 0b01100, 0b00100, 0b00100, 0b00100, 0b01110}}, // i
{{0b00010, 0b00000, 0b00110, 0b00010, 0b00010, 0b10010, 0b01100}}, // j
{{0b10000, 0b10000, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010}}, // k
{{0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110}}, // l
{{0b00000, 0b00000, 0b11010, 0b10101, 0b10101, 0b10001, 0b10001}}, // m
{{0b00000, 0b00000, 0b10110, 0b11001, 0b10001, 0b10001, 0b10001}}, // n
{{0b00000, 0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110}}, // o
{{0b00000, 0b00000, 0b11110, 0b10001, 0b11110, 0b10000, 0b10000}}, // p
{{0b00000, 0b00000, 0b01101, 0b10011, 0b01111, 0b00001, 0b00001}}, // q
{{0b00000, 0b00000, 0b10110, 0b11001, 0b10000, 0b10000, 0b10000}}, // r
{{0b00000, 0b00000, 0b01110, 0b10000, 0b01110, 0b00001, 0b11110}}, // s
{{0b01000, 0b01000, 0b11100, 0b01000, 0b01000, 0b01001, 0b00110}}, // t
{{0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b10011, 0b01101}}, // u
{{0b00000, 0b00000, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100}}, // v
{{0b00000, 0b00000, 0b10001, 0b10001, 0b10101, 0b10101, 0b01010}}, // w
{{0b00000, 0b00000, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001}}, // x
{{0b00000, 0b00000, 0b10001, 0b10001, 0b01111, 0b00001, 0b01110}}, // y
{{0b00000, 0b00000, 0b11111, 0b00010, 0b00100, 0b01000, 0b11111}}, // z
{{0b00010, 0b00100, 0b00100, 0b01000, 0b00100, 0b00100, 0b00010}}, // {
{{0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100}}, // |
{{0b01000, 0b00100, 0b00100, 0b00010, 0b00100, 0b00100, 0b01000}}, // }
{{0b00000, 0b00000, 0b00000, 0b01101, 0b10010, 0b00000, 0b00000}}, // ~
*/

@ -0,0 +1,25 @@
/**
* TODO file description
*/
#ifndef HD44780UTF_CGRAM_H
#define HD44780UTF_CGRAM_H
#include "utf8.h"
#include <stdint.h>
/** Pattern for CGRAM */
struct cgram_pattern {
/** The symbol displayed */
struct Utf8Char symbol;
/** ASCII char shown if there is no space for the CGRAM pattern */
char fallback;
/** Graphic data (rows) - the 8th row is typically blank and can be left out from the definition */
uint8_t data[8];
};
extern const struct cgram_pattern CGRAM_CZ[];
#endif //HD44780UTF_CGRAM_H

@ -0,0 +1,136 @@
#include <stdint.h>
#include "utf8.h"
#include "cgrom.h"
const struct cgrom_entry CGROM_A00[] = {
{.address = 32, .symbol = " "},
{.address = 33, .symbol = "!"},
{.address = 34, .symbol = "\""},
{.address = 35, .symbol = "#"},
{.address = 36, .symbol = "$"},
{.address = 37, .symbol = "%"},
{.address = 38, .symbol = "&"},
{.address = 39, .symbol = "'"},
{.address = 40, .symbol = "("},
{.address = 41, .symbol = ")"},
{.address = 42, .symbol = "*"},
{.address = 43, .symbol = "+"},
{.address = 44, .symbol = ","},
{.address = 45, .symbol = "-"},
{.address = 46, .symbol = "."},
{.address = 47, .symbol = "/"},
{.address = 48, .symbol = "0"},
{.address = 49, .symbol = "1"},
{.address = 50, .symbol = "2"},
{.address = 51, .symbol = "3"},
{.address = 52, .symbol = "4"},
{.address = 53, .symbol = "5"},
{.address = 54, .symbol = "6"},
{.address = 55, .symbol = "7"},
{.address = 56, .symbol = "8"},
{.address = 57, .symbol = "9"},
{.address = 58, .symbol = ":"},
{.address = 59, .symbol = ";"},
{.address = 60, .symbol = "<"},
{.address = 61, .symbol = "="},
{.address = 62, .symbol = ">"},
{.address = 63, .symbol = "?"},
{.address = 64, .symbol = "@"},
{.address = 65, .symbol = "A"},
{.address = 66, .symbol = "B"},
{.address = 67, .symbol = "C"},
{.address = 68, .symbol = "D"},
{.address = 69, .symbol = "E"},
{.address = 70, .symbol = "F"},
{.address = 71, .symbol = "G"},
{.address = 72, .symbol = "H"},
{.address = 73, .symbol = "I"},
{.address = 74, .symbol = "J"},
{.address = 75, .symbol = "K"},
{.address = 76, .symbol = "L"},
{.address = 77, .symbol = "M"},
{.address = 78, .symbol = "N"},
{.address = 79, .symbol = "O"},
{.address = 80, .symbol = "P"},
{.address = 81, .symbol = "Q"},
{.address = 82, .symbol = "R"},
{.address = 83, .symbol = "S"},
{.address = 84, .symbol = "T"},
{.address = 85, .symbol = "U"},
{.address = 86, .symbol = "V"},
{.address = 87, .symbol = "W"},
{.address = 88, .symbol = "X"},
{.address = 89, .symbol = "Y"},
{.address = 90, .symbol = "Z"},
{.address = 91, .symbol = "["},
{.address = 92, .symbol = "¥"}, // yen
{.address = 93, .symbol = "]"},
{.address = 94, .symbol = "^"},
{.address = 95, .symbol = "_"},
{.address = 96, .symbol = "`"},
{.address = 97, .symbol = "a"},
{.address = 98, .symbol = "b"},
{.address = 99, .symbol = "c"},
{.address = 100, .symbol = "d"},
{.address = 101, .symbol = "e"},
{.address = 102, .symbol = "f"},
{.address = 103, .symbol = "g"},
{.address = 104, .symbol = "h"},
{.address = 105, .symbol = "i"},
{.address = 106, .symbol = "j"},
{.address = 107, .symbol = "k"},
{.address = 108, .symbol = "l"},
{.address = 109, .symbol = "m"},
{.address = 110, .symbol = "n"},
{.address = 111, .symbol = "o"},
{.address = 112, .symbol = "p"},
{.address = 113, .symbol = "q"},
{.address = 114, .symbol = "r"},
{.address = 115, .symbol = "s"},
{.address = 116, .symbol = "t"},
{.address = 117, .symbol = "u"},
{.address = 118, .symbol = "v"},
{.address = 119, .symbol = "w"},
{.address = 120, .symbol = "x"},
{.address = 121, .symbol = "y"},
{.address = 122, .symbol = "z"},
{.address = 123, .symbol = "{"},
{.address = 124, .symbol = "|"},
{.address = 125, .symbol = "}"},
{.address = 126, .symbol = ""},
{.address = 127, .symbol = ""},
// lots of japanese symbols - add them yourself if you need them
{.address = 0xA2, .symbol = ""},
{.address = 0xA3, .symbol = ""},
{.address = 0xA5, .symbol = "·"},
{.address = 0xDF, .symbol = "°"},
// there is also some greek and obscure diacritics
{.address = 0xE0, .symbol = "α"},
{.address = 0xE1, .symbol = "ä"},
{.address = 0xE2, .symbol = "β"},
{.address = 0xE3, .symbol = "ϵ"},
{.address = 0xE4, .symbol = "μ"},
{.address = 0xE5, .symbol = "σ"},
{.address = 0xE6, .symbol = "ρ"},
{.address = 0xE8, .symbol = ""},
// E9 is nice superscript minus one, but it's not in Unicode :(
{.address = 0xEC, .symbol = "¢"},
{.address = 0xED, .symbol = "£"},
{.address = 0xEE, .symbol = "ñ"},
{.address = 0xEF, .symbol = "ö"},
{.address = 0xF2, .symbol = "Θ"},
{.address = 0xF3, .symbol = ""},
{.address = 0xF4, .symbol = "Ω"},
{.address = 0xF5, .symbol = "Ü"},
{.address = 0xF6, .symbol = "Σ"},
{.address = 0xF7, .symbol = "π"},
{.address = 0xFC, .symbol = ""},
{.address = 0xFD, .symbol = "÷"},
{.address = 0xFF, .symbol = ""},
{}, /* end mark */
};

@ -0,0 +1,24 @@
/**
* A definition of the actual CGROM
*/
#ifndef HD44780UTF_CGROM_H
#define HD44780UTF_CGROM_H
#include <stdint.h>
#include "utf8.h"
/** CGROM look-up table entry */
struct cgrom_entry {
/** Address in the CGROM */
uint8_t address;
/** Corresponding symbol */
struct Utf8Char symbol;
};
/** The standard japanese lookup table, terminated by an empty entry */
extern const struct cgrom_entry CGROM_A00[];
#endif //HD44780UTF_CGROM_H

@ -0,0 +1,256 @@
/**
* TODO file description
*/
#include <string.h>
#include <assert.h>
#include "lcdbuf.h"
/** Initialize the struct */
void LcdBuffer_Init(struct LcdBuffer *self, const struct cgrom_entry *cgrom, const struct cgram_pattern *custom_symbols)
{
assert(self);
assert(cgrom);
assert(custom_symbols);
LcdBuffer_Clear(self);
self->cgrom = cgrom;
self->custom_symbols = custom_symbols;
}
/** Clear the screen */
void LcdBuffer_Clear(struct LcdBuffer *self)
{
assert(self);
memset(self->cgram, 0, sizeof(self->cgram));
memset(self->screen, 32, sizeof(self->screen));
memset(self->dirty_extents, 0, sizeof(self->dirty_extents));
self->full_repaint_required = false;
}
/** Write what needs to be written to the HW, clear all dirty marks */
void LcdBuffer_Flush(struct LcdBuffer *self)
{
for (int i = 0; i < 8; i++) {
if (self->cgram[i].refcount > 0 && self->cgram[i].dirty) {
LcdBuffer_IO_WriteCGRAM(i, self->custom_symbols[self->cgram[i].symbol_index].data);
self->cgram[i].dirty = false;
}
}
if (self->full_repaint_required) {
for (int r = 0; r < LINE_NUM; r++) {
LcdBuffer_IO_WriteAt(r, 0, self->screen[r], LINE_LEN);
}
memset(self->dirty_extents, 0, sizeof(self->dirty_extents));
} else {
for (int e = 0; e < BUFLEN_DIRTY_LIST; e++) {
struct DirtyExtent *ext = &self->dirty_extents[e];
if (!ext->count) {
continue;
}
LcdBuffer_IO_WriteAt(ext->row, ext->col, &self->screen[ext->row][ext->col], ext->count);
ext->count = 0; // mark the slot as free
}
}
}
/** Fully write everything to the display */
void LcdBuffer_FlushAll(struct LcdBuffer *self)
{
for (int i = 0; i < 8; i++) {
if (self->cgram[i].refcount > 0) {
LcdBuffer_IO_WriteCGRAM(i, self->custom_symbols[self->cgram[i].symbol_index].data);
}
self->cgram[i].dirty = false;
}
for (int r = 0; r < LINE_NUM; r++) {
LcdBuffer_IO_WriteAt(r, 0, self->screen[r], LINE_LEN);
}
memset(self->dirty_extents, 0, sizeof(self->dirty_extents));
self->full_repaint_required = false;
}
static void mark_dirty(struct LcdBuffer *self, uint8_t row, uint8_t col)
{
int first_empty_extent_slot = -1;
for (int i = 0; i < BUFLEN_DIRTY_LIST; i++) {
struct DirtyExtent *ext = &self->dirty_extents[i];
if (ext->count == 0) {
// unused
if (first_empty_extent_slot == -1) {
first_empty_extent_slot = i;
}
continue;
}
if (ext->row != row) {
// not this row
continue;
}
// this is a filled extent
if (ext->col < col && ext->col + ext->count > col) {
// already in this extent
return;
}
if (col < ext->col && (ext->col - col) <= 5) {
ext->count += (ext->col - col);
ext->col = col;
return;
}
if (col >= ext->col + ext->count && (col - ext->col + ext->count) <= 5) {
ext->count += (col - ext->col + ext->count);
return;
}
}
if (first_empty_extent_slot == -1) {
self->full_repaint_required = true;
} else {
self->dirty_extents[first_empty_extent_slot].col = col;
self->dirty_extents[first_empty_extent_slot].row = row;
self->dirty_extents[first_empty_extent_slot].count = 1;
}
}
/** Set one utf8 character at a position */
void LcdBuffer_Set(struct LcdBuffer *self, uint8_t row, uint8_t col, struct Utf8Char ch)
{
assert(self);
assert(row < LINE_NUM);
assert(col < LINE_LEN);
uint8_t oldchar = self->screen[row][col];
if (oldchar >= 8 && oldchar == ch.uint) {
// No change
return;
}
// Fast path for standard ASCII
if (ch.uint >= 32 && ch.uint < 126 && ch.uint != '\\') { // A00 has YEN in place of BACKSLASH
// normal ASCII
if (oldchar < 8) {
// release refcount on the CGRAM cell
self->cgram[oldchar].refcount -= 1;
}
self->screen[row][col] = ch.uint;
goto done_dirty;
}
// Find if it's in CGROM
const struct cgrom_entry *rom = self->cgrom;
for (;;) {
if (rom->symbol.uint == 0) {
// End of the lookup table
break;
}
if (rom->symbol.uint == ch.uint) {
// found it!
if (oldchar < 8) {
// release refcount on the CGRAM cell
self->cgram[oldchar].refcount -= 1;
}
self->screen[row][col] = rom->address;
goto done_dirty;
}
rom++;
}
// Check if the same custom char is already used - if so, increment refcount and reuse it
int first_empty_custom_slot = -1;
for (int i = 0; i < 8; i++) {
if (self->cgram[i].refcount > 0) {
if (self->cgram[i].uint == ch.uint) {
if (oldchar == i) {
// No change, was already the same custom
return;
}
if (oldchar < 8) {
// release refcount on the CGRAM cell
self->cgram[oldchar].refcount -= 1;
}
self->cgram[i].refcount += 1;
self->screen[row][col] = i;
goto done_dirty;
}
} else if (first_empty_custom_slot == -1) {
first_empty_custom_slot = i;
}
}
// New custom pattern is needed
if (oldchar < 8) {
// release refcount on the CGRAM cell
self->cgram[oldchar].refcount -= 1;
}
uint32_t index = 0;
const struct cgram_pattern *pattern = self->custom_symbols;
for (;;) {
if (pattern->symbol.uint == 0) {
// End of the lookup table
break;
}
if (pattern->symbol.uint == ch.uint) {
// found it!
if (first_empty_custom_slot == -1) {
// Whoops, out of slots. Show a fallback glyph
if (oldchar != pattern->fallback) {
self->screen[row][col] = pattern->fallback;
goto done_dirty;
}
return;
}
// Allocate a new slot in the CGRAM
self->cgram[first_empty_custom_slot].refcount = 1;
self->cgram[first_empty_custom_slot].uint = ch.uint;
self->cgram[first_empty_custom_slot].dirty = true; // it should be flushed!
self->cgram[first_empty_custom_slot].symbol_index = index;
self->screen[row][col] = first_empty_custom_slot;
goto done_dirty;
}
index++;
pattern++;
}
// Fallback, no way to show this glyph
self->screen[row][col] = '?';
done_dirty:
mark_dirty(self, row, col);
}
/** Write a UTF8 string at a position */
void LcdBuffer_Write(struct LcdBuffer *self, uint8_t row, uint8_t col, char *utf_string)
{
struct Utf8Iterator iter;
Utf8Iterator_Init(&iter, utf_string);
struct Utf8Char uchar;
while ((uchar = Utf8Iterator_Next(&iter)).uint) {
if (col >= LINE_LEN) {
break;
}
LcdBuffer_Set(self, row, col, uchar);
col++;
}
}

@ -0,0 +1,82 @@
/**
* TODO file description
*/
#ifndef HD44780UTF_LCDBUF_H
#define HD44780UTF_LCDBUF_H
#include <stdint.h>
#include "utf8.h"
#include "cgram.h"
#include "cgrom.h"
#define LINE_NUM 4
#define LINE_LEN 20
#define BUFLEN_DIRTY_LIST 8
_Static_assert(LINE_NUM * LINE_LEN < 256, "LINE_NUM * LINE_LEN must fit in u8");
/** Indicates a range of screen cells that were changed and must be written to HW */
struct DirtyExtent {
uint8_t row;
uint8_t col;
uint8_t count;
};
/** Struct for one CGRAM slot */
struct CgramState {
/** UTF8 uint shown in this slot */
uint32_t uint;
/** Array index in the custom symbols table, use for look-up when writing the font data to HW */
uint32_t symbol_index;
/** Number of occurrences of this symbol in the screen array */
uint8_t refcount;
/** This CGRAM slot needs to be written to HW */
bool dirty;
};
struct LcdBuffer {
/** The raw screen buffer. Custom symbols are 0x00-0x07 */
uint8_t screen[LINE_NUM][LINE_LEN];
/** CGRAM state array */
struct CgramState cgram[8];
/** Hardware CGROM lookup table, used to map UTF8 to existing ROM symbols */
const struct cgrom_entry *cgrom;
/** Defined custom display pattern of utf8 symbols */
const struct cgram_pattern *custom_symbols;
/** Array of dirty extents - ranges in the display that need to be flushed to HW */
struct DirtyExtent dirty_extents[BUFLEN_DIRTY_LIST];
/** If the dirty extents array was not sufficient to hold all changes, this flag is set,
* indicating the dirty_extents array should be disregarded. */
bool full_repaint_required;
};
/** Initialize the struct */
void LcdBuffer_Init(struct LcdBuffer *self, const struct cgrom_entry *cgrom, const struct cgram_pattern *custom_symbols);
/** Clear the screen */
void LcdBuffer_Clear(struct LcdBuffer *self);
/** Write what needs to be written to the HW, clear all dirty marks */
void LcdBuffer_Flush(struct LcdBuffer *self);
/** Fully write everything to the display */
void LcdBuffer_FlushAll(struct LcdBuffer *self);
/** Set one utf8 character at a position */
void LcdBuffer_Set(struct LcdBuffer *self, uint8_t row, uint8_t col, struct Utf8Char ch);
/** Write a UTF8 string at a position */
void LcdBuffer_Write(struct LcdBuffer *self, uint8_t row, uint8_t col, char *utf_string);
/* Callbacks - need to be implemented by the application! */
/** Write character data at position */
void LcdBuffer_IO_WriteAt(uint8_t row, uint8_t col, const uint8_t *buf, uint8_t len);
/** Write CGRAM data. Data is always 8 bytes long. */
void LcdBuffer_IO_WriteCGRAM(uint8_t position, const uint8_t* data);
#endif //HD44780UTF_LCDBUF_H

@ -0,0 +1,28 @@
#include <stdio.h>
#include <stdint.h>
#include "lcdbuf.h"
int main()
{
struct LcdBuffer buf;
LcdBuffer_Init(&buf, CGROM_A00, CGRAM_CZ);
LcdBuffer_Write(&buf, 0, 0, "Ahoj");
LcdBuffer_Flush(&buf);
return 0;
}
/** Write character data at position */
void LcdBuffer_IO_WriteAt(uint8_t row, uint8_t col, const uint8_t *buf, uint8_t len)
{
printf("W@%d,%d: \"%.*s\" (len %d)\n", row, col, len, buf, len);
}
/** Write CGRAM data. Data is always 8 bytes long. */
void LcdBuffer_IO_WriteCGRAM(uint8_t position, const uint8_t *data)
{
printf("G@%d: %02x %02x %02x %02x %02x %02x %02x %02x\n",
position, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
}

@ -0,0 +1,136 @@
#include <stdint.h>
#include "utf8.h"
//
// Created by MightyPork on 2017/08/20.
//
// UTF-8 parser - collects bytes of a code point before writing them
// into a screen cell.
//
const struct Utf8Char EMPTY_CHAR = (struct Utf8Char) {.uint = 0};
// Code Points First Byte Second Byte Third Byte Fourth Byte
// U+0000 - U+007F 00 - 7F
// U+0080 - U+07FF C2 - DF 80 - BF
// U+0800 - U+0FFF E0 *A0 - BF 80 - BF
// U+1000 - U+CFFF E1 - EC 80 - BF 80 - BF
// U+D000 - U+D7FF ED 80 - *9F 80 - BF
// U+E000 - U+FFFF EE - EF 80 - BF 80 - BF
// U+10000 - U+3FFFF F0 *90 - BF 80 - BF 80 - BF
// U+40000 - U+FFFFF F1 - F3 80 - BF 80 - BF 80 - BF
// U+100000 - U+10FFFF F4 80 - *8F 80 - BF 80 - BF
void Utf8Parser_Clear(struct Utf8Parser *self)
{
self->buffer.uint = 0;
self->utf_j = 0;
self->utf_len = 0;
}
/**
* Handle a received character
*/
struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c)
{
uint8_t *bytes = self->buffer.bytes;
uint8_t uc = (uint8_t) c;
// collecting unicode glyphs...
if (uc & 0x80) {
if (self->utf_len == 0) {
bytes[0] = uc;
self->utf_j = 1;
// start
if (uc == 0xC0 || uc == 0xC1 || uc > 0xF4) {
// forbidden start codes
goto fail;
}
if ((uc & 0xE0) == 0xC0) {
self->utf_len = 2;
} else if ((uc & 0xF0) == 0xE0) {
self->utf_len = 3;
} else if ((uc & 0xF8) == 0xF0) {
self->utf_len = 4;
} else {
// chars over 127 that don't start unicode sequences
goto fail;
}
} else {
if ((uc & 0xC0) != 0x80) {
bytes[self->utf_j++] = uc;
goto fail;
} else {
bytes[self->utf_j++] = uc;
if (self->utf_j >= self->utf_len) {
// check for bad sequences - overlong or some other problem
if (bytes[0] == 0xF4 && bytes[1] > 0x8F) { goto fail; }
if (bytes[0] == 0xF0 && bytes[1] < 0x90) { goto fail; }
if (bytes[0] == 0xED && bytes[1] > 0x9F) { goto fail; }
if (bytes[0] == 0xE0 && bytes[1] < 0xA0) { goto fail; }
// trap for surrogates - those break javascript
if (bytes[0] == 0xED && bytes[1] >= 0xA0 && bytes[1] <= 0xBF) { goto fail; }
goto success;
}
}
}
} else {
bytes[0] = uc;
goto success;
}
return EMPTY_CHAR;
success:;
struct Utf8Char result = self->buffer;
self->buffer.uint = 0; // erase the buffer
self->utf_len = 0;
return result;
fail:
self->buffer.uint = 0; // erase the buffer
self->utf_len = 0;
return EMPTY_CHAR;
}
void Utf8Iterator_Init(struct Utf8Iterator *self, const char *source)
{
Utf8Parser_Clear(&self->parser);
self->source = source;
}
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self)
{
char c;
struct Utf8Char uchar;
while (1) {
c = *self->source++;
if (!c) { break; }
uchar = Utf8Parser_Handle(&self->parser, c);
if (uchar.uint) {
return uchar;
}
}
return EMPTY_CHAR;
}
size_t utf8_strlen(const char *text)
{
// TODO optimize
struct Utf8Iterator iter;
Utf8Iterator_Init(&iter, text);
size_t num = 0;
while ((Utf8Iterator_Next(&iter)).uint) {
num++;
}
return num;
}

@ -0,0 +1,115 @@
/**
* UTF-8 string parsing and character iteration
*
* Created on 2020/01/04.
*/
#ifndef LIQUIDTYPE_UTF8_H
#define LIQUIDTYPE_UTF8_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
/** Character containing all zeros */
extern const struct Utf8Char EMPTY_CHAR;
/**
* UTF-8 encoded character.
*
* It's convenient to use the uint values internally to represent this symbol.
* Since UTF8 can't contain zero bytes, just strip the trailing zeros when composing a string.
*/
struct Utf8Char {
union {
/**
* character bytes; padded by zero bytes if shorter than 4.
*
* Can be initialized by a string literal in lookup tables
*/
uint8_t bytes[4];
/** u32 view of the bytes */
uint32_t uint;
};
};
/** UTF8 string parser internal state */
struct Utf8Parser {
/** UTF-8 bytes buffer */
struct Utf8Char buffer;
/** Currently collected UTF-8 character length */
uint8_t utf_len;
/** Position in the current character */
uint8_t utf_j;
};
/**
* Clear the parser internal state
*
* @param self
*/
void Utf8Parser_Clear(struct Utf8Parser *self);
/**
* Initialize the parser struct before starting to parse
*/
static void Utf8Parser_Init(struct Utf8Parser *self) {
Utf8Parser_Clear(self);
}
/**
* Parse a character.
*
* The returned struct contains NIL (uint == 0) if no character is yet available.
*
* ASCII is passed through, utf-8 is collected and returned in one piece.
*/
struct Utf8Char Utf8Parser_Handle(struct Utf8Parser *self, char c);
/**
* Utf8 character iterator.
*
* Usage:
* struct Utf8Iterator iter;
* Utf8Iterator_Init(&iter, myString);
*
* struct Utf8Char uchar;
* while ((uchar = Utf8Iterator_Next(&iter)).uint) {
* // do something with the char
* }
*/
struct Utf8Iterator {
/* Characters to parse. The pointer is advanced as the iterator progresses. */
const char *source;
struct Utf8Parser parser;
};
/**
* Initialize the iterator struct
*
* @param self
* @param source - string to iterate; It can be in RO memory, it's only read.
*/
void Utf8Iterator_Init(struct Utf8Iterator *self, const char *source);
/**
* Get the next character from the iterator;
*
* Returns empty character if there are no more characters to parse (the .uint field is zero)
*
* Invalid characters are skipped.
*/
struct Utf8Char Utf8Iterator_Next(struct Utf8Iterator *self);
/**
* Get utf8 string length, counting codepoints.
*
* @attention This function is rather expensive, cache the result if reused
*
* @param text - utf8 string, zero terminated
* @return number of codepoints
*/
size_t utf8_strlen(const char *text);
#endif //LIQUIDTYPE_UTF8_H
Loading…
Cancel
Save