Преглед изворни кода

Add 'chess/' from commit '322f7f22a3f9098338f9ea2a8644f7cd36fe61ff'

git-subtree-dir: chess
git-subtree-mainline: cd6dc6a5a3ec2a7bc5814286027b70b41975460a
git-subtree-split: 322f7f22a3f9098338f9ea2a8644f7cd36fe61ff
Willy-JL пре 2 година
родитељ
комит
c5a1497221
43 измењених фајлова са 11382 додато и 0 уклоњено
  1. 32 0
      chess/.github/workflows/build.yml
  2. 44 0
      chess/.github/workflows/release.yml
  3. 35 0
      chess/.github/workflows/ufbt_build.yaml
  4. 52 0
      chess/.gitignore
  5. 21 0
      chess/LICENSE
  6. 34 0
      chess/README.md
  7. 19 0
      chess/application.fam
  8. BIN
      chess/catalog/browser.png
  9. BIN
      chess/catalog/game1.png
  10. BIN
      chess/catalog/game2.png
  11. BIN
      chess/catalog/game3.png
  12. BIN
      chess/catalog/game4.png
  13. BIN
      chess/catalog/game5.png
  14. 16 0
      chess/catalog/manifest.yml
  15. BIN
      chess/catalog/menu.png
  16. BIN
      chess/catalog/settings.png
  17. BIN
      chess/catalog/startscreen.png
  18. 3492 0
      chess/chess/smallchesslib.h
  19. 179 0
      chess/flipchess.c
  20. 78 0
      chess/flipchess.h
  21. BIN
      chess/flipchess_10px.png
  22. 16 0
      chess/helpers/flipchess_custom_event.h
  23. 153 0
      chess/helpers/flipchess_file.c
  24. 15 0
      chess/helpers/flipchess_file.h
  25. 35 0
      chess/helpers/flipchess_haptic.c
  26. 7 0
      chess/helpers/flipchess_haptic.h
  27. 37 0
      chess/helpers/flipchess_voice.cpp
  28. 12 0
      chess/helpers/flipchess_voice.h
  29. BIN
      chess/icons/Background_128x11.png
  30. BIN
      chess/icons/FLIPR_128x64.png
  31. 5703 0
      chess/sam/stm32_sam.cpp
  32. 96 0
      chess/sam/stm32_sam.h
  33. 30 0
      chess/scenes/flipchess_scene.c
  34. 29 0
      chess/scenes/flipchess_scene.h
  35. 4 0
      chess/scenes/flipchess_scene_config.h
  36. 91 0
      chess/scenes/flipchess_scene_menu.c
  37. 55 0
      chess/scenes/flipchess_scene_scene_1.c
  38. 102 0
      chess/scenes/flipchess_scene_settings.c
  39. 67 0
      chess/scenes/flipchess_scene_startscreen.c
  40. 727 0
      chess/views/flipchess_scene_1.c
  41. 19 0
      chess/views/flipchess_scene_1.h
  42. 163 0
      chess/views/flipchess_startscreen.c
  43. 19 0
      chess/views/flipchess_startscreen.h

+ 32 - 0
chess/.github/workflows/build.yml

@@ -0,0 +1,32 @@
+name: Build
+
+on:
+  push:
+    branches:
+      - main
+      - develop
+
+env:
+  firmware_version: '0.86.1'
+
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout Flipper Zero Firmware
+        uses: actions/checkout@v3
+        with:
+          repository: 'flipperdevices/flipperzero-firmware'
+          ref: ${{ env.firmware_version }}
+          submodules: true
+      - name: Checkout flipper-chess
+        uses: actions/checkout@v3
+        with:
+          path: 'applications_user/flipper-chess'
+      - name: Build FAPs
+        run: ./fbt COMPACT=1 DEBUG=0 faps
+      - name: Check flipper-chess Built
+        run: test -f build/f7-firmware-C/.extapps/chess.fap
+      - name: Check flipper-chess Size
+        run: ls -l --block-size=K build/f7-firmware-C/.extapps/chess.fap

+ 44 - 0
chess/.github/workflows/release.yml

@@ -0,0 +1,44 @@
+name: Release
+
+on:
+  push:
+    tags:
+      - 'v[0-9]+.[0-9]+.[0-9]+'
+
+env:
+  firmware_version: '0.86.1'
+
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout Flipper Zero Firmware
+        uses: actions/checkout@v3
+        with:
+          repository: 'flipperdevices/flipperzero-firmware'
+          ref: ${{ env.firmware_version }}
+          submodules: true
+      - name: Checkout flipper-chess
+        uses: actions/checkout@v3
+        with:
+          path: 'applications_user/flipper-chess'
+      - name: Build FAPs
+        run: ./fbt COMPACT=1 DEBUG=0 faps
+      - name: Check flipper-chess Built
+        run: test -f build/f7-firmware-C/.extapps/chess.fap
+      - name: Get Tag
+        id: tag
+        uses: dawidd6/action-get-tag@v1
+        with:
+          strip_v: false
+      - name: Publish flipper-chess
+        uses: softprops/action-gh-release@v1
+        with:
+          files: |
+            build/f7-firmware-C/.extapps/chess.fap
+            applications_user/flipper-chess/README.md
+          name: ${{steps.tag.outputs.tag}}
+          body: Built against Flipper Zero firmware v${{ env.firmware_version }}
+          generate_release_notes: true
+          fail_on_unmatched_files: true

+ 35 - 0
chess/.github/workflows/ufbt_build.yaml

@@ -0,0 +1,35 @@
+name: "FAP: Build for multiple SDK sources"
+on:
+  push:
+    branches: 
+      - main
+      - develop
+  pull_request:
+  # schedule: 
+    # do a build every day
+    # - cron: "1 1 * * *"
+jobs:
+  ufbt-build-action:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        include:
+          - name: dev channel
+            sdk-channel: dev
+          - name: release channel
+            sdk-channel: release
+    name: 'ufbt: Build for ${{ matrix.name }}'
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v3
+      - name: Build with ufbt
+        uses: flipperdevices/flipperzero-ufbt-action@v0.1.2
+        id: build-app
+        with:
+          sdk-channel: ${{ matrix.sdk-channel }}
+          sdk-index-url: ${{ matrix.sdk-index-url }}
+      - name: Upload app artifacts
+        uses: actions/upload-artifact@v3
+        with:
+          name: ${{ github.event.repository.name }}-${{ steps.build-app.outputs.suffix }}
+          path: ${{ steps.build-app.outputs.fap-artifacts }}

+ 52 - 0
chess/.gitignore

@@ -0,0 +1,52 @@
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf

+ 21 - 0
chess/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Struan Clark
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 34 - 0
chess/README.md

@@ -0,0 +1,34 @@
+# flipper-chess
+
+[![Build](https://github.com/xtruan/flipper-chess/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/xtruan/flipper-chess/actions/workflows/build.yml)
+
+`HOW ABOUT A NICE GAME OF CHESS?`
+
+![FLIPR](https://github.com/xtruan/flipper-chess/blob/main/icons/FLIPR_128x64.png)
+
+## Chess game for Flipper Zero
+- Built against `0.86.1` Flipper Zero firmware release
+- Uses [smallchesslib](https://codeberg.org/drummyfish/smallchesslib)
+- Included in [RogueMaster Custom Firmware](https://github.com/RogueMaster/flipperzero-firmware-wPlugins)
+
+### Installation
+
+- Download [last release fap file](https://github.com/xtruan/flipper-chess/releases/latest)
+- Copy fap file to the apps folder of your Flipper SD card
+
+### Usage
+
+- Start "Chess" plugin
+
+### Build
+
+- Recursively clone your base firmware (official or not)
+- Clone this repository in `applications_user`
+- Build with `./fbt fap_dist APPSRC=applications_user/flipper-chess`
+- Retrieve build fap in dist subfolders
+
+(More info about build tool [here](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md))
+
+### 
+
+

+ 19 - 0
chess/application.fam

@@ -0,0 +1,19 @@
+App(
+    appid="chess",
+    name="Chess",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="flipchess_app",
+    requires=[
+        "gui",
+    ],
+    stack_size=4 * 1024,
+    order=10,
+    fap_icon="flipchess_10px.png",
+    fap_icon_assets="icons",
+    fap_icon_assets_symbol="flipchess",
+    fap_category="Games",
+    fap_author="Struan Clark (xtruan)",
+    fap_weburl="https://github.com/xtruan/flipper-chess",
+    fap_version=(1, 9),
+    fap_description="Chess for Flipper",
+)

BIN
chess/catalog/browser.png


BIN
chess/catalog/game1.png


BIN
chess/catalog/game2.png


BIN
chess/catalog/game3.png


BIN
chess/catalog/game4.png


BIN
chess/catalog/game5.png


+ 16 - 0
chess/catalog/manifest.yml

@@ -0,0 +1,16 @@
+sourcecode:
+  type: git
+  location:
+    origin: https://github.com/xtruan/flipper-chess.git
+    commit_sha: c340eb5ec6a68f9e1213d725002d25812bbe989b
+description: "How about a nice game of chess?"
+changelog: "v1.9.0 - Gameplay enhancements and bug fixes"
+author: "@xtruan"
+screenshots:
+  - "./catalog/startscreen.png"
+  - "./catalog/menu.png"
+  - "./catalog/game2.png"
+  - "./catalog/game3.png"
+  - "./catalog/game4.png"
+  - "./catalog/game5.png"
+  - "./catalog/settings.png"

BIN
chess/catalog/menu.png


BIN
chess/catalog/settings.png


BIN
chess/catalog/startscreen.png


+ 3492 - 0
chess/chess/smallchesslib.h

@@ -0,0 +1,3492 @@
+#ifndef SMALLCHESSLIB_H
+#define SMALLCHESSLIB_H
+
+/**
+  @file smallchesslib.h
+
+  Small and simple single header C99 public domain chess library and engine.
+
+  author: Miloslav Ciz (drummyfish)
+  license: CC0 1.0 (public domain)
+           found at https://creativecommons.org/publicdomain/zero/1.0/
+           + additional waiver of all IP
+  version: 0.8d
+
+  Default notation format for this library is a coordinate one, i.e.
+  
+    squarefrom squareto [promotedpiece]
+
+  e.g.: e2e4 or A2A1q
+
+  This work's goal is to never be encumbered by any exclusive intellectual
+  property rights. The work is therefore provided under CC0 1.0 + additional
+  WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
+  intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
+  INTELLECTUAL PROPERTY RGHTS is as follows:
+
+  Each contributor to this work agrees that they waive any exclusive rights,
+  including but not limited to copyright, patents, trademark, trade dress,
+  industrial design, plant varieties and trade secrets, to any and all ideas,
+  concepts, processes, discoveries, improvements and inventions conceived,
+  discovered, made, designed, researched or developed by the contributor either
+  solely or jointly with others, which relate to this work or result from this
+  work. Should any waiver of such right be judged legally invalid or
+  ineffective under applicable law, the contributor hereby grants to each
+  affected person a royalty-free, non transferable, non sublicensable, non
+  exclusive, irrevocable and unconditional license to this right.
+*/
+
+#include <stdint.h>
+
+#ifndef SCL_DEBUG_AI
+/** AI will print out a Newick-like tree of searched moves. */
+#define SCL_DEBUG_AI 0
+#endif
+
+/**
+  Maximum number of moves a chess piece can have (a queen in the middle of the
+  board).
+*/
+#define SCL_CHESS_PIECE_MAX_MOVES 25
+#define SCL_BOARD_SQUARES 64
+
+typedef uint8_t (*SCL_RandomFunction)(void);
+
+#if SCL_COUNT_EVALUATED_POSITIONS
+uint32_t SCL_positionsEvaluated = 0; /**< If enabled by 
+                                            SCL_COUNT_EVALUATED_POSITIONS, this
+                                            will increment with every
+                                            dynamically evaluated position (e.g.
+                                            when AI computes its move). */
+#endif
+
+#ifndef SCL_CALL_WDT_RESET
+#define SCL_CALL_WDT_RESET \
+    0 /**< Option that should be enabled on some
+                                    Arduinos. If 1, call to watchdog timer
+                                    reset will be performed during dynamic
+                                    evaluation (without it if AI takes long the
+                                    program will reset). */
+#endif
+
+/**
+  Returns a pseudorandom byte. This function has a period 256 and returns each
+  possible byte value exactly once in the period.
+*/
+uint8_t SCL_randomSimple(void);
+void SCL_randomSimpleSeed(uint8_t seed);
+
+/**
+  Like SCL_randomSimple, but internally uses a 16 bit value, so the period is
+  65536.
+*/
+uint8_t SCL_randomBetter(void);
+void SCL_randomBetterSeed(uint16_t seed);
+
+#ifndef SCL_EVALUATION_FUNCTION
+/**
+    If defined, AI will always use the static evaluation function with this
+    name. This helps avoid pointers to functions and can be faster but the
+    function can't be changed at runtime.
+  */
+#define SCL_EVALUATION_FUNCTION
+#undef SCL_EVALUATION_FUNCTION
+#endif
+
+#ifndef SCL_960_CASTLING
+/**
+    If set, chess 960 (Fisher random) castling will be considered by the library
+    rather than normal castling. 960 castling is slightly different (e.g.
+    requires the inital rook positions to be stored in board state). The
+    castling move is performed as "capturing own rook".
+  */
+#define SCL_960_CASTLING 0
+#endif
+
+#ifndef SCL_ALPHA_BETA
+/**
+    Turns alpha-beta pruning (AI optimization) on or off. This can gain
+    performance and should normally be turned on. AI behavior should not
+    change at all.
+  */
+#define SCL_ALPHA_BETA 1
+#endif
+
+/**
+  A set of game squares as a bit array, each bit representing one game square.
+  Useful for representing e.g. possible moves. To easily iterate over the set
+  use provided macros (SCL_SQUARE_SET_ITERATE, ...).
+*/
+typedef uint8_t SCL_SquareSet[8];
+
+#define SCL_SQUARE_SET_EMPTY \
+    { 0, 0, 0, 0, 0, 0, 0, 0 }
+
+void SCL_squareSetClear(SCL_SquareSet squareSet);
+void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square);
+uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square);
+uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet);
+uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet);
+
+/**
+  Returns a random square from a square set.
+*/
+uint8_t SCL_squareSetGetRandom(const SCL_SquareSet squareSet, SCL_RandomFunction randFunc);
+
+#define SCL_SQUARE_SET_ITERATE_BEGIN(squareSet)                  \
+    {                                                            \
+        uint8_t iteratedSquare = 0;                              \
+        uint8_t iterationEnd = 0;                                \
+        for(int8_t _i = 0; _i < 8 && !iterationEnd; ++_i) {      \
+            uint8_t _row = squareSet[_i];                        \
+            if(_row == 0) {                                      \
+                iteratedSquare += 8;                             \
+                continue;                                        \
+            }                                                    \
+                                                                 \
+            for(uint8_t _j = 0; _j < 8 && !iterationEnd; ++_j) { \
+                if(_row & 0x01) {
+/*
+  Between SCL_SQUARE_SET_ITERATE_BEGIN and _END iteratedSquare variable
+  represents the next square contained in the set. To break out of the
+  iteration set iterationEnd to 1.
+*/
+
+#define SCL_SQUARE_SET_ITERATE_END \
+    }                              \
+    _row >>= 1;                    \
+    iteratedSquare++;              \
+    }                              \
+    } /*for*/                      \
+    }
+
+#define SCL_SQUARE_SET_ITERATE(squareSet, command) \
+    SCL_SQUARE_SET_ITERATE_BEGIN(squareSet){command} SCL_SQUARE_SET_ITERATE_END
+
+#define SCL_BOARD_STATE_SIZE 69
+
+/**
+  Represents chess board state as a string in this format:
+  - First 64 characters represent the chess board (A1, B1, ... H8), each field
+    can be either a piece (PRNBKQprnbkq) or empty ('.'). I.e. the state looks
+    like this:
+
+      0 (A1) RNBQKBNR 
+             PPPPPPPP
+             ........
+             ........
+             ........
+             ........
+             pppppppp
+             rnbqkbnr 63 (H8)
+
+  - After this more bytes follow to represent global state, these are:
+    - 64: bits holding en-passant and castling related information:
+      - bits 0-3 (lsb): Column of the pawn that can, in current turn, be
+        taken by en-passant (0xF means no pawn can be taken this way).
+      - bit 4: Whether white is not prevented from short castling by previous
+        king or rook movement.
+      - bit 5: Same as 4, but for long castling.
+      - bit 6: Same as 4, but for black.
+      - bit 7: Same as 4, but for black and long castling.
+    - 65: Number saying the number of ply (half-moves) that have already been
+      played, also determining whose turn it currently is.
+    - 66: Move counter used in the 50 move rule, says the number of ply since
+      the last pawn move or capture.
+    - 67: Extra byte, left for storing additional info in variants. For normal
+      chess this byte should always be 0.
+    - 68: The last byte is always 0 to properly terminate the string in case
+      someone tries to print it.
+  - The state is designed so as to be simple and also print-friendly, i.e. you
+    can simply print it with line break after 8 characters to get a human
+    readable representation of the board.
+
+  NOTE: there is a much more compact representation which however trades some
+  access speed which would affect the AI performance and isn't print friendly,
+  so we don't use it. In it each square takes 4 bits, using 15 out of 16
+  possible values (empty square and W and B pieces including 2 types of pawns,
+  one "en-passant takeable"). Then only one extra byte needed is for castling
+  info (4 bits) and ply count (4 bits).
+*/
+typedef char SCL_Board[SCL_BOARD_STATE_SIZE];
+
+#define SCL_BOARD_ENPASSANT_CASTLE_BYTE 64
+#define SCL_BOARD_PLY_BYTE 65
+#define SCL_BOARD_MOVE_COUNT_BYTE 66
+#define SCL_BOARD_EXTRA_BYTE 67
+
+#if SCL_960_CASTLING
+#define _SCL_EXTRA_BYTE_VALUE (0 | (7 << 3)) // rooks on classic positions
+#else
+#define _SCL_EXTRA_BYTE_VALUE 0
+#endif
+
+#define SCL_BOARD_START_STATE                                                                   \
+    {                                                                                           \
+        82, 78, 66, 81, 75, 66, 78, 82, 80, 80, 80, 80, 80, 80, 80, 80, 46, 46, 46, 46, 46, 46, \
+            46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, \
+            46, 46, 46, 46, 46, 112, 112, 112, 112, 112, 112, 112, 112, 114, 110, 98, 113, 107, \
+            98, 110, 114, (char)0xff, 0, 0, _SCL_EXTRA_BYTE_VALUE, 0                            \
+    }
+
+#define SCL_FEN_START "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
+
+#define SCL_FEN_HORDE "ppp2ppp/pppppppp/pppppppp/pppppppp/3pp3/8/PPPPPPPP/RNBQKBNR w KQ - 0 1"
+
+#define SCL_FEN_UPSIDE_DOWN "RNBKQBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbkqbnr w - - 0 1"
+
+#define SCL_FEN_PEASANT_REVOLT "1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
+
+#define SCL_FEN_ENDGAME "4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
+
+#define SCL_FEN_KNIGHTS "N6n/1N4n1/2N2n2/3Nn3/k2nN2K/2n2N2/1n4N1/n6N w - - 0 1"
+
+/**
+  Holds an info required to undo a single move.
+*/
+typedef struct {
+    uint8_t squareFrom; ///< start square
+    uint8_t squareTo; ///< target square
+    char enPassantCastle; ///< previous en passant/castle byte
+    char moveCount; ///< previous values of the move counter byte
+    uint8_t other; /**< lowest 7 bits: previous value of target square,
+                                highest bit: if 1 then the move was promotion or
+                                en passant */
+} SCL_MoveUndo;
+
+#define SCL_GAME_STATE_PLAYING 0x00
+#define SCL_GAME_STATE_WHITE_WIN 0x01
+#define SCL_GAME_STATE_BLACK_WIN 0x02
+#define SCL_GAME_STATE_DRAW 0x10 ///< further unspecified draw
+#define SCL_GAME_STATE_DRAW_STALEMATE 0x11 ///< draw by stalemate
+#define SCL_GAME_STATE_DRAW_REPETITION 0x12 ///< draw by repetition
+#define SCL_GAME_STATE_DRAW_50 0x13 ///< draw by 50 move rule
+#define SCL_GAME_STATE_DRAW_DEAD 0x14 ///< draw by dead position
+#define SCL_GAME_STATE_END 0xff ///< end without known result
+
+/**
+  Converts square in common notation (e.g. 'c' 8) to square number. Only accepts
+  lowercase column.
+*/
+#define SCL_SQUARE(colChar, rowInt) (((rowInt)-1) * 8 + ((colChar) - 'a'))
+#define SCL_S(c, r) SCL_SQUARE(c, r)
+
+void SCL_boardInit(SCL_Board board);
+void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo);
+
+/**
+  Initializes given chess 960 (Fisher random) position. If SCL_960_CASTLING
+  is not set, castling will be disabled by this function.
+*/
+void SCL_boardInit960(SCL_Board board, uint16_t positionNumber);
+
+void SCL_boardDisableCastling(SCL_Board board);
+
+uint32_t SCL_boardHash32(const SCL_Board board);
+
+#define SCL_PHASE_OPENING 0
+#define SCL_PHASE_MIDGAME 1
+#define SCL_PHASE_ENDGAME 2
+
+/**
+  Estimates the game phase: opening, midgame or endgame.
+*/
+uint8_t SCL_boardEstimatePhase(SCL_Board board);
+
+/**
+  Sets the board position. The input string should be 64 characters long zero
+  terminated C string representing the board as squares A1, A2, ..., H8 with
+  each char being either a piece (RKBKQPrkbkqp) or an empty square ('.').
+*/
+void SCL_boardSetPosition(
+    SCL_Board board,
+    const char* pieces,
+    uint8_t castlingEnPassant,
+    uint8_t moveCount,
+    uint8_t ply);
+
+uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2);
+
+/**
+  Gets a random move on given board for the player whose move it is.
+*/
+void SCL_boardRandomMove(
+    SCL_Board board,
+    SCL_RandomFunction randFunc,
+    uint8_t* squareFrom,
+    uint8_t* squareTo,
+    char* resultProm);
+
+#define SCL_FEN_MAX_LENGTH 90
+
+/**
+  Converts a position to FEN (Forsyth–Edwards Notation) string. The string has
+  to have at least SCL_FEN_MAX_LENGTH bytes allocated to guarantee the
+  function won't write to unallocated memory. The string will be terminated by
+  0 (this is included in SCL_FEN_MAX_LENGTH). The number of bytes written
+  (including the terminating 0) is returned.
+*/
+uint8_t SCL_boardToFEN(SCL_Board board, char* string);
+
+/**
+  Loads a board from FEN (Forsyth–Edwards Notation) string. Returns 1 on
+  success, 0 otherwise. XFEN isn't supported fully but a start position in
+  chess960 can be loaded with this function. 
+*/
+uint8_t SCL_boardFromFEN(SCL_Board board, const char* string);
+
+/**
+  Returns an approximate/heuristic board rating as a number, 0 meaning equal
+  chances for both players, positive favoring white, negative favoring black.
+*/
+typedef int16_t (*SCL_StaticEvaluationFunction)(SCL_Board);
+
+/* 
+  NOTE: int8_t as a return value was tried for evaluation function, which would
+  be simpler, but it fails to capture important non-material position
+  differences counted in fractions of pawn values, hence we have to use int16_t.
+*/
+
+/**
+  Basic static evaluation function. WARNING: this function supposes a standard
+  chess game, for non-standard positions it may either not work well or even
+  crash the program. You should use a different function for non-standard games.
+*/
+int16_t SCL_boardEvaluateStatic(SCL_Board board);
+
+/**
+  Dynamic evaluation function (search), i.e. unlike SCL_boardEvaluateStatic,
+  this one performs a recursive search for deeper positions to get a more
+  accurate score. Of course, this is much slower and hugely dependent on
+  baseDepth (you mostly want to keep this under 5).
+*/
+int16_t SCL_boardEvaluateDynamic(
+    SCL_Board board,
+    uint8_t baseDepth,
+    uint8_t extensionExtraDepth,
+    SCL_StaticEvaluationFunction evalFunction);
+
+#define SCL_EVALUATION_MAX_SCORE 32600 // don't increase this, we need a margin
+
+/**
+  Checks if the board position is dead, i.e. mate is impossible (e.g. due to
+  insufficient material), which by the rules results in a draw. WARNING: This
+  function may fail to detect some dead positions as this is a non-trivial task.
+*/
+uint8_t SCL_boardDead(SCL_Board board);
+
+/**
+  Tests whether given player is in check.
+*/
+uint8_t SCL_boardCheck(SCL_Board board, uint8_t white);
+
+/**
+  Checks whether given move resets the move counter (used in the 50 move rule).
+*/
+uint8_t SCL_boardMoveResetsCount(SCL_Board board, uint8_t squareFrom, uint8_t squareTo);
+
+uint8_t SCL_boardMate(SCL_Board board);
+
+/**
+  Performs a move on a board WITHOUT checking if the move is legal. Returns an
+  info with which the move can be undone.
+*/
+SCL_MoveUndo
+    SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo, char promotePiece);
+
+void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo);
+
+/**
+  Checks if the game is over, i.e. the current player to move has no legal
+  moves, the game is in dead position etc.
+*/
+uint8_t SCL_boardGameOver(SCL_Board board);
+
+/**
+  Checks if given move is legal.
+*/
+uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom, uint8_t squareTo);
+
+/**
+  Checks if the player to move has at least one legal move.
+*/
+uint8_t SCL_boardMovePossible(SCL_Board board);
+
+#define SCL_POSITION_NORMAL 0x00
+#define SCL_POSITION_CHECK 0x01
+#define SCL_POSITION_MATE 0x02
+#define SCL_POSITION_STALEMATE 0x03
+#define SCL_POSITION_DEAD 0x04
+
+uint8_t SCL_boardGetPosition(SCL_Board board);
+
+/**
+  Returns 1 if the square is attacked by player of given color. This is used to
+  examine checks, so for performance reasons the functions only checks whether
+  or not the square is attacked (not the number of attackers).
+*/
+uint8_t SCL_boardSquareAttacked(SCL_Board board, uint8_t square, uint8_t byWhite);
+
+/**
+  Gets pseudo moves of a piece: all possible moves WITHOUT eliminating moves
+  that lead to own check. To get only legal moves use SCL_boardGetMoves.
+*/
+void SCL_boardGetPseudoMoves(
+    SCL_Board board,
+    uint8_t pieceSquare,
+    uint8_t checkCastling,
+    SCL_SquareSet result);
+
+/**
+  Gets all legal moves of given piece.
+*/
+void SCL_boardGetMoves(SCL_Board board, uint8_t pieceSquare, SCL_SquareSet result);
+
+static inline uint8_t SCL_boardWhitesTurn(SCL_Board board);
+
+static inline uint8_t SCL_pieceIsWhite(char piece);
+static inline uint8_t SCL_squareIsWhite(uint8_t square);
+char SCL_pieceToColor(uint8_t piece, uint8_t toWhite);
+
+/**
+  Converts square coordinates to square number. Each coordinate must be a number
+  <1,8>. Validity of the coordinates is NOT checked.
+*/
+static inline uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column);
+
+#ifndef SCL_VALUE_PAWN
+#define SCL_VALUE_PAWN 256
+#endif
+
+#ifndef SCL_VALUE_KNIGHT
+#define SCL_VALUE_KNIGHT 768
+#endif
+
+#ifndef SCL_VALUE_BISHOP
+#define SCL_VALUE_BISHOP 800
+#endif
+
+#ifndef SCL_VALUE_ROOK
+#define SCL_VALUE_ROOK 1280
+#endif
+
+#ifndef SCL_VALUE_QUEEN
+#define SCL_VALUE_QUEEN 2304
+#endif
+
+#ifndef SCL_VALUE_KING
+#define SCL_VALUE_KING 0
+#endif
+
+#define SCL_ENDGAME_MATERIAL_LIMIT \
+    (2 *                           \
+     (SCL_VALUE_PAWN * 4 + SCL_VALUE_QUEEN + SCL_VALUE_KING + SCL_VALUE_ROOK + SCL_VALUE_KNIGHT))
+
+#define SCL_START_MATERIAL                                                                    \
+    (16 * SCL_VALUE_PAWN + 4 * SCL_VALUE_ROOK + 4 * SCL_VALUE_KNIGHT + 4 * SCL_VALUE_BISHOP + \
+     2 * SCL_VALUE_QUEEN + 2 * SCL_VALUE_KING)
+
+#ifndef SCL_RECORD_MAX_LENGTH
+#define SCL_RECORD_MAX_LENGTH 256
+#endif
+
+#define SCL_RECORD_MAX_SIZE (SCL_RECORD_MAX_LENGTH * 2)
+
+/**
+  Records a single chess game. The format is following:
+
+  Each record item consists of 2 bytes which record a single move (ply):
+
+  abxxxxxx cdyyyyyy
+
+    xxxxxx  Start square of the move, counted as A0, A1, ...
+    yyyyyy  End square of the move in the same format as the start square.
+    ab      00 means this move isn't the last move of the game, other possible
+            values are 01: white wins, 10: black wins, 11: draw or end for
+            other reasons.
+    cd      In case of pawn promotion move this encodes the promoted piece as
+            00: queen, 01: rook, 10: bishop, 11: knight (pawn isn't allowed by
+            chess rules).
+
+  Every record should be ended by an ending move (ab != 00), empty record should
+  have one move where xxxxxx == yyyyyy == 0 and ab == 11.
+*/
+typedef uint8_t SCL_Record[SCL_RECORD_MAX_SIZE];
+
+#define SCL_RECORD_CONT 0x00
+#define SCL_RECORD_W_WIN 0x40
+#define SCL_RECORD_B_WIN 0x80
+#define SCL_RECORD_END 0xc0
+
+#define SCL_RECORD_PROM_Q 0x00
+#define SCL_RECORD_PROM_R 0x40
+#define SCL_RECORD_PROM_B 0x80
+#define SCL_RECORD_PROM_N 0xc0
+
+#define SCL_RECORD_ITEM(s0, s1, p, e) ((e) | (s0)), ((p) | (s1))
+
+void SCL_recordInit(SCL_Record r);
+
+void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo);
+
+/**
+  Represents a complete game of chess (or a variant with different staring
+  position). This struct along with associated functions allows to easily
+  implement a chess game that allows undoing moves, detecting draws, recording
+  the moves etc. On platforms with extremely little RAM one can reduce
+  SCL_RECORD_MAX_LENGTH to reduce the size of this struct (which will however
+  possibly limit how many moves can be undone).
+*/
+typedef struct {
+    SCL_Board board;
+    SCL_Record record; /**< Holds the game record. This record is here 
+                              firstly because games are usually recorded and
+                              secondly this allows undoing moves up to the 
+                              beginning of the game. This infinite undoing will
+                              only work as long as the record is able to hold
+                              the whole game; if the record is full, undoing is
+                              no longet possible. */
+    uint16_t state;
+    uint16_t ply; ///< ply count (board ply counter is only 8 bit)
+
+    uint32_t prevMoves[14]; ///< stores last moves, for repetition detection
+
+    const char* startState; /**< Optional pointer to the starting board state.
+                              If this is null, standard chess start position is
+                              assumed. This is needed for undoing moves with
+                              game record. */
+} SCL_Game;
+
+/**
+  Initializes a new chess game. The startState parameter is optional and allows
+  for setting up chess variants that differ by starting positions, setting this
+  to 0 will assume traditional starting position. WARNING: if startState is
+  provided, the pointed to board mustn't be deallocated afterwards, the string
+  is not internally copied (for memory saving reasons).
+*/
+void SCL_gameInit(SCL_Game* game, const SCL_Board startState);
+
+void SCL_gameMakeMove(SCL_Game* game, uint8_t squareFrom, uint8_t squareTo, char promoteTo);
+
+uint8_t SCL_gameUndoMove(SCL_Game* game);
+
+/**
+  Gets a move which if played now would cause a draw by repetition. Returns 1
+  if such move exists, 0 otherwise. The results parameters can be set to 0 in
+  which case they will be ignored and only the existence of a draw move will be
+  tested.
+*/
+uint8_t SCL_gameGetRepetiotionMove(SCL_Game* game, uint8_t* squareFrom, uint8_t* squareTo);
+
+/**
+  Leads a game record from PGN string. The function will probably not strictly
+  adhere to the PGN input format, but should accept most sanely written PGN
+  strings.
+*/
+void SCL_recordFromPGN(SCL_Record r, const char* pgn);
+
+uint16_t SCL_recordLength(const SCL_Record r);
+
+/**
+  Gets the move out of a game record, returns the end state of the move
+  (SCL_RECORD_CONT, SCL_RECORD_END etc.)
+*/
+uint8_t SCL_recordGetMove(
+    const SCL_Record r,
+    uint16_t index,
+    uint8_t* squareFrom,
+    uint8_t* squareTo,
+    char* promotedPiece);
+
+/**
+  Adds another move to the game record. Terminating the record is handled so
+  that the last move is always marked with end flag, endState is here to only
+  indicate possible game result (otherwise pass SCL_RECORD_CONT). Returns 1 if
+  the item was added, otherwise 0 (replay was already of maximum size).
+*/
+uint8_t SCL_recordAdd(
+    SCL_Record r,
+    uint8_t squareFrom,
+    uint8_t squareTo,
+    char promotePiece,
+    uint8_t endState);
+
+/**
+  Removes the last move from the record, returns 1 if the replay is non-empty
+  after the removal, otherwise 0.
+*/
+uint8_t SCL_recordRemoveLast(SCL_Record r);
+
+/**
+  Applies given number of half-moves (ply) to a given board (the board is
+  automatically initialized at the beginning).
+*/
+void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves);
+
+int16_t SCL_pieceValue(char piece);
+int16_t SCL_pieceValuePositive(char piece);
+
+#define SCL_PRINT_FORMAT_NONE 0
+#define SCL_PRINT_FORMAT_NORMAL 1
+#define SCL_PRINT_FORMAT_COMPACT 2
+#define SCL_PRINT_FORMAT_UTF8 3
+#define SCL_PRINT_FORMAT_COMPACT_UTF8 4
+
+/**
+  Gets the best move for the currently moving player as computed by AI. The
+  return value is the value of the move (with the same semantics as the value
+  of an evaluation function). baseDepth is depth in plys to which all moves will
+  be checked. If baseDepth 0 is passed, the function makes a random move and
+  returns the evaluation of the board. extensionExtraDepth is extra depth for
+  checking specific situations like exchanges and checks. endgameExtraDepth is
+  extra depth which is added to baseDepth in the endgame. If the randomness
+  function is 0, AI will always make the first best move it finds, if it is
+  not 0 and randomness is 0, AI will randomly pick between the equally best
+  moves, if it is not 0 and randomness is positive, AI will randomly choose
+  between best moves with some bias (may not pick the best rated move).
+*/
+int16_t SCL_getAIMove(
+    SCL_Board board,
+    uint8_t baseDepth,
+    uint8_t extensionExtraDepth,
+    uint8_t endgameExtraDepth,
+    SCL_StaticEvaluationFunction evalFunc,
+    SCL_RandomFunction randFunc,
+    uint8_t randomness,
+    uint8_t repetitionMoveFrom,
+    uint8_t repetitionMoveTo,
+    uint8_t* resultFrom,
+    uint8_t* resultTo,
+    char* resultProm);
+
+/**
+  Function that prints out a single character. This is passed to printing
+  functions.
+*/
+typedef void (*SCL_PutCharFunction)(char);
+
+/**
+  Prints given chessboard using given format and an abstract printing function.
+*/
+void SCL_printBoard(
+    SCL_Board board,
+    SCL_PutCharFunction putCharFunc,
+    SCL_SquareSet highlightSquares,
+    uint8_t selectSquare,
+    uint8_t format,
+    uint8_t offset,
+    uint8_t labels,
+    uint8_t blackDown);
+
+void SCL_printBoardSimple(
+    SCL_Board board,
+    SCL_PutCharFunction putCharFunc,
+    uint8_t selectSquare,
+    uint8_t format);
+
+void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc);
+void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc, SCL_Board initialState);
+
+/**
+  Reads a move from string (the notation format is described at the top of this
+  file). The function is safe as long as the string is 0 terminated. Returns 1
+  on success or 0 on fail (invalid move string).
+*/
+uint8_t SCL_stringToMove(
+    const char* moveString,
+    uint8_t* resultFrom,
+    uint8_t* resultTo,
+    char* resultPromotion);
+
+char* SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1, char promotion, char* string);
+
+/**
+  Function used in drawing, it is called to draw the next pixel. The first
+  parameter is the pixel color, the second one if the sequential number of the
+  pixel.
+*/
+typedef void (*SCL_PutPixelFunction)(uint8_t, uint16_t);
+
+#define SCL_BOARD_PICTURE_WIDTH 64
+
+/**
+  Draws a simple 1bit 64x64 pixels board using a provided abstract function for
+  drawing pixels. The function renders from top left to bottom right, i.e. no
+  frame buffer is required.
+*/
+void SCL_drawBoard(
+    SCL_Board board,
+    SCL_PutPixelFunction putPixel,
+    uint8_t selectedSquare,
+    SCL_SquareSet highlightSquares,
+    uint8_t blackDown);
+
+/**
+  Converts square number to string representation (e.g. "d2"). This function
+  will modify exactly the first two bytes of the provided string.
+*/
+static inline char* SCL_squareToString(uint8_t square, char* string);
+
+/**
+  Converts a string, such as "A1" or "b4", to square number. The string must
+  start with a letter (lower or upper case) and be followed by a number <1,8>.
+  Validity of the string is NOT checked.
+*/
+uint8_t SCL_stringToSquare(const char* square);
+
+//=============================================================================
+// privates:
+
+#define SCL_UNUSED(v) (void)(v)
+
+uint8_t SCL_currentRandom8 = 0;
+
+uint16_t SCL_currentRandom16 = 0;
+
+void SCL_randomSimpleSeed(uint8_t seed) {
+    SCL_currentRandom8 = seed;
+}
+
+uint8_t SCL_randomSimple(void) {
+    SCL_currentRandom8 *= 13;
+    SCL_currentRandom8 += 7;
+    return SCL_currentRandom8;
+}
+
+uint8_t SCL_randomBetter(void) {
+    SCL_currentRandom16 *= 13;
+    SCL_currentRandom16 += 7;
+    return (SCL_currentRandom16 % 256) ^ (SCL_currentRandom16 / 256);
+}
+
+void SCL_randomBetterSeed(uint16_t seed) {
+    SCL_currentRandom16 = seed;
+}
+
+void SCL_squareSetClear(SCL_SquareSet squareSet) {
+    for(uint8_t i = 0; i < 8; ++i) squareSet[i] = 0;
+}
+
+uint8_t SCL_stringToSquare(const char* square) {
+    return (square[1] - '1') * 8 +
+           (square[0] - ((square[0] >= 'A' && square[0] <= 'Z') ? 'A' : 'a'));
+}
+
+char* SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1, char promotion, char* string) {
+    char* result = string;
+
+    SCL_squareToString(s0, string);
+    string += 2;
+    string = SCL_squareToString(s1, string);
+    string += 2;
+
+    char c = board[s0];
+
+    if(c == 'p' || c == 'P') {
+        uint8_t rank = s1 / 8;
+
+        if(rank == 0 || rank == 7) {
+            *string = promotion;
+            string++;
+        }
+    }
+
+    *string = 0;
+
+    return result;
+}
+
+uint8_t SCL_boardWhitesTurn(SCL_Board board) {
+    return (board[SCL_BOARD_PLY_BYTE] % 2) == 0;
+}
+
+uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column) {
+    return row * 8 + column;
+}
+
+uint8_t SCL_pieceIsWhite(char piece) {
+    return piece < 'a';
+}
+
+char* SCL_squareToString(uint8_t square, char* string) {
+    string[0] = 'a' + square % 8;
+    string[1] = '1' + square / 8;
+
+    return string;
+}
+
+uint8_t SCL_squareIsWhite(uint8_t square) {
+    return (square % 2) != ((square / 8) % 2);
+}
+
+char SCL_pieceToColor(uint8_t piece, uint8_t toWhite) {
+    return (SCL_pieceIsWhite(piece) == toWhite) ? piece : (piece + (toWhite ? -32 : 32));
+}
+
+/**
+  Records the rook starting positions in the board state. This is required in
+  chess 960 in order to be able to correctly perform castling (castling rights
+  knowledge isn't enough as one rook might have moved to the other side and we
+  wouldn't know which one can castle and which not).
+*/
+void _SCL_board960RememberRookPositions(SCL_Board board) {
+    uint8_t pos = 0;
+    uint8_t rooks = 2;
+
+    while(pos < 8 && rooks != 0) {
+        if(board[pos] == 'R') {
+            board[SCL_BOARD_EXTRA_BYTE] = rooks == 2 ? pos :
+                                                       (board[SCL_BOARD_EXTRA_BYTE] | (pos << 3));
+
+            rooks--;
+        }
+
+        pos++;
+    }
+}
+
+void SCL_boardInit(SCL_Board board) {
+    /*
+    We might use SCL_BOARD_START_STATE and copy it to the board, but that might
+    waste RAM on Arduino, so we init the board by code.
+  */
+
+    char* b = board;
+
+    *b = 'R';
+    b++;
+    *b = 'N';
+    b++;
+    *b = 'B';
+    b++;
+    *b = 'Q';
+    b++;
+    *b = 'K';
+    b++;
+    *b = 'B';
+    b++;
+    *b = 'N';
+    b++;
+    *b = 'R';
+    b++;
+
+    char* b2 = board + 48;
+
+    for(uint8_t i = 0; i < 8; ++i, b++, b2++) {
+        *b = 'P';
+        *b2 = 'p';
+    }
+
+    for(uint8_t i = 0; i < 32; ++i, b++) *b = '.';
+
+    b += 8;
+
+    *b = 'r';
+    b++;
+    *b = 'n';
+    b++;
+    *b = 'b';
+    b++;
+    *b = 'q';
+    b++;
+    *b = 'k';
+    b++;
+    *b = 'b';
+    b++;
+    *b = 'n';
+    b++;
+    *b = 'r';
+    b++;
+
+    for(uint8_t i = 0; i < SCL_BOARD_STATE_SIZE - SCL_BOARD_SQUARES; ++i, ++b) *b = 0;
+
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = (char)0xff;
+
+#if SCL_960_CASTLING
+    _SCL_board960RememberRookPositions(board);
+#endif
+}
+
+void _SCL_boardPlaceOnNthAvailable(SCL_Board board, uint8_t pos, char piece) {
+    char* c = board;
+
+    while(1) {
+        if(*c == '.') {
+            if(pos == 0) break;
+
+            pos--;
+        }
+
+        c++;
+    }
+
+    *c = piece;
+}
+
+void SCL_boardInit960(SCL_Board board, uint16_t positionNumber) {
+    SCL_Board b;
+
+    SCL_boardInit(b);
+
+    for(uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
+        board[i] = ((i >= 8 && i < 56) || i >= 64) ? b[i] : '.';
+
+    uint8_t helper = positionNumber % 16;
+
+    board[(helper / 4) * 2] = 'B';
+    board[1 + (helper % 4) * 2] = 'B';
+
+    helper = positionNumber / 16;
+
+    // maybe there's a simpler way :)
+
+    _SCL_boardPlaceOnNthAvailable(board, helper % 6, 'Q');
+    _SCL_boardPlaceOnNthAvailable(board, 0, helper <= 23 ? 'N' : 'R');
+
+    _SCL_boardPlaceOnNthAvailable(
+        board, 0, (helper >= 7 && helper <= 23) ? 'R' : (helper > 41 ? 'K' : 'N'));
+
+    _SCL_boardPlaceOnNthAvailable(
+        board,
+        0,
+        (helper <= 5 || helper >= 54) ?
+            'R' :
+            (((helper >= 12 && helper <= 23) || (helper >= 30 && helper <= 41)) ? 'K' : 'N'));
+
+    _SCL_boardPlaceOnNthAvailable(
+        board,
+        0,
+        (helper <= 11 || (helper <= 29 && helper >= 24)) ?
+            'K' :
+            (((helper >= 18 && helper <= 23) || (helper >= 36 && helper <= 41) ||
+              (helper >= 48 && helper <= 53)) ?
+                 'R' :
+                 'N'));
+
+    uint8_t rooks = 0;
+
+    for(uint8_t i = 0; i < 8; ++i)
+        if(board[i] == 'R') rooks++;
+
+    _SCL_boardPlaceOnNthAvailable(board, 0, rooks == 2 ? 'N' : 'R');
+
+    for(uint8_t i = 0; i < 8; ++i) board[56 + i] = SCL_pieceToColor(board[i], 0);
+
+#if SCL_960_CASTLING
+    _SCL_board960RememberRookPositions(board);
+#else
+    SCL_boardDisableCastling(board);
+#endif
+}
+
+uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2) {
+    const char *p1 = b1, *p2 = b2;
+
+    while(p1 < b1 + SCL_BOARD_STATE_SIZE) {
+        if(*p1 != *p2) return 1;
+
+        p1++;
+        p2++;
+    }
+
+    return 0;
+}
+
+void SCL_recordInit(SCL_Record r) {
+    r[0] = 0 | SCL_RECORD_END;
+    r[1] = 0;
+}
+
+void SCL_recordFromPGN(SCL_Record r, const char* pgn) {
+    SCL_Board board;
+
+    SCL_boardInit(board);
+
+    SCL_recordInit(r);
+
+    uint8_t state = 0;
+    uint8_t evenMove = 0;
+
+    while(*pgn != 0) {
+        switch(state) {
+        case 0: // skipping tags and spaces, outside []
+            if(*pgn == '1')
+                state = 2;
+            else if(*pgn == '[')
+                state = 1;
+
+            break;
+
+        case 1: // skipping tags and spaces, inside []
+            if(*pgn == ']') state = 0;
+
+            break;
+
+        case 2: // reading move number
+            if(*pgn == '{')
+                state = 3;
+            else if((*pgn >= 'a' && *pgn <= 'h') || (*pgn >= 'A' && *pgn <= 'Z')) {
+                state = 4;
+                pgn--;
+            }
+
+            break;
+
+        case 3: // initial comment
+            if(*pgn == '}') state = 2;
+
+            break;
+
+        case 4: // reading move
+        {
+            char piece = 'p';
+            char promoteTo = 'q';
+            uint8_t castle = 0;
+            uint8_t promotion = 0;
+
+            int8_t coords[4];
+
+            uint8_t ranks = 0, files = 0;
+
+            for(uint8_t i = 0; i < 4; ++i) coords[i] = -1;
+
+            while(*pgn != ' ' && *pgn != '\n' && *pgn != '\t' && *pgn != '{' && *pgn != 0) {
+                if(*pgn == '=') promotion = 1;
+                if(*pgn == 'O' || *pgn == '0') castle++;
+                if(*pgn >= 'A' && *pgn <= 'Z') {
+                    if(promotion)
+                        promoteTo = *pgn;
+                    else
+                        piece = *pgn;
+                } else if(*pgn >= 'a' && *pgn <= 'h') {
+                    coords[files * 2] = *pgn - 'a';
+                    files++;
+                } else if(*pgn >= '1' && *pgn <= '8') {
+                    coords[1 + ranks * 2] = *pgn - '1';
+                    ranks++;
+                }
+
+                pgn++;
+            }
+
+            if(castle) {
+                piece = 'K';
+
+                coords[0] = 4;
+                coords[1] = 0;
+                coords[2] = castle < 3 ? 6 : 2;
+                coords[3] = 0;
+
+                if(evenMove) {
+                    coords[1] = 7;
+                    coords[3] = 7;
+                }
+            }
+
+            piece = SCL_pieceToColor(piece, evenMove == 0);
+
+            if(coords[2] < 0) {
+                coords[2] = coords[0];
+                coords[0] = -1;
+            }
+
+            if(coords[3] < 0) {
+                coords[3] = coords[1];
+                coords[1] = -1;
+            }
+
+            uint8_t squareTo = coords[3] * 8 + coords[2];
+
+            if(coords[0] < 0 || coords[1] < 0) {
+                // without complete starting coords we have to find the piece
+
+                for(int i = 0; i < SCL_BOARD_SQUARES; ++i)
+                    if(board[i] == piece) {
+                        SCL_SquareSet s;
+
+                        SCL_squareSetClear(s);
+
+                        SCL_boardGetMoves(board, i, s);
+
+                        if(SCL_squareSetContains(s, squareTo) &&
+                           (coords[0] < 0 || coords[0] == i % 8) &&
+                           (coords[1] < 0 || coords[1] == i / 8)) {
+                            coords[0] = i % 8;
+                            coords[1] = i / 8;
+                            break;
+                        }
+                    }
+            }
+
+            uint8_t squareFrom = coords[1] * 8 + coords[0];
+
+            SCL_boardMakeMove(board, squareFrom, squareTo, promoteTo);
+
+            // for some reason tcc bugs here, the above line sets squareFrom to 0 lol
+            // can be fixed with doing "squareFrom = coords[1] * 8 + coords[0];" again
+
+            SCL_recordAdd(r, squareFrom, squareTo, promoteTo, SCL_RECORD_CONT);
+
+            while(*pgn == ' ' || *pgn == '\n' || *pgn == '\t' || *pgn == '{') {
+                if(*pgn == '{')
+                    while(*pgn != '}') pgn++;
+
+                pgn++;
+            }
+
+            if(*pgn == 0) return;
+
+            pgn--;
+
+            if(evenMove) state = 2;
+
+            evenMove = !evenMove;
+
+            break;
+        }
+
+        default:
+            break;
+        }
+
+        pgn++;
+    }
+}
+
+uint16_t SCL_recordLength(const SCL_Record r) {
+    if((r[0] & 0x3f) == (r[1] & 0x3f)) // empty record that's only terminator
+        return 0;
+
+    uint16_t result = 0;
+
+    while((r[result] & 0xc0) == 0) result += 2;
+
+    return (result / 2) + 1;
+}
+
+uint8_t SCL_recordGetMove(
+    const SCL_Record r,
+    uint16_t index,
+    uint8_t* squareFrom,
+    uint8_t* squareTo,
+    char* promotedPiece) {
+    index *= 2;
+
+    uint8_t b = r[index];
+
+    *squareFrom = b & 0x3f;
+    uint8_t result = b & 0xc0;
+
+    index++;
+
+    b = r[index];
+
+    *squareTo = b & 0x3f;
+
+    b &= 0xc0;
+
+    switch(b) {
+    case SCL_RECORD_PROM_Q:
+        *promotedPiece = 'q';
+        break;
+    case SCL_RECORD_PROM_R:
+        *promotedPiece = 'r';
+        break;
+    case SCL_RECORD_PROM_B:
+        *promotedPiece = 'b';
+        break;
+    case SCL_RECORD_PROM_N:
+    default:
+        *promotedPiece = 'n';
+        break;
+    }
+
+    return result;
+}
+
+uint8_t SCL_recordAdd(
+    SCL_Record r,
+    uint8_t squareFrom,
+    uint8_t squareTo,
+    char promotePiece,
+    uint8_t endState) {
+    uint16_t l = SCL_recordLength(r);
+
+    if(l >= SCL_RECORD_MAX_LENGTH) return 0;
+
+    l *= 2;
+
+    if(l != 0) r[l - 2] &= 0x3f; // remove the end flag from previous item
+
+    if(endState == SCL_RECORD_CONT) endState = SCL_RECORD_END;
+
+    r[l] = squareFrom | endState;
+
+    uint8_t p;
+
+    switch(promotePiece) {
+    case 'n':
+    case 'N':
+        p = SCL_RECORD_PROM_N;
+        break;
+    case 'b':
+    case 'B':
+        p = SCL_RECORD_PROM_B;
+        break;
+    case 'r':
+    case 'R':
+        p = SCL_RECORD_PROM_R;
+        break;
+    case 'q':
+    case 'Q':
+    default:
+        p = SCL_RECORD_PROM_Q;
+        break;
+    }
+
+    l++;
+
+    r[l] = squareTo | p;
+
+    return 1;
+}
+
+uint8_t SCL_recordRemoveLast(SCL_Record r) {
+    uint16_t l = SCL_recordLength(r);
+
+    if(l == 0) return 0;
+
+    if(l == 1)
+        SCL_recordInit(r);
+    else {
+        l = (l - 2) * 2;
+
+        r[l] = (r[l] & 0x3f) | SCL_RECORD_END;
+    }
+
+    return 1;
+}
+
+void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves) {
+    SCL_boardInit(b);
+
+    uint16_t l = SCL_recordLength(r);
+
+    if(moves > l) moves = l;
+
+    for(uint16_t i = 0; i < moves; ++i) {
+        uint8_t s0, s1;
+        char p;
+
+        SCL_recordGetMove(r, i, &s0, &s1, &p);
+        SCL_boardMakeMove(b, s0, s1, p);
+    }
+}
+
+void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo) {
+#if SCL_960_CASTLING
+    char squareToNow = board[moveUndo.squareTo];
+#endif
+
+    board[moveUndo.squareFrom] = board[moveUndo.squareTo];
+    board[moveUndo.squareTo] = moveUndo.other & 0x7f;
+    board[SCL_BOARD_PLY_BYTE]--;
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = moveUndo.enPassantCastle;
+    board[SCL_BOARD_MOVE_COUNT_BYTE] = moveUndo.moveCount;
+
+    if(moveUndo.other & 0x80) {
+        moveUndo.squareTo /= 8;
+
+        if(moveUndo.squareTo == 0 || moveUndo.squareTo == 7)
+            board[moveUndo.squareFrom] = SCL_pieceIsWhite(board[moveUndo.squareFrom]) ? 'P' : 'p';
+        // ^ was promotion
+        else
+            board[(moveUndo.squareFrom / 8) * 8 + (moveUndo.enPassantCastle & 0x0f)] =
+                (board[moveUndo.squareFrom] == 'P') ? 'p' : 'P'; // was en passant
+    }
+#if !SCL_960_CASTLING
+    else if(
+        board[moveUndo.squareFrom] == 'k' && // black castling
+        moveUndo.squareFrom == 60) {
+        if(moveUndo.squareTo == 58) {
+            board[59] = '.';
+            board[56] = 'r';
+        } else if(moveUndo.squareTo == 62) {
+            board[61] = '.';
+            board[63] = 'r';
+        }
+    } else if(
+        board[moveUndo.squareFrom] == 'K' && // white castling
+        moveUndo.squareFrom == 4) {
+        if(moveUndo.squareTo == 2) {
+            board[3] = '.';
+            board[0] = 'R';
+        } else if(moveUndo.squareTo == 6) {
+            board[5] = '.';
+            board[7] = 'R';
+        }
+    }
+#else // 960 castling
+    else if(
+        ((moveUndo.other & 0x7f) == 'r') && // black castling
+        (squareToNow == '.' || !SCL_pieceIsWhite(squareToNow))) {
+        board[moveUndo.squareTo < moveUndo.squareFrom ? 59 : 61] = '.';
+        board[moveUndo.squareTo < moveUndo.squareFrom ? 58 : 62] = '.';
+
+        board[moveUndo.squareFrom] = 'k';
+        board[moveUndo.squareTo] = 'r';
+    } else if(
+        ((moveUndo.other & 0x7f) == 'R') && // white castling
+        (squareToNow == '.' || SCL_pieceIsWhite(squareToNow))) {
+        board[moveUndo.squareTo < moveUndo.squareFrom ? 3 : 5] = '.';
+        board[moveUndo.squareTo < moveUndo.squareFrom ? 2 : 6] = '.';
+
+        board[moveUndo.squareFrom] = 'K';
+        board[moveUndo.squareTo] = 'R';
+    }
+#endif
+}
+
+/**
+  Potentially disables castling rights according to whether something moved from
+  or to a square with a rook.
+*/
+void _SCL_handleRookActivity(SCL_Board board, uint8_t rookSquare) {
+#if !SCL_960_CASTLING
+    switch(rookSquare) {
+    case 0:
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x20;
+        break;
+    case 7:
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x10;
+        break;
+    case 56:
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x80;
+        break;
+    case 63:
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x40;
+        break;
+    default:
+        break;
+    }
+#else // 960 castling
+    if(rookSquare == (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x20;
+    else if(rookSquare == (board[SCL_BOARD_EXTRA_BYTE] >> 3))
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x10;
+    else if(rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x80;
+    else if(rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] >> 3))
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t)~0x40;
+#endif
+}
+
+SCL_MoveUndo
+    SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo, char promotePiece) {
+    char s = board[squareFrom];
+
+    SCL_MoveUndo moveUndo;
+
+    moveUndo.squareFrom = squareFrom;
+    moveUndo.squareTo = squareTo;
+    moveUndo.moveCount = board[SCL_BOARD_MOVE_COUNT_BYTE];
+    moveUndo.enPassantCastle = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE];
+    moveUndo.other = board[squareTo];
+
+    // reset the en-passant state
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] |= 0x0f;
+
+    if(SCL_boardMoveResetsCount(board, squareFrom, squareTo))
+        board[SCL_BOARD_MOVE_COUNT_BYTE] = 0;
+    else
+        board[SCL_BOARD_MOVE_COUNT_BYTE]++;
+
+#if SCL_960_CASTLING
+    uint8_t castled = 0;
+#endif
+
+    if((s == 'k') || (s == 'K')) {
+#if !SCL_960_CASTLING
+        if((squareFrom == 4) || (squareFrom == 60)) // check castling
+        {
+            int8_t difference = squareTo - squareFrom;
+
+            char rook = SCL_pieceToColor('r', SCL_pieceIsWhite(s));
+
+            if(difference == 2) // short
+            {
+                board[squareTo - 1] = rook;
+                board[squareTo + 1] = '.';
+            } else if(difference == -2) // long
+            {
+                board[squareTo - 2] = '.';
+                board[squareTo + 1] = rook;
+            }
+        }
+#else // 960 castling
+        uint8_t isWhite = SCL_pieceIsWhite(s);
+        char rook = SCL_pieceToColor('r', isWhite);
+
+        if(board[squareTo] == rook) {
+            castled = 1;
+
+            board[squareFrom] = '.';
+            board[squareTo] = '.';
+
+            if(squareTo > squareFrom) // short
+            {
+                board[isWhite ? 6 : (56 + 6)] = s;
+                board[isWhite ? 5 : (56 + 5)] = rook;
+            } else // long
+            {
+                board[isWhite ? 2 : (56 + 2)] = s;
+                board[isWhite ? 3 : (56 + 3)] = rook;
+            }
+        }
+#endif
+
+        // after king move disable castling
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= ~(0x03 << ((s == 'K') ? 4 : 6));
+    } else if((s == 'p') || (s == 'P')) {
+        uint8_t row = squareTo / 8;
+
+        int8_t rowDiff = squareFrom / 8 - row;
+
+        if(rowDiff == 2 || rowDiff == -2) // record en passant column
+        {
+            board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] =
+                (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0) | (squareFrom % 8);
+        }
+
+        if(row == 0 || row == 7) {
+            // promotion
+            s = SCL_pieceToColor(promotePiece, SCL_pieceIsWhite(s));
+
+            moveUndo.other |= 0x80;
+        } else {
+            // check en passant move
+
+            int8_t columnDiff = (squareTo % 8) - (squareFrom % 8);
+
+            if((columnDiff != 0) && (board[squareTo] == '.')) {
+                board[squareFrom + columnDiff] = '.';
+                moveUndo.other |= 0x80;
+            }
+        }
+    } else if((s == 'r') || (s == 'R'))
+        _SCL_handleRookActivity(board, squareFrom);
+
+    char taken = board[squareTo];
+
+    // taking a rook may also disable castling:
+
+    if(taken == 'R' || taken == 'r') _SCL_handleRookActivity(board, squareTo);
+
+#if SCL_960_CASTLING
+    if(!castled)
+#endif
+    {
+        board[squareTo] = s;
+        board[squareFrom] = '.';
+    }
+
+    board[SCL_BOARD_PLY_BYTE]++; // increase ply count
+
+    return moveUndo;
+}
+
+void SCL_boardSetPosition(
+    SCL_Board board,
+    const char* pieces,
+    uint8_t castlingEnPassant,
+    uint8_t moveCount,
+    uint8_t ply) {
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, pieces++)
+        if(*pieces != 0)
+            board[i] = *pieces;
+        else
+            break;
+
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castlingEnPassant;
+    board[SCL_BOARD_PLY_BYTE] = ply;
+    board[SCL_BOARD_MOVE_COUNT_BYTE] = moveCount;
+    board[SCL_BOARD_STATE_SIZE - 1] = 0;
+}
+
+void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square) {
+    squareSet[square / 8] |= 0x01 << (square % 8);
+}
+
+uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square) {
+    return squareSet[square / 8] & (0x01 << (square % 8));
+}
+
+uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet) {
+    uint8_t result = 0;
+
+    for(uint8_t i = 0; i < 8; ++i) {
+        uint8_t byte = squareSet[i];
+
+        for(uint8_t j = 0; j < 8; ++j) {
+            result += byte & 0x01;
+            byte >>= 1;
+        }
+    }
+
+    return result;
+}
+
+uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet) {
+    for(uint8_t i = 0; i < 8; ++i)
+        if(squareSet[i] != 0) return 0;
+
+    return 1;
+}
+
+uint8_t SCL_squareSetGetRandom(const SCL_SquareSet squareSet, SCL_RandomFunction randFunc) {
+    uint8_t size = SCL_squareSetSize(squareSet);
+
+    if(size == 0) return 0;
+
+    uint8_t n = (randFunc() % size) + 1;
+    uint8_t i = 0;
+
+    while(i < SCL_BOARD_SQUARES) {
+        if(SCL_squareSetContains(squareSet, i)) {
+            n--;
+
+            if(n == 0) break;
+        }
+
+        ++i;
+    }
+
+    return i;
+}
+
+void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo) {
+    for(uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i) boardTo[i] = boardFrom[i];
+}
+
+uint8_t SCL_boardSquareAttacked(SCL_Board board, uint8_t square, uint8_t byWhite) {
+    const char* currentSquare = board;
+
+    /* We need to place a temporary piece on the tested square in order to test if
+     the square is attacked (consider testing if attacked by a pawn). */
+
+    char previous = board[square];
+
+    board[square] = SCL_pieceToColor('r', !byWhite);
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++currentSquare) {
+        char s = *currentSquare;
+
+        if((s == '.') || (SCL_pieceIsWhite(s) != byWhite)) continue;
+
+        SCL_SquareSet moves;
+        SCL_boardGetPseudoMoves(board, i, 0, moves);
+
+        if(SCL_squareSetContains(moves, square)) {
+            board[square] = previous;
+            return 1;
+        }
+    }
+
+    board[square] = previous;
+    return 0;
+}
+
+uint8_t SCL_boardCheck(SCL_Board board, uint8_t white) {
+    const char* square = board;
+    char kingChar = white ? 'K' : 'k';
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++square)
+        if((*square == kingChar && SCL_boardSquareAttacked(board, i, !white))) return 1;
+
+    return 0;
+}
+
+uint8_t SCL_boardGameOver(SCL_Board board) {
+    uint8_t position = SCL_boardGetPosition(board);
+
+    return (position == SCL_POSITION_MATE) || (position == SCL_POSITION_STALEMATE) ||
+           (position == SCL_POSITION_DEAD);
+}
+
+uint8_t SCL_boardMovePossible(SCL_Board board) {
+    uint8_t white = SCL_boardWhitesTurn(board);
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) {
+        char s = board[i];
+
+        if((s != '.') && (SCL_pieceIsWhite(s) == white)) {
+            SCL_SquareSet moves;
+
+            SCL_boardGetMoves(board, i, moves);
+
+            if(SCL_squareSetSize(moves) != 0) return 1;
+        }
+    }
+
+    return 0;
+}
+
+uint8_t SCL_boardMate(SCL_Board board) {
+    return SCL_boardGetPosition(board) == SCL_POSITION_MATE;
+}
+
+void SCL_boardGetPseudoMoves(
+    SCL_Board board,
+    uint8_t pieceSquare,
+    uint8_t checkCastling,
+    SCL_SquareSet result) {
+    char piece = board[pieceSquare];
+
+    SCL_squareSetClear(result);
+
+    uint8_t isWhite = SCL_pieceIsWhite(piece);
+    int8_t horizontalPosition = pieceSquare % 8;
+    int8_t pawnOffset = -8;
+
+    switch(piece) {
+    case 'P':
+        pawnOffset = 8;
+        /* FALLTHROUGH */
+    case 'p': {
+        uint8_t square = pieceSquare + pawnOffset;
+        uint8_t verticalPosition = pieceSquare / 8;
+
+        if(board[square] == '.') // forward move
+        {
+            SCL_squareSetAdd(result, square);
+
+            if(verticalPosition == (1 + (piece == 'p') * 5)) // start position?
+            {
+                uint8_t square2 = square + pawnOffset;
+
+                if(board[square2] == '.') SCL_squareSetAdd(result, square2);
+            }
+        }
+
+#define checkDiagonal(hor, add)                                                           \
+    if(horizontalPosition != hor) {                                                       \
+        uint8_t square2 = square + add;                                                   \
+        char c = board[square2];                                                          \
+        if(c != '.' && SCL_pieceIsWhite(c) != isWhite) SCL_squareSetAdd(result, square2); \
+    }
+
+        // diagonal moves
+        checkDiagonal(0, -1) checkDiagonal(7, 1)
+
+            uint8_t enPassantRow = 4;
+        uint8_t enemyPawn = 'p';
+
+        if(piece == 'p') {
+            enPassantRow = 3;
+            enemyPawn = 'P';
+        }
+
+        // en-passant moves
+        if(verticalPosition == enPassantRow) {
+            uint8_t enPassantColumn = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
+            uint8_t column = pieceSquare % 8;
+
+            for(int8_t offset = -1; offset < 2; offset += 2)
+                if((enPassantColumn == column + offset) &&
+                   (board[pieceSquare + offset] == enemyPawn)) {
+                    SCL_squareSetAdd(result, pieceSquare + pawnOffset + offset);
+                    break;
+                }
+        }
+
+#undef checkDiagonal
+    } break;
+
+    case 'r': // rook
+    case 'R':
+    case 'b': // bishop
+    case 'B':
+    case 'q': // queen
+    case 'Q': {
+        const int8_t offsets[8] = {-8, 1, 8, -1, -7, 9, -9, 7};
+        const int8_t columnDirs[8] = {0, 1, 0, -1, 1, 1, -1, -1};
+
+        uint8_t from = (piece == 'b' || piece == 'B') * 4;
+        uint8_t to = 4 + (piece != 'r' && piece != 'R') * 4;
+
+        for(uint8_t i = from; i < to; ++i) {
+            int8_t offset = offsets[i];
+            int8_t columnDir = columnDirs[i];
+            int8_t square = pieceSquare;
+            int8_t col = horizontalPosition;
+
+            while(1) {
+                square += offset;
+                col += columnDir;
+
+                if(square < 0 || square > 63 || col < 0 || col > 7) break;
+
+                char squareC = board[square];
+
+                if(squareC == '.')
+                    SCL_squareSetAdd(result, square);
+                else {
+                    if(SCL_pieceIsWhite(squareC) != isWhite) SCL_squareSetAdd(result, square);
+
+                    break;
+                }
+            }
+        }
+    } break;
+
+    case 'n': // knight
+    case 'N': {
+        const int8_t offsets[4] = {6, 10, 15, 17};
+        const int8_t columnsMinus[4] = {2, -2, 1, -1};
+        const int8_t columnsPlus[4] = {-2, 2, -1, 1};
+        const int8_t *off, *col;
+
+#define checkOffsets(op, comp, limit, dir)                             \
+    off = offsets;                                                     \
+    col = columns##dir;                                                \
+    for(uint8_t i = 0; i < 4; ++i, ++off, ++col) {                     \
+        int8_t square = pieceSquare op(*off);                          \
+        if(square comp limit) /* out of board? */                      \
+            break;                                                     \
+        int8_t horizontalCheck = horizontalPosition + (*col);          \
+        if(horizontalCheck < 0 || horizontalCheck >= 8) continue;      \
+        char squareC = board[square];                                  \
+        if((squareC == '.') || (SCL_pieceIsWhite(squareC) != isWhite)) \
+            SCL_squareSetAdd(result, square);                          \
+    }
+
+        checkOffsets(-, <, 0, Minus) checkOffsets(+, >=, SCL_BOARD_SQUARES, Plus)
+
+#undef checkOffsets
+    } break;
+
+    case 'k': // king
+    case 'K': {
+        uint8_t verticalPosition = pieceSquare / 8;
+
+        uint8_t u = verticalPosition != 0, d = verticalPosition != 7, l = horizontalPosition != 0,
+                r = horizontalPosition != 7;
+
+        uint8_t square2 = pieceSquare - 9;
+
+#define checkSquare(cond, add)                                                             \
+    if(cond && ((board[square2] == '.') || (SCL_pieceIsWhite(board[square2])) != isWhite)) \
+        SCL_squareSetAdd(result, square2);                                                 \
+    square2 += add;
+
+        checkSquare(l && u, 1) checkSquare(u, 1) checkSquare(r && u, 6) checkSquare(l, 2)
+            checkSquare(r, 6) checkSquare(l && d, 1) checkSquare(d, 1) checkSquare(r && d, 0)
+#undef checkSquare
+
+            // castling:
+
+            if(checkCastling) {
+            uint8_t bitShift = 4 + 2 * (!isWhite);
+
+            if((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x03 << bitShift)) &&
+               !SCL_boardSquareAttacked(board, pieceSquare, !isWhite)) // no check?
+            {
+#if !SCL_960_CASTLING
+                // short castle:
+                pieceSquare++;
+
+                if((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x01 << bitShift)) &&
+                   (board[pieceSquare] == '.') && (board[pieceSquare + 1] == '.') &&
+                   (board[pieceSquare + 2] == SCL_pieceToColor('r', isWhite)) &&
+                   !SCL_boardSquareAttacked(board, pieceSquare, !isWhite))
+                    SCL_squareSetAdd(result, pieceSquare + 1);
+
+                /* note: don't check the final square for check, it will potentially
+             be removed later (can't end up in check) */
+
+                // long castle:
+                pieceSquare -= 2;
+
+                if((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x02 << bitShift)) &&
+                   (board[pieceSquare] == '.') && (board[pieceSquare - 1] == '.') &&
+                   (board[pieceSquare - 2] == '.') &&
+                   (board[pieceSquare - 3] == SCL_pieceToColor('r', isWhite)) &&
+                   !SCL_boardSquareAttacked(board, pieceSquare, !isWhite))
+                    SCL_squareSetAdd(result, pieceSquare - 1);
+#else // 960 castling
+                for(int i = 0; i < 2; ++i) // short and long
+                    if(board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & ((i + 1) << bitShift)) {
+                        uint8_t rookPos = board[SCL_BOARD_EXTRA_BYTE] >> 3, targetPos = 5;
+
+                        if(i == 1) {
+                            rookPos = board[SCL_BOARD_EXTRA_BYTE] & 0x07, targetPos = 3;
+                        }
+
+                        if(!isWhite) {
+                            rookPos += 56;
+                            targetPos += 56;
+                        }
+
+                        uint8_t ok = board[rookPos] == SCL_pieceToColor('r', isWhite);
+
+                        if(!ok) continue;
+
+                        int8_t inc = 1 - 2 * (targetPos > rookPos);
+
+                        while(targetPos != rookPos) // check vacant squares for the rook
+                        {
+                            if(board[targetPos] != '.' && targetPos != pieceSquare) {
+                                ok = 0;
+                                break;
+                            }
+
+                            targetPos += inc;
+                        }
+
+                        if(!ok) continue;
+
+                        targetPos = i == 0 ? 6 : 2;
+
+                        if(!isWhite) targetPos += 56;
+
+                        inc = 1 - 2 * (targetPos > pieceSquare);
+
+                        while(targetPos != pieceSquare) // check squares for the king
+                        {
+                            if((board[targetPos] != '.' && targetPos != rookPos) ||
+                               SCL_boardSquareAttacked(board, targetPos, !isWhite)) {
+                                ok = 0;
+                                break;
+                            }
+
+                            targetPos += inc;
+                        }
+
+                        if(ok) SCL_squareSetAdd(result, rookPos);
+                    }
+#endif
+            }
+        }
+    } break;
+
+    default:
+        break;
+    }
+}
+
+void SCL_printSquareSet(SCL_SquareSet set, SCL_PutCharFunction putCharFunc) {
+    uint8_t first = 1;
+
+    putCharFunc('(');
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) {
+        if(!SCL_squareSetContains(set, i)) continue;
+
+        if(!first)
+            putCharFunc(',');
+        else
+            first = 0;
+
+        putCharFunc('A' + i % 8);
+        putCharFunc('1' + i / 8);
+    }
+
+    putCharFunc(')');
+}
+
+void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc) {
+    uint32_t val = 0;
+
+    switch(square) {
+    case 'r':
+        val = 0x9c99e200;
+        break;
+    case 'n':
+        val = 0x9e99e200;
+        break;
+    case 'b':
+        val = 0x9d99e200;
+        break;
+    case 'q':
+        val = 0x9b99e200;
+        break;
+    case 'k':
+        val = 0x9a99e200;
+        break;
+    case 'p':
+        val = 0x9f99e200;
+        break;
+    case 'R':
+        val = 0x9699e200;
+        break;
+    case 'N':
+        val = 0x9899e200;
+        break;
+    case 'B':
+        val = 0x9799e200;
+        break;
+    case 'Q':
+        val = 0x9599e200;
+        break;
+    case 'K':
+        val = 0x9499e200;
+        break;
+    case 'P':
+        val = 0x9999e200;
+        break;
+    case '.':
+        val = 0x9296e200;
+        break;
+    case ',':
+        val = 0x9196e200;
+        break;
+    default:
+        putCharFunc(square);
+        return;
+        break;
+    }
+
+    uint8_t count = 4;
+
+    while((val % 256 == 0) && (count > 0)) {
+        val /= 256;
+        count--;
+    }
+
+    while(count > 0) {
+        putCharFunc(val % 256);
+        val /= 256;
+        count--;
+    }
+}
+
+void SCL_boardGetMoves(SCL_Board board, uint8_t pieceSquare, SCL_SquareSet result) {
+    SCL_SquareSet allMoves;
+
+    SCL_squareSetClear(allMoves);
+
+    for(uint8_t i = 0; i < 8; ++i) result[i] = 0;
+
+    SCL_boardGetPseudoMoves(board, pieceSquare, 1, allMoves);
+
+    // Now only keep moves that don't lead to one's check:
+
+    SCL_SQUARE_SET_ITERATE_BEGIN(allMoves)
+
+    SCL_MoveUndo undo = SCL_boardMakeMove(board, pieceSquare, iteratedSquare, 'q');
+
+    if(!SCL_boardCheck(board, !SCL_boardWhitesTurn(board)))
+        SCL_squareSetAdd(result, iteratedSquare);
+
+    SCL_boardUndoMove(board, undo);
+
+    SCL_SQUARE_SET_ITERATE_END
+}
+
+uint8_t SCL_boardDead(SCL_Board board) {
+    /*
+    This byte represents material by bits:
+
+    MSB _ _ _ _ _ _ _ _ LSB
+          | | |   | | \_ white knight
+          | | |   |  \__ white bishop on white
+          | | |    \____ white bishop on black
+          | |  \________ black knight
+          |  \__________ black bishop on white
+           \____________ black bishop on black
+  */
+    uint8_t material = 0;
+
+    const char* p = board;
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) {
+        char c = *p;
+
+        switch(c) {
+        case 'n':
+            material |= 0x01;
+            break;
+        case 'N':
+            material |= 0x10;
+            break;
+        case 'b':
+            material |= (0x02 << (!SCL_squareIsWhite(i)));
+            break;
+        case 'B':
+            material |= (0x20 << (!SCL_squareIsWhite(i)));
+            break;
+        case 'p':
+        case 'P':
+        case 'r':
+        case 'R':
+        case 'q':
+        case 'Q':
+            return 0; // REMOVE later if more complex check are performed
+            break;
+
+        default:
+            break;
+        }
+
+        p++;
+    }
+
+    // TODO: add other checks than only insufficient material
+
+    // possible combinations of insufficient material:
+
+    return (material == 0x00) || // king vs king
+           (material == 0x01) || // king and knight vs king
+           (material == 0x10) || // king and knight vs king
+           (material == 0x02) || // king and bishop vs king
+           (material == 0x20) || // king and bishop vs king
+           (material == 0x04) || // king and bishop vs king
+           (material == 0x40) || // king and bishop vs king
+           (material == 0x22) || // king and bishop vs king and bishop (same color)
+           (material == 0x44); // king and bishop vs king and bishop (same color)
+}
+
+uint8_t SCL_boardGetPosition(SCL_Board board) {
+    uint8_t check = SCL_boardCheck(board, SCL_boardWhitesTurn(board));
+    uint8_t moves = SCL_boardMovePossible(board);
+
+    if(check)
+        return moves ? SCL_POSITION_CHECK : SCL_POSITION_MATE;
+    else if(!moves)
+        return SCL_POSITION_STALEMATE;
+
+    if(SCL_boardDead(board)) return SCL_POSITION_DEAD;
+
+    return SCL_POSITION_NORMAL;
+}
+
+uint8_t SCL_stringToMove(
+    const char* moveString,
+    uint8_t* resultFrom,
+    uint8_t* resultTo,
+    char* resultPromotion) {
+    char c;
+
+    uint8_t* dst = resultFrom;
+
+    for(uint8_t i = 0; i < 2; ++i) {
+        c = *moveString;
+
+        *dst = (c >= 'a') ? (c - 'a') : (c - 'A');
+
+        if(*dst > 7) return 0;
+
+        moveString++;
+        c = *moveString;
+
+        *dst += 8 * (c - '1');
+
+        if(*dst > 63) return 0;
+
+        moveString++;
+
+        dst = resultTo;
+    }
+
+    c = *moveString;
+
+    if(c < 'A') c = c - 'A' + 'a';
+
+    switch(c) {
+    case 'N':
+    case 'n':
+        *resultPromotion = 'n';
+        break;
+    case 'B':
+    case 'b':
+        *resultPromotion = 'b';
+        break;
+    case 'R':
+    case 'r':
+        *resultPromotion = 'r';
+        break;
+    case 'Q':
+    case 'q':
+    default:
+        *resultPromotion = 'q';
+        break;
+    }
+
+    return 1;
+}
+
+void SCL_printBoard(
+    SCL_Board board,
+    SCL_PutCharFunction putCharFunc,
+    SCL_SquareSet highlightSquares,
+    uint8_t selectSquare,
+    uint8_t format,
+    uint8_t offset,
+    uint8_t labels,
+    uint8_t blackDown) {
+    if(labels) {
+        for(uint8_t i = 0; i < offset + 2; ++i) putCharFunc(' ');
+
+        for(uint8_t i = 0; i < 8; ++i) {
+            if((format != SCL_PRINT_FORMAT_COMPACT) && (format != SCL_PRINT_FORMAT_COMPACT_UTF8))
+                putCharFunc(' ');
+
+            putCharFunc(blackDown ? ('H' - i) : ('A' + i));
+        }
+
+        putCharFunc('\n');
+    }
+
+    int8_t i = 7;
+    int8_t add = 1;
+
+    if(!blackDown) {
+        i = 56;
+        add = -1;
+    }
+
+    for(int8_t row = 0; row < 8; ++row) {
+        for(uint8_t j = 0; j < offset; ++j) putCharFunc(' ');
+
+        if(labels) {
+            putCharFunc(!blackDown ? ('8' - row) : ('1' + row));
+            putCharFunc(' ');
+        }
+
+        const char* square = board + i;
+
+        for(int8_t col = 0; col < 8; ++col) {
+            switch(format) {
+            case SCL_PRINT_FORMAT_COMPACT:
+                putCharFunc(
+                    (*square == '.') ?
+                        (((i != selectSquare) ?
+                              (!SCL_squareSetContains(highlightSquares, i) ? *square : '*') :
+                              '#')) :
+                        *square);
+                break;
+
+            case SCL_PRINT_FORMAT_UTF8: {
+                char squareChar = SCL_squareIsWhite(i) ? '.' : ',';
+                char pieceChar = (*square == '.') ? squareChar : *square;
+
+                if(i == selectSquare) {
+                    putCharFunc('(');
+
+                    if(*square == '.')
+                        putCharFunc(')');
+                    else
+                        SCL_printSquareUTF8(pieceChar, putCharFunc);
+                } else if(!SCL_squareSetContains(highlightSquares, i)) {
+                    SCL_printSquareUTF8(squareChar, putCharFunc);
+                    SCL_printSquareUTF8(pieceChar, putCharFunc);
+                } else {
+                    putCharFunc('[');
+
+                    if(*square == '.')
+                        putCharFunc(']');
+                    else
+                        SCL_printSquareUTF8(*square, putCharFunc);
+                }
+
+                break;
+            }
+
+            case SCL_PRINT_FORMAT_COMPACT_UTF8:
+                SCL_printSquareUTF8(
+                    (*square == '.') ?
+                        (SCL_squareSetContains(highlightSquares, i) ?
+                             '*' :
+                             (i == selectSquare ? '#' : ((SCL_squareIsWhite(i) ? '.' : ',')))) :
+                        *square,
+                    putCharFunc);
+                break;
+
+            case SCL_PRINT_FORMAT_NORMAL:
+            default: {
+                uint8_t c = *square;
+
+                char squareColor = SCL_squareIsWhite(i) ? ' ' : ':';
+
+                putCharFunc(
+                    (i != selectSquare) ?
+                        (!SCL_squareSetContains(highlightSquares, i) ? squareColor : '#') :
+                        '@');
+
+                putCharFunc(c == '.' ? squareColor : *square);
+                break;
+            }
+            }
+
+            i -= add;
+            square -= add;
+        }
+
+        putCharFunc('\n');
+
+        i += add * 16;
+    } // for rows
+}
+
+int16_t SCL_pieceValuePositive(char piece) {
+    switch(piece) {
+    case 'p':
+    case 'P':
+        return SCL_VALUE_PAWN;
+        break;
+    case 'n':
+    case 'N':
+        return SCL_VALUE_KNIGHT;
+        break;
+    case 'b':
+    case 'B':
+        return SCL_VALUE_BISHOP;
+        break;
+    case 'r':
+    case 'R':
+        return SCL_VALUE_ROOK;
+        break;
+    case 'q':
+    case 'Q':
+        return SCL_VALUE_QUEEN;
+        break;
+    case 'k':
+    case 'K':
+        return SCL_VALUE_KING;
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+int16_t SCL_pieceValue(char piece) {
+    switch(piece) {
+    case 'P':
+        return SCL_VALUE_PAWN;
+        break;
+    case 'N':
+        return SCL_VALUE_KNIGHT;
+        break;
+    case 'B':
+        return SCL_VALUE_BISHOP;
+        break;
+    case 'R':
+        return SCL_VALUE_ROOK;
+        break;
+    case 'Q':
+        return SCL_VALUE_QUEEN;
+        break;
+    case 'K':
+        return SCL_VALUE_KING;
+        break;
+    case 'p':
+        return -1 * SCL_VALUE_PAWN;
+        break;
+    case 'n':
+        return -1 * SCL_VALUE_KNIGHT;
+        break;
+    case 'b':
+        return -1 * SCL_VALUE_BISHOP;
+        break;
+    case 'r':
+        return -1 * SCL_VALUE_ROOK;
+        break;
+    case 'q':
+        return -1 * SCL_VALUE_QUEEN;
+        break;
+    case 'k':
+        return -1 * SCL_VALUE_KING;
+        break;
+    default:
+        break;
+    }
+
+    return 0;
+}
+
+#define ATTACK_BONUS 3
+#define MOBILITY_BONUS 10
+#define CENTER_BONUS 7
+#define CHECK_BONUS 5
+#define KING_CASTLED_BONUS 30
+#define KING_BACK_BONUS 15
+#define KING_NOT_CENTER_BONUS 15
+#define PAWN_NON_DOUBLE_BONUS 3
+#define PAWN_PAIR_BONUS 3
+#define KING_CENTERNESS 10
+
+int16_t _SCL_rateKingEndgamePosition(uint8_t position) {
+    int16_t result = 0;
+    uint8_t rank = position / 8;
+    position %= 8;
+
+    if(position > 1 && position < 6) result += KING_CENTERNESS;
+
+    if(rank > 1 && rank < 6) result += KING_CENTERNESS;
+
+    return result;
+}
+
+int16_t SCL_boardEvaluateStatic(SCL_Board board) {
+    uint8_t position = SCL_boardGetPosition(board);
+
+    int16_t total = 0;
+
+    switch(position) {
+    case SCL_POSITION_MATE:
+        return SCL_boardWhitesTurn(board) ? -1 * SCL_EVALUATION_MAX_SCORE :
+                                            SCL_EVALUATION_MAX_SCORE;
+        break;
+
+    case SCL_POSITION_STALEMATE:
+    case SCL_POSITION_DEAD:
+        return 0;
+        break;
+
+        /*
+      main points are assigned as follows:
+      - points for material as a sum of all material on board
+      - for playing side: if a piece attacks piece of greater value, a fraction
+        of the value difference is gained (we suppose exchange), this is only
+        gained once per every attacking piece (maximum gain is taken), we only
+        take fraction so that actually taking the piece is favored
+      - ATTACK_BONUS points for any attacked piece
+
+      other points are assigned as follows (in total these shouldn't be more
+      than the value of one pawn)
+      - mobility: MOBILITY_BONUS points for each piece with at least 4 possible
+        moves
+      - center control: CENTER_BONUS points for a piece on a center square
+      - CHECK_BONUS points for check
+      - king:
+        - safety (non endgame): KING_BACK_BONUS points for king on staring rank,
+          additional KING_CASTLED_BONUS if the kind if on castled square or
+          closer to the edge, additional KING_NOT_CENTER_BONUS for king not on
+          its start neighbouring center square
+        - center closeness (endgame): up to 2 * KING_CENTERNESS points for
+          being closer to center
+      - non-doubled pawns: PAWN_NON_DOUBLE_BONUS points for each pawn without
+        same color pawn directly in front of it 
+      - pawn structure: PAWN_PAIR_BONUS points for each pawn guarding own pawn
+      - advancing pawns: 1 point for each pawn's rank in its move
+        direction
+    */
+
+    case SCL_POSITION_CHECK:
+        total += SCL_boardWhitesTurn(board) ? -1 * CHECK_BONUS : CHECK_BONUS;
+        /* FALLTHROUGH */
+    case SCL_POSITION_NORMAL:
+    default: {
+        SCL_SquareSet moves;
+
+        const char* p = board;
+
+        int16_t positiveMaterial = 0;
+        uint8_t endgame = 0;
+
+        // first count material to see if this is endgame or not
+        for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p) {
+            char s = *p;
+
+            if(s != '.') {
+                positiveMaterial += SCL_pieceValuePositive(s);
+                total += SCL_pieceValue(s);
+            }
+        }
+
+        endgame = positiveMaterial <= SCL_ENDGAME_MATERIAL_LIMIT;
+
+        p = board;
+
+        for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p) {
+            char s = *p;
+
+            if(s != '.') {
+                uint8_t white = SCL_pieceIsWhite(s);
+
+                switch(s) {
+                case 'k': // king safety
+                    if(endgame)
+                        total -= _SCL_rateKingEndgamePosition(i);
+                    else if(i >= 56) {
+                        total -= KING_BACK_BONUS;
+
+                        if(i != 59) {
+                            total -= KING_NOT_CENTER_BONUS;
+
+                            if(i >= 62 || i <= 58) total -= KING_CASTLED_BONUS;
+                        }
+                    }
+                    break;
+
+                case 'K':
+                    if(endgame)
+                        total += _SCL_rateKingEndgamePosition(i);
+                    else if(i <= 7) {
+                        total += KING_BACK_BONUS;
+
+                        if(i != 3) {
+                            total += KING_NOT_CENTER_BONUS;
+
+                            if(i <= 2 || i >= 6) total += KING_CASTLED_BONUS;
+                        }
+                    }
+                    break;
+
+                case 'P': // pawns
+                case 'p': {
+                    int8_t rank = i / 8;
+
+                    if(rank != 0 && rank != 7) {
+                        if(s == 'P') {
+                            total += rank;
+
+                            char* tmp = board + i + 8;
+
+                            if(*tmp != 'P') total += PAWN_NON_DOUBLE_BONUS;
+
+                            if(i % 8 != 7) {
+                                tmp++;
+
+                                if(*tmp == 'P') total += PAWN_PAIR_BONUS;
+
+                                if(*(tmp - 16) == 'P') total += PAWN_PAIR_BONUS;
+                            }
+                        } else {
+                            total -= 7 - rank;
+
+                            char* tmp = board + i - 8;
+
+                            if(*tmp != 'p') total -= PAWN_NON_DOUBLE_BONUS;
+
+                            if(i % 8 != 7) {
+                                tmp += 17;
+
+                                if(*tmp == 'p') total -= PAWN_PAIR_BONUS;
+
+                                if(*(tmp - 16) == 'p') total -= PAWN_PAIR_BONUS;
+                            }
+                        }
+                    }
+
+                    break;
+                }
+
+                default:
+                    break;
+                }
+
+                if(i >= 27 && i <= 36 && (i >= 35 || i <= 28)) // center control
+                    total += white ? CENTER_BONUS : (-1 * CENTER_BONUS);
+
+                // for performance we only take pseudo moves
+                SCL_boardGetPseudoMoves(board, i, 0, moves);
+
+                if(SCL_squareSetSize(moves) >= 4) // mobility
+                    total += white ? MOBILITY_BONUS : (-1 * MOBILITY_BONUS);
+
+                int16_t exchangeBonus = 0;
+
+                SCL_SQUARE_SET_ITERATE_BEGIN(moves)
+
+                if(board[iteratedSquare] != '.') {
+                    total += white ? ATTACK_BONUS : (-1 * ATTACK_BONUS);
+
+                    if(SCL_boardWhitesTurn(board) == white) {
+                        int16_t valueDiff = SCL_pieceValuePositive(board[iteratedSquare]) -
+                                            SCL_pieceValuePositive(s);
+
+                        valueDiff /= 4; // only take a fraction to favor taking
+
+                        if(valueDiff > exchangeBonus) exchangeBonus = valueDiff;
+                    }
+                }
+
+                SCL_SQUARE_SET_ITERATE_END
+
+                if(exchangeBonus != 0) total += white ? exchangeBonus : -1 * exchangeBonus;
+            }
+        } // for each square
+
+        return total;
+
+        break;
+
+    } // normal position
+    } // switch
+
+    return 0;
+}
+
+#undef ATTACK_BONUS
+#undef MOBILITY_BONUS
+#undef CENTER_BONUS
+#undef CHECK_BONUS
+#undef KING_CASTLED_BONUS
+#undef KING_BACK_BONUS
+#undef PAWN_NON_DOUBLE_BONUS
+#undef PAWN_PAIR_BONUS
+#undef KING_CENTERNESS
+
+SCL_StaticEvaluationFunction _SCL_staticEvaluationFunction;
+int16_t _SCL_currentEval;
+int8_t _SCL_depthHardLimit;
+
+/**
+  Inner recursive function for SCL_boardEvaluateDynamic. It is passed a square
+  (or -1) at which last capture happened, to implement capture extension.
+*/
+int16_t _SCL_boardEvaluateDynamic(
+    SCL_Board board,
+    int8_t depth,
+    int16_t alphaBeta,
+    int8_t takenSquare) {
+#if SCL_COUNT_EVALUATED_POSITIONS
+    SCL_positionsEvaluated++;
+#endif
+
+#if SCL_CALL_WDT_RESET
+    wdt_reset();
+#endif
+
+    uint8_t whitesTurn = SCL_boardWhitesTurn(board);
+    int8_t valueMultiply = whitesTurn ? 1 : -1;
+    int16_t bestMoveValue = -1 * SCL_EVALUATION_MAX_SCORE;
+    uint8_t shouldCompute = depth > 0;
+    uint8_t extended = 0;
+    uint8_t positionType = SCL_boardGetPosition(board);
+
+    if(!shouldCompute) {
+        /* here we do two extensions (deeper search): taking on a same square 
+      (exchanges) and checks (good for mating and preventing mates): */
+        extended = (depth > _SCL_depthHardLimit) &&
+                   (takenSquare >= 0 || (SCL_boardGetPosition(board) == SCL_POSITION_CHECK));
+
+        shouldCompute = extended;
+    }
+
+#if SCL_DEBUG_AI
+    char moveStr[8];
+    uint8_t debugFirst = 1;
+#endif
+
+    if(shouldCompute &&
+       (positionType == SCL_POSITION_NORMAL || positionType == SCL_POSITION_CHECK)) {
+#if SCL_DEBUG_AI
+        putchar('(');
+#endif
+
+        alphaBeta *= valueMultiply;
+        uint8_t end = 0;
+        const char* b = board;
+
+        depth--;
+
+        for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b) {
+            char s = *b;
+
+            if(s != '.' && SCL_pieceIsWhite(s) == whitesTurn) {
+                SCL_SquareSet moves;
+
+                SCL_squareSetClear(moves);
+
+                SCL_boardGetMoves(board, i, moves);
+
+                if(!SCL_squareSetEmpty(moves)) {
+                    SCL_SQUARE_SET_ITERATE_BEGIN(moves)
+
+                    int8_t captureExtension = -1;
+
+                    if(board[iteratedSquare] != '.' && // takes a piece
+                       (takenSquare == -1 || // extend on first taken sq.
+                        (extended && takenSquare != -1) || // ignore check extension
+                        (iteratedSquare == takenSquare))) // extend on same sq. taken
+                        captureExtension = iteratedSquare;
+
+                    SCL_MoveUndo undo = SCL_boardMakeMove(board, i, iteratedSquare, 'q');
+
+                    uint8_t s0Dummy, s1Dummy;
+                    char pDummy;
+
+                    SCL_UNUSED(s0Dummy);
+                    SCL_UNUSED(s1Dummy);
+                    SCL_UNUSED(pDummy);
+
+#if SCL_DEBUG_AI
+                    if(debugFirst)
+                        debugFirst = 0;
+                    else
+                        putchar(',');
+
+                    if(extended) putchar('*');
+
+                    printf("%s ", SCL_moveToString(board, i, iteratedSquare, 'q', moveStr));
+#endif
+
+                    int16_t value = _SCL_boardEvaluateDynamic(
+                                        board,
+                                        depth, // this is depth - 1, we decremented it
+#if SCL_ALPHA_BETA
+                                        valueMultiply * bestMoveValue,
+#else
+                                        0,
+#endif
+                                        captureExtension) *
+                                    valueMultiply;
+
+                    SCL_boardUndoMove(board, undo);
+
+                    if(value > bestMoveValue) {
+                        bestMoveValue = value;
+
+#if SCL_ALPHA_BETA
+                        // alpha-beta pruning:
+
+                        if(value > alphaBeta) // no, >= can't be here
+                        {
+                            end = 1;
+                            iterationEnd = 1;
+                        }
+#endif
+                    }
+
+                    SCL_SQUARE_SET_ITERATE_END
+                } // !squre set empty?
+            } // valid piece?
+
+            if(end) break;
+
+        } // for each square
+
+#if SCL_DEBUG_AI
+        putchar(')');
+#endif
+    } else // don't dive recursively, evaluate statically
+    {
+        bestMoveValue = valueMultiply *
+#ifndef SCL_EVALUATION_FUNCTION
+                        _SCL_staticEvaluationFunction(board);
+#else
+                        SCL_EVALUATION_FUNCTION(board);
+#endif
+
+        /* For stalemate return the opposite value of the board, i.e. if the
+       position is good for white, then stalemate is good for black and vice
+       versa. */
+        if(positionType == SCL_POSITION_STALEMATE) bestMoveValue *= -1;
+    }
+
+    /* Here we either improve (if the move worsens the situation) or devalve (if
+     it improves the situation) the result: this needs to be done so that good
+     moves far away are seen as worse compared to equally good moves achieved
+     in fewer moves. Without this an AI in winning situation may just repeat
+     random moves and draw by repetition even if it has mate in 1 (it sees all
+     moves as leading to mate). */
+    bestMoveValue += bestMoveValue > _SCL_currentEval * valueMultiply ? -1 : 1;
+
+#if SCL_DEBUG_AI
+    printf("%d", bestMoveValue * valueMultiply);
+#endif
+
+    return bestMoveValue * valueMultiply;
+}
+
+int16_t SCL_boardEvaluateDynamic(
+    SCL_Board board,
+    uint8_t baseDepth,
+    uint8_t extensionExtraDepth,
+    SCL_StaticEvaluationFunction evalFunction) {
+    _SCL_staticEvaluationFunction = evalFunction;
+    _SCL_currentEval = evalFunction(board);
+    _SCL_depthHardLimit = 0;
+    _SCL_depthHardLimit -= extensionExtraDepth;
+
+    return _SCL_boardEvaluateDynamic(
+        board,
+        baseDepth,
+        SCL_boardWhitesTurn(board) ? SCL_EVALUATION_MAX_SCORE : (-1 * SCL_EVALUATION_MAX_SCORE),
+        -1);
+}
+
+void SCL_boardRandomMove(
+    SCL_Board board,
+    SCL_RandomFunction randFunc,
+    uint8_t* squareFrom,
+    uint8_t* squareTo,
+    char* resultProm) {
+    *resultProm = (randFunc() < 128) ? ((randFunc() < 128) ? 'r' : 'n') :
+                                       ((randFunc() < 128) ? 'b' : 'q');
+
+    SCL_SquareSet set;
+    uint8_t white = SCL_boardWhitesTurn(board);
+    const char* s = board;
+
+    SCL_squareSetClear(set);
+
+    // find squares with pieces that have legal moves
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++s) {
+        char c = *s;
+
+        if(c != '.' && SCL_pieceIsWhite(c) == white) {
+            SCL_SquareSet moves;
+
+            SCL_boardGetMoves(board, i, moves);
+
+            if(SCL_squareSetSize(moves) != 0) SCL_squareSetAdd(set, i);
+        }
+    }
+
+    *squareFrom = SCL_squareSetGetRandom(set, randFunc);
+
+    SCL_boardGetMoves(board, *squareFrom, set);
+
+    *squareTo = SCL_squareSetGetRandom(set, randFunc);
+}
+
+void SCL_printBoardSimple(
+    SCL_Board board,
+    SCL_PutCharFunction putCharFunc,
+    uint8_t selectSquare,
+    uint8_t format) {
+    SCL_SquareSet s;
+
+    SCL_squareSetClear(s);
+
+    SCL_printBoard(board, putCharFunc, s, selectSquare, format, 1, 1, 0);
+}
+
+int16_t SCL_getAIMove(
+    SCL_Board board,
+    uint8_t baseDepth,
+    uint8_t extensionExtraDepth,
+    uint8_t endgameExtraDepth,
+    SCL_StaticEvaluationFunction evalFunc,
+    SCL_RandomFunction randFunc,
+    uint8_t randomness,
+    uint8_t repetitionMoveFrom,
+    uint8_t repetitionMoveTo,
+    uint8_t* resultFrom,
+    uint8_t* resultTo,
+    char* resultProm) {
+#if SCL_DEBUG_AI
+    puts("===== AI debug =====");
+    putchar('(');
+    unsigned char debugFirst = 1;
+    char moveStr[8];
+#endif
+
+    if(baseDepth == 0) {
+        SCL_boardRandomMove(board, randFunc, resultFrom, resultTo, resultProm);
+#ifndef SCL_EVALUATION_FUNCTION
+        return evalFunc(board);
+#else
+        return SCL_EVALUATION_FUNCTION(board);
+#endif
+    }
+
+    if(SCL_boardEstimatePhase(board) == SCL_PHASE_ENDGAME) baseDepth += endgameExtraDepth;
+
+    *resultFrom = 0;
+    *resultTo = 0;
+    *resultProm = 'q';
+
+    int16_t bestScore = SCL_boardWhitesTurn(board) ? -1 * SCL_EVALUATION_MAX_SCORE - 1 :
+                                                     (SCL_EVALUATION_MAX_SCORE + 1);
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
+        if(board[i] != '.' && SCL_boardWhitesTurn(board) == SCL_pieceIsWhite(board[i])) {
+            SCL_SquareSet moves;
+
+            SCL_squareSetClear(moves);
+
+            SCL_boardGetMoves(board, i, moves);
+
+            SCL_SQUARE_SET_ITERATE_BEGIN(moves)
+
+            int16_t score = 0;
+
+#if SCL_DEBUG_AI
+            if(debugFirst)
+                debugFirst = 0;
+            else
+                putchar(',');
+
+            printf("%s ", SCL_moveToString(board, i, iteratedSquare, 'q', moveStr));
+
+#endif
+
+            if(i != repetitionMoveFrom || iteratedSquare != repetitionMoveTo) {
+                SCL_MoveUndo undo = SCL_boardMakeMove(board, i, iteratedSquare, 'q');
+
+                score =
+                    SCL_boardEvaluateDynamic(board, baseDepth - 1, extensionExtraDepth, evalFunc);
+
+                SCL_boardUndoMove(board, undo);
+            }
+
+            if(randFunc != 0 && randomness > 1 && score < 16000 && score > -16000) {
+                /*^ We limit randomizing by about half the max score for two reasons:
+            to prevent over/under flows and secondly we don't want to alter
+            the highest values for checkmate -- these are modified by tiny
+            values depending on their depth so as to prevent endless loops in
+            which most moves are winning, biasing such values would completely
+            kill that algorithm */
+
+                int16_t bias = randFunc();
+                bias = (bias - 128) / 2;
+                bias *= randomness - 1;
+                score += bias;
+            }
+
+            uint8_t comparison = score == bestScore;
+
+            if((comparison != 1) && ((SCL_boardWhitesTurn(board) && score > bestScore) ||
+                                     (!SCL_boardWhitesTurn(board) && score < bestScore)))
+                comparison = 2;
+
+            uint8_t replace = 0;
+
+            if(randFunc == 0)
+                replace = comparison == 2;
+            else
+                replace =
+                    (comparison == 2) ||
+                    ((comparison == 1) && (randFunc() < 160)); // not uniform distr. but simple
+
+            if(replace) {
+                *resultFrom = i;
+                *resultTo = iteratedSquare;
+                bestScore = score;
+            }
+
+            SCL_SQUARE_SET_ITERATE_END
+        }
+
+#if SCL_DEBUG_AI
+    printf(")%d %s\n", bestScore, SCL_moveToString(board, *resultFrom, *resultTo, 'q', moveStr));
+    puts("===== AI debug end ===== ");
+#endif
+
+    return bestScore;
+}
+
+uint8_t SCL_boardToFEN(SCL_Board board, char* string) {
+    uint8_t square = 56;
+    uint8_t spaces = 0;
+    uint8_t result = 0;
+
+#define put(c)         \
+    {                  \
+        *string = (c); \
+        string++;      \
+        result++;      \
+    }
+
+    while(1) // pieces
+    {
+        char s = board[square];
+
+        if(s == '.') {
+            spaces++;
+        } else {
+            if(spaces != 0) {
+                put('0' + spaces) spaces = 0;
+            }
+
+            put(s)
+        }
+
+        square++;
+
+        if(square % 8 == 0) {
+            if(spaces != 0) {
+                put('0' + spaces) spaces = 0;
+            }
+
+            if(square == 8) break;
+
+            put('/');
+
+            square -= 16;
+        }
+    }
+
+    put(' ');
+    put(SCL_boardWhitesTurn(board) ? 'w' : 'b');
+    put(' ');
+
+    uint8_t b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0;
+
+    if(b != 0) // castling
+    {
+        if(b & 0x10) put('K');
+        if(b & 0x20) put('Q');
+        if(b & 0x40) put('k');
+        if(b & 0x80) put('q');
+    } else
+        put('-');
+
+    put(' ');
+
+    b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
+
+    if(b < 8) {
+        put('a' + b);
+        put(SCL_boardWhitesTurn(board) ? '6' : '3');
+    } else
+        put('-');
+
+    for(uint8_t i = 0; i < 2; ++i) {
+        put(' ');
+
+        uint8_t moves = i == 0 ? ((uint8_t)board[SCL_BOARD_MOVE_COUNT_BYTE]) :
+                                 (((uint8_t)board[SCL_BOARD_PLY_BYTE]) / 2 + 1);
+
+        uint8_t hundreds = moves / 100;
+        uint8_t tens = (moves % 100) / 10;
+
+        if(hundreds != 0) {
+            put('0' + hundreds);
+            put('0' + tens);
+        } else if(tens != 0)
+            put('0' + tens);
+
+        put('0' + moves % 10);
+    }
+
+    *string = 0; // terminate the string
+
+    return result + 1;
+
+#undef put
+}
+
+uint8_t SCL_boardFromFEN(SCL_Board board, const char* string) {
+    uint8_t square = 56;
+
+    while(1) {
+        char c = *string;
+
+        if(c == 0) return 0;
+
+        if(c != '/' && c != ' ') // ignore line separators
+        {
+            if(c < '9') // empty square sequence
+            {
+                while(c > '0') {
+                    board[square] = '.';
+                    square++;
+                    c--;
+                }
+            } else // piece
+            {
+                board[square] = c;
+                square++;
+            }
+        } else {
+            if(square == 8) break;
+
+            square -= 16;
+        }
+
+        string++;
+    }
+
+#define nextChar \
+    string++;    \
+    if(*string == 0) return 0;
+
+    nextChar // space
+
+        board[SCL_BOARD_PLY_BYTE] = *string == 'b';
+    nextChar
+
+        nextChar // space
+
+            uint8_t castleEnPassant = 0x0;
+
+    while(*string != ' ') {
+        switch(*string) {
+        case 'K':
+            castleEnPassant |= 0x10;
+            break;
+        case 'Q':
+            castleEnPassant |= 0x20;
+            break;
+        case 'k':
+            castleEnPassant |= 0x40;
+            break;
+        case 'q':
+            castleEnPassant |= 0x80;
+            break;
+        default:
+            castleEnPassant |= 0xf0;
+            break; // for partial XFEN compat.
+        }
+
+        nextChar
+    }
+
+    nextChar // space
+
+        if(*string != '-') {
+        castleEnPassant |= *string - 'a';
+        nextChar
+    }
+    else castleEnPassant |= 0x0f;
+
+    nextChar
+
+        board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castleEnPassant;
+
+    for(uint8_t i = 0; i < 2; ++i) {
+        nextChar // space
+
+            uint8_t ply = 0;
+
+        while(1) {
+            char c = *string;
+
+            if(c < '0' || c > '9') break;
+
+            ply = ply * 10 + (c - '0');
+
+            string++;
+        }
+
+        if(i == 0 && *string == 0) return 0;
+
+        if(i == 0)
+            board[SCL_BOARD_MOVE_COUNT_BYTE] = ply;
+        else
+            board[SCL_BOARD_PLY_BYTE] += (ply - 1) * 2;
+    }
+
+#if SCL_960_CASTLING
+    _SCL_board960RememberRookPositions(board);
+#endif
+
+    return 1;
+#undef nextChar
+}
+
+uint8_t SCL_boardEstimatePhase(SCL_Board board) {
+    uint16_t totalMaterial = 0;
+
+    uint8_t ply = board[SCL_BOARD_PLY_BYTE];
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i) {
+        char s = *board;
+
+        if(s != '.') {
+            int16_t v = SCL_pieceValue(s);
+
+            if(!SCL_pieceIsWhite(s)) v *= -1;
+
+            totalMaterial += v;
+        }
+
+        board++;
+    }
+
+    if(totalMaterial < SCL_ENDGAME_MATERIAL_LIMIT) return SCL_PHASE_ENDGAME;
+
+    if(ply <= 10 && (totalMaterial >= SCL_START_MATERIAL - 3 * SCL_VALUE_PAWN))
+        return SCL_PHASE_OPENING;
+
+    return SCL_PHASE_MIDGAME;
+}
+
+#define SCL_IMAGE_COUNT 12
+
+static const uint8_t SCL_images[8 * SCL_IMAGE_COUNT] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0xe7, 0xf7,
+    0xf7, 0xaa, 0xff, 0xbd, 0xe7, 0xf7, 0xf7, 0xaa, 0xff, 0xc3, 0xc3, 0xe3, 0xc1, 0x80,
+    0xff, 0x99, 0xdb, 0xeb, 0xc9, 0x94, 0xe7, 0xc3, 0x81, 0xc1, 0x94, 0x80, 0xe7, 0xdb,
+    0xbd, 0xdd, 0xbe, 0xbe, 0xc3, 0xc3, 0x91, 0xe3, 0x80, 0x80, 0xdb, 0x99, 0x8d, 0xeb,
+    0xaa, 0xbe, 0xc3, 0x81, 0xe1, 0xc1, 0xc1, 0xc1, 0xdb, 0xbd, 0xdd, 0xe3, 0xdd, 0xdd,
+    0x81, 0x81, 0xc1, 0x9c, 0xc1, 0xc1, 0x81, 0x81, 0xc1, 0x9c, 0xc1, 0xc1};
+
+void SCL_drawBoard(
+    SCL_Board board,
+    SCL_PutPixelFunction putPixel,
+    uint8_t selectedSquare,
+    SCL_SquareSet highlightSquares,
+    uint8_t blackDown) {
+    uint8_t row = 0;
+    uint8_t col = 0;
+    uint8_t x = 0;
+    uint8_t y = 0;
+    uint16_t n = 0;
+    uint8_t s = 0;
+
+    uint8_t pictureLine = 0;
+    uint8_t loadLine = 1;
+
+    while(row < 8) {
+        if(loadLine) {
+            s = blackDown ? (row * 8 + (7 - col)) : ((7 - row) * 8 + col);
+
+            char piece = board[s];
+
+            if(piece == '.')
+                pictureLine = (y == 4) ? 0xef : 0xff;
+            else {
+                uint8_t offset = SCL_pieceIsWhite(piece) ? 6 : 0;
+                piece = SCL_pieceToColor(piece, 1);
+
+                switch(piece) {
+                case 'R':
+                    offset += 1;
+                    break;
+                case 'N':
+                    offset += 2;
+                    break;
+                case 'B':
+                    offset += 3;
+                    break;
+                case 'K':
+                    offset += 4;
+                    break;
+                case 'Q':
+                    offset += 5;
+                    break;
+                default:
+                    break;
+                }
+
+                pictureLine = SCL_images[y * SCL_IMAGE_COUNT + offset];
+            }
+
+            if(SCL_squareSetContains(highlightSquares, s)) pictureLine &= (y % 2) ? 0xaa : 0x55;
+
+            if(s == selectedSquare) pictureLine &= (y == 0 || y == 7) ? 0x00 : ~0x81;
+
+            loadLine = 0;
+        }
+
+        putPixel(pictureLine & 0x80, n);
+        pictureLine <<= 1;
+
+        n++;
+        x++;
+
+        if(x == 8) {
+            col++;
+            loadLine = 1;
+            x = 0;
+        }
+
+        if(col == 8) {
+            y++;
+            col = 0;
+            x = 0;
+        }
+
+        if(y == 8) {
+            row++;
+            y = 0;
+        }
+    }
+}
+
+uint32_t SCL_boardHash32(const SCL_Board board) {
+    uint32_t result = (board[SCL_BOARD_PLY_BYTE] & 0x01) +
+                      (((uint32_t)((uint8_t)board[SCL_BOARD_ENPASSANT_CASTLE_BYTE])) << 24) +
+                      board[SCL_BOARD_MOVE_COUNT_BYTE];
+
+    const char* b = board;
+
+    for(uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b) {
+        switch(*b) {
+#define C(p, n)                \
+    case p:                    \
+        result ^= (i + 1) * n; \
+        break;
+            // the below number are primes
+            C('P', 4003)
+            C('R', 84673)
+            C('N', 93911)
+            C('B', 999331)
+            C('Q', 909091)
+            C('K', 2796203)
+            C('p', 4793)
+            C('r', 19391)
+            C('n', 391939)
+            C('b', 108301)
+            C('q', 174763)
+            C('k', 2474431)
+#undef C
+        default:
+            break;
+        }
+    }
+
+    // for extra spread of values we swap the low/high parts:
+    result = (result >> 16) | (result << 16);
+
+    return result;
+}
+
+void SCL_boardDisableCastling(SCL_Board board) {
+    board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= 0x0f;
+}
+
+uint8_t SCL_boardMoveResetsCount(SCL_Board board, uint8_t squareFrom, uint8_t squareTo) {
+    return board[squareFrom] == 'P' || board[squareFrom] == 'p' || board[squareTo] != '.';
+}
+
+void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc, SCL_Board initialState) {
+    if(SCL_recordLength(r) == 0) return;
+
+    uint16_t pos = 0;
+
+    SCL_Board board;
+
+    if(initialState != 0)
+        for(uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i) board[i] = initialState[i];
+    else
+        SCL_boardInit(board);
+
+    while(1) {
+        uint8_t s0, s1;
+        char p;
+
+        uint8_t state = SCL_recordGetMove(r, pos, &s0, &s1, &p);
+
+        pos++;
+
+        if(pos % 2) {
+            uint8_t move = pos / 2 + 1;
+
+            if(move / 100 != 0) putCharFunc('0' + move / 100);
+
+            if(move / 10 != 0 || move / 100 != 0) putCharFunc('0' + (move % 100) / 10);
+
+            putCharFunc('0' + move % 10);
+
+            putCharFunc('.');
+            putCharFunc(' ');
+        }
+
+#if !SCL_960_CASTLING
+        if((board[s0] == 'K' && s0 == 4 && (s1 == 2 || s1 == 6)) ||
+           (board[s0] == 'k' && s0 == 60 && (s1 == 62 || s1 == 58)))
+#else
+        if((board[s0] == 'K' && board[s1] == 'R') || (board[s0] == 'k' && board[s1] == 'r'))
+#endif
+        {
+            putCharFunc('O');
+            putCharFunc('-');
+            putCharFunc('O');
+
+#if !SCL_960_CASTLING
+            if(s1 == 58 || s1 == 2)
+#else
+            if((s1 == (board[SCL_BOARD_EXTRA_BYTE] & 0x07)) ||
+               (s1 == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07)))
+#endif
+            {
+                putCharFunc('-');
+                putCharFunc('O');
+            }
+        } else {
+            uint8_t pawn = board[s0] == 'P' || board[s0] == 'p';
+
+            if(!pawn) {
+                putCharFunc(SCL_pieceToColor(board[s0], 1));
+
+                // disambiguation:
+
+                uint8_t specify = 0;
+
+                for(int i = 0; i < SCL_BOARD_SQUARES; ++i)
+                    if(i != s0 && board[i] == board[s0]) {
+                        SCL_SquareSet s;
+
+                        SCL_squareSetClear(s);
+
+                        SCL_boardGetMoves(board, i, s);
+
+                        if(SCL_squareSetContains(s, s1)) specify |= (s0 % 8 != s1 % 8) ? 1 : 2;
+                    }
+
+                if(specify & 0x01) putCharFunc('a' + s0 % 8);
+
+                if(specify & 0x02) putCharFunc('1' + s0 / 8);
+            }
+
+            if(board[s1] != '.' || (pawn && s0 % 8 != s1 % 8 && board[s1] == '.')) // capture?
+            {
+                if(pawn) putCharFunc('a' + s0 % 8);
+
+                putCharFunc('x');
+            }
+
+            putCharFunc('a' + s1 % 8);
+            putCharFunc('1' + s1 / 8);
+
+            if(pawn && (s1 >= 56 || s1 <= 7)) // promotion?
+            {
+                putCharFunc('=');
+                putCharFunc(SCL_pieceToColor(p, 1));
+            }
+        }
+
+        SCL_boardMakeMove(board, s0, s1, p);
+
+        uint8_t position = SCL_boardGetPosition(board);
+
+        if(position == SCL_POSITION_CHECK) putCharFunc('+');
+
+        if(position == SCL_POSITION_MATE) {
+            putCharFunc('#');
+            break;
+        } else if(state != SCL_RECORD_CONT) {
+            putCharFunc('*');
+            break;
+        }
+
+        putCharFunc(' ');
+    }
+}
+
+void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo) {
+    for(uint16_t i = 0; i < SCL_RECORD_MAX_SIZE; ++i) recordTo[i] = recordFrom[i];
+}
+
+void SCL_gameInit(SCL_Game* game, const SCL_Board startState) {
+    game->startState = startState;
+
+    if(startState != 0)
+        SCL_boardCopy(startState, game->board);
+    else
+        SCL_boardInit(game->board);
+
+    SCL_recordInit(game->record);
+
+    for(uint8_t i = 0; i < 14; ++i) game->prevMoves[i] = 0;
+
+    game->state = SCL_GAME_STATE_PLAYING;
+    game->ply = 0;
+
+    SCL_recordInit(game->record);
+}
+
+uint8_t SCL_gameGetRepetiotionMove(SCL_Game* game, uint8_t* squareFrom, uint8_t* squareTo) {
+    if(squareFrom != 0 && squareTo != 0) {
+        *squareFrom = 0;
+        *squareTo = 0;
+    }
+
+    /*  pos. 1st         2nd         3rd
+            |           |           |
+            v           v           v       
+             01 23 45 67 89 AB CD EF
+     move    ab cd ba dc ab cd ba dc */
+
+    if(game->ply >= 7 && game->prevMoves[0] == game->prevMoves[5] &&
+       game->prevMoves[0] == game->prevMoves[8] && game->prevMoves[0] == game->prevMoves[13] &&
+
+       game->prevMoves[1] == game->prevMoves[4] && game->prevMoves[1] == game->prevMoves[9] &&
+       game->prevMoves[1] == game->prevMoves[12] &&
+
+       game->prevMoves[2] == game->prevMoves[7] && game->prevMoves[2] == game->prevMoves[10] &&
+
+       game->prevMoves[3] == game->prevMoves[6] && game->prevMoves[3] == game->prevMoves[11]) {
+        if(squareFrom != 0 && squareTo != 0) {
+            *squareFrom = game->prevMoves[3];
+            *squareTo = game->prevMoves[2];
+        }
+
+        return 1;
+    }
+
+    return 0;
+}
+
+void SCL_gameMakeMove(SCL_Game* game, uint8_t squareFrom, uint8_t squareTo, char promoteTo) {
+    uint8_t repetitionS0, repetitionS1;
+
+    SCL_gameGetRepetiotionMove(game, &repetitionS0, &repetitionS1);
+    SCL_boardMakeMove(game->board, squareFrom, squareTo, promoteTo);
+    SCL_recordAdd(game->record, squareFrom, squareTo, promoteTo, SCL_RECORD_CONT);
+    // ^ TODO: SCL_RECORD_CONT
+
+    game->ply++;
+
+    for(uint8_t i = 0; i < 14 - 2; ++i) game->prevMoves[i] = game->prevMoves[i + 2];
+
+    game->prevMoves[12] = squareFrom;
+    game->prevMoves[13] = squareTo;
+
+    if(squareFrom == repetitionS0 && squareTo == repetitionS1)
+        game->state = SCL_GAME_STATE_DRAW_REPETITION;
+    else if(game->board[SCL_BOARD_MOVE_COUNT_BYTE] >= 50)
+        game->state = SCL_GAME_STATE_DRAW_50;
+    else {
+        uint8_t position = SCL_boardGetPosition(game->board);
+
+        switch(position) {
+        case SCL_POSITION_MATE:
+            game->state = SCL_boardWhitesTurn(game->board) ? SCL_GAME_STATE_BLACK_WIN :
+                                                             SCL_GAME_STATE_WHITE_WIN;
+            break;
+
+        case SCL_POSITION_STALEMATE:
+            game->state = SCL_GAME_STATE_DRAW_STALEMATE;
+            break;
+
+        case SCL_POSITION_DEAD:
+            game->state = SCL_GAME_STATE_DRAW_DEAD;
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+uint8_t SCL_gameUndoMove(SCL_Game* game) {
+    if(game->ply == 0) return 0;
+
+    if((game->ply - 1) > SCL_recordLength(game->record)) return 0; // can't undo, lacking record
+
+    SCL_Record r;
+
+    SCL_recordCopy(game->record, r);
+
+    uint16_t applyMoves = game->ply - 1;
+
+    SCL_gameInit(game, game->startState);
+
+    for(uint16_t i = 0; i < applyMoves; ++i) {
+        uint8_t s0, s1;
+        char p;
+
+        SCL_recordGetMove(r, i, &s0, &s1, &p);
+        SCL_gameMakeMove(game, s0, s1, p);
+    }
+
+    return 1;
+}
+
+uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom, uint8_t squareTo) {
+    if(squareFrom >= SCL_BOARD_SQUARES || squareTo >= SCL_BOARD_SQUARES) return 0;
+
+    char piece = board[squareFrom];
+
+    if((piece == '.') || (SCL_boardWhitesTurn(board) != SCL_pieceIsWhite(piece))) return 0;
+
+    SCL_SquareSet moves;
+
+    SCL_boardGetMoves(board, squareFrom, moves);
+
+    return SCL_squareSetContains(moves, squareTo);
+}
+
+#endif // guard

+ 179 - 0
chess/flipchess.c

@@ -0,0 +1,179 @@
+#include "flipchess.h"
+#include "helpers/flipchess_haptic.h"
+
+bool flipchess_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    FlipChess* app = context;
+    return scene_manager_handle_custom_event(app->scene_manager, event);
+}
+
+void flipchess_tick_event_callback(void* context) {
+    furi_assert(context);
+    FlipChess* app = context;
+    scene_manager_handle_tick_event(app->scene_manager);
+}
+
+//leave app if back button pressed
+bool flipchess_navigation_event_callback(void* context) {
+    furi_assert(context);
+    FlipChess* app = context;
+    return scene_manager_handle_back_event(app->scene_manager);
+}
+
+static void text_input_callback(void* context) {
+    furi_assert(context);
+    FlipChess* app = context;
+    bool handled = false;
+
+    // check that there is text in the input
+    if(strlen(app->input_text) > 0) {
+        if(app->input_state == FlipChessTextInputGame) {
+            if(app->import_game == 1) {
+                strncpy(app->import_game_text, app->input_text, TEXT_SIZE);
+
+                uint8_t status = FlipChessStatusNone;
+                if(status == FlipChessStatusNone) {
+                    //notification_message(app->notification, &sequence_blink_cyan_100);
+                    flipchess_play_happy_bump(app);
+                } else {
+                    //notification_message(app->notification, &sequence_blink_red_100);
+                    flipchess_play_long_bump(app);
+                }
+            }
+            // reset input state
+            app->input_state = FlipChessTextInputDefault;
+            handled = true;
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdMenu);
+        }
+    }
+
+    if(!handled) {
+        // reset input state
+        app->input_state = FlipChessTextInputDefault;
+        view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdMenu);
+    }
+}
+
+FlipChess* flipchess_app_alloc() {
+    FlipChess* app = malloc(sizeof(FlipChess));
+    app->gui = furi_record_open(RECORD_GUI);
+    app->notification = furi_record_open(RECORD_NOTIFICATION);
+
+    //Turn backlight on, believe me this makes testing your app easier
+    notification_message(app->notification, &sequence_display_backlight_on);
+
+    //Scene additions
+    app->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_enable_queue(app->view_dispatcher);
+
+    app->scene_manager = scene_manager_alloc(&flipchess_scene_handlers, app);
+    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+    view_dispatcher_set_navigation_event_callback(
+        app->view_dispatcher, flipchess_navigation_event_callback);
+    view_dispatcher_set_tick_event_callback(
+        app->view_dispatcher, flipchess_tick_event_callback, 100);
+    view_dispatcher_set_custom_event_callback(
+        app->view_dispatcher, flipchess_custom_event_callback);
+    app->submenu = submenu_alloc();
+
+    // Settings
+    app->haptic = FlipChessHapticOn;
+    app->white_mode = FlipChessPlayerHuman;
+    app->black_mode = FlipChessPlayerAI1;
+
+    // Startscreen
+    app->sound = 0;
+    // Main menu
+    app->import_game = 0;
+
+    // Text input
+    app->input_state = FlipChessTextInputDefault;
+
+    view_dispatcher_add_view(
+        app->view_dispatcher, FlipChessViewIdMenu, submenu_get_view(app->submenu));
+    app->flipchess_startscreen = flipchess_startscreen_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher,
+        FlipChessViewIdStartscreen,
+        flipchess_startscreen_get_view(app->flipchess_startscreen));
+    app->flipchess_scene_1 = flipchess_scene_1_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher,
+        FlipChessViewIdScene1,
+        flipchess_scene_1_get_view(app->flipchess_scene_1));
+    app->variable_item_list = variable_item_list_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher,
+        FlipChessViewIdSettings,
+        variable_item_list_get_view(app->variable_item_list));
+
+    app->text_input = text_input_alloc();
+    text_input_set_result_callback(
+        app->text_input,
+        text_input_callback,
+        (void*)app,
+        app->input_text,
+        TEXT_BUFFER_SIZE,
+        //clear default text
+        true);
+    text_input_set_header_text(app->text_input, "Input");
+    view_dispatcher_add_view(
+        app->view_dispatcher, FlipChessViewIdTextInput, text_input_get_view(app->text_input));
+
+    //End Scene Additions
+
+    return app;
+}
+
+void flipchess_app_free(FlipChess* app) {
+    furi_assert(app);
+
+    // Scene manager
+    scene_manager_free(app->scene_manager);
+
+    text_input_free(app->text_input);
+
+    // View Dispatcher
+    view_dispatcher_remove_view(app->view_dispatcher, FlipChessViewIdMenu);
+    view_dispatcher_remove_view(app->view_dispatcher, FlipChessViewIdScene1);
+    view_dispatcher_remove_view(app->view_dispatcher, FlipChessViewIdSettings);
+    view_dispatcher_remove_view(app->view_dispatcher, FlipChessViewIdTextInput);
+    submenu_free(app->submenu);
+
+    view_dispatcher_free(app->view_dispatcher);
+    furi_record_close(RECORD_GUI);
+
+    app->gui = NULL;
+    app->notification = NULL;
+
+    //Remove whatever is left
+    //memzero(app, sizeof(FlipChess));
+    free(app);
+}
+
+int32_t flipchess_app(void* p) {
+    UNUSED(p);
+    FlipChess* app = flipchess_app_alloc();
+
+    // Disabled because causes exit on custom firmwares such as RM
+    /*if(!furi_hal_region_is_provisioned()) {
+        flipchess_app_free(app);
+        return 1;
+    }*/
+
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+
+    scene_manager_next_scene(
+        app->scene_manager, FlipChessSceneStartscreen); //Start with start screen
+    //scene_manager_next_scene(app->scene_manager, FlipChessSceneMenu); //if you want to directly start with Menu
+
+    furi_hal_random_init();
+    // furi_hal_power_suppress_charge_enter();
+
+    view_dispatcher_run(app->view_dispatcher);
+
+    // furi_hal_power_suppress_charge_exit();
+    flipchess_app_free(app);
+
+    return 0;
+}

+ 78 - 0
chess/flipchess.h

@@ -0,0 +1,78 @@
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_random.h>
+#include <gui/gui.h>
+#include <input/input.h>
+#include <stdlib.h>
+#include <notification/notification_messages.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/submenu.h>
+#include <gui/scene_manager.h>
+#include <gui/modules/variable_item_list.h>
+#include <gui/modules/text_input.h>
+#include "scenes/flipchess_scene.h"
+#include "views/flipchess_startscreen.h"
+#include "views/flipchess_scene_1.h"
+
+#define FLIPCHESS_VERSION "v1.9.0"
+
+#define TEXT_BUFFER_SIZE 96
+#define TEXT_SIZE (TEXT_BUFFER_SIZE - 1)
+
+typedef struct {
+    Gui* gui;
+    NotificationApp* notification;
+    ViewDispatcher* view_dispatcher;
+    Submenu* submenu;
+    SceneManager* scene_manager;
+    VariableItemList* variable_item_list;
+    TextInput* text_input;
+    FlipChessStartscreen* flipchess_startscreen;
+    FlipChessScene1* flipchess_scene_1;
+    // Settings options
+    int haptic;
+    int white_mode;
+    int black_mode;
+    // Startscreen options
+    uint8_t sound;
+    // Main menu options
+    uint8_t import_game;
+    // Text input
+    uint8_t input_state;
+    char import_game_text[TEXT_BUFFER_SIZE];
+    char input_text[TEXT_BUFFER_SIZE];
+} FlipChess;
+
+typedef enum {
+    FlipChessViewIdStartscreen,
+    FlipChessViewIdMenu,
+    FlipChessViewIdScene1,
+    FlipChessViewIdSettings,
+    FlipChessViewIdTextInput,
+} FlipChessViewId;
+
+typedef enum {
+    FlipChessHapticOff,
+    FlipChessHapticOn,
+} FlipChessHapticState;
+
+typedef enum {
+    FlipChessPlayerHuman = 0,
+    FlipChessPlayerAI1 = 1,
+    FlipChessPlayerAI2 = 2,
+    FlipChessPlayerAI3 = 3,
+} FlipChessPlayerMode;
+
+typedef enum { FlipChessTextInputDefault, FlipChessTextInputGame } FlipChessTextInputState;
+
+typedef enum {
+    FlipChessStatusNone = 0,
+    FlipChessStatusMovePlayer = 1,
+    FlipChessStatusMoveAI = 2,
+    FlipChessStatusMoveUndo = 3,
+    FlipChessStatusReturn = 10,
+    FlipChessStatusLoadError = 11,
+    FlipChessStatusSaveError = 12,
+} FlipChessStatus;

BIN
chess/flipchess_10px.png


+ 16 - 0
chess/helpers/flipchess_custom_event.h

@@ -0,0 +1,16 @@
+#pragma once
+
+typedef enum {
+    FlipChessCustomEventStartscreenUp,
+    FlipChessCustomEventStartscreenDown,
+    FlipChessCustomEventStartscreenLeft,
+    FlipChessCustomEventStartscreenRight,
+    FlipChessCustomEventStartscreenOk,
+    FlipChessCustomEventStartscreenBack,
+    FlipChessCustomEventScene1Up,
+    FlipChessCustomEventScene1Down,
+    FlipChessCustomEventScene1Left,
+    FlipChessCustomEventScene1Right,
+    FlipChessCustomEventScene1Ok,
+    FlipChessCustomEventScene1Back,
+} FlipChessCustomEvent;

+ 153 - 0
chess/helpers/flipchess_file.c

@@ -0,0 +1,153 @@
+#include "flipchess_file.h"
+#include <storage/storage.h>
+#include <loader/loader.h>
+
+// #define FLIPCHESS_APP_BASE_FOLDER APP_BOARDA_PATH("flipchess")
+#define FLIPCHESS_APP_BASE_FOLDER EXT_PATH("apps_data/flipchess")
+#define FLIPCHESS_APP_BASE_FOLDER_PATH(path) FLIPCHESS_APP_BASE_FOLDER "/" path
+#define FLIPCHESS_BOARD_FILE_NAME "board_fen.txt"
+#define FLIPCHESS_BOARD_FILE_NAME_BAK "board_fen.bak"
+#define FLIPCHESS_BOARD_PATH FLIPCHESS_APP_BASE_FOLDER_PATH(FLIPCHESS_BOARD_FILE_NAME)
+#define FLIPCHESS_BOARD_PATH_BAK FLIPCHESS_APP_BASE_FOLDER_PATH(FLIPCHESS_BOARD_FILE_NAME_BAK)
+
+#define FILE_MAX_PATH_LEN 48
+#define FILE_MAX_CHARS 94
+
+bool flipchess_has_file(const FlipChessFile file_type, const char* file_name, const bool remove) {
+    bool ret = false;
+    const char* path;
+    if(file_type == FlipChessFileBoard) {
+        path = FLIPCHESS_BOARD_PATH;
+    } else {
+        char path_buf[FILE_MAX_PATH_LEN] = {0};
+        strcpy(path_buf, FLIPCHESS_APP_BASE_FOLDER); // 22
+        strcpy(path_buf + strlen(path_buf), "/");
+        strcpy(path_buf + strlen(path_buf), file_name);
+        path = path_buf;
+    }
+
+    Storage* fs_api = furi_record_open(RECORD_STORAGE);
+    if(remove) {
+        ret = storage_simply_remove(fs_api, path);
+    } else {
+        ret = storage_file_exists(fs_api, path);
+    }
+    furi_record_close(RECORD_STORAGE);
+
+    return ret;
+}
+
+bool flipchess_load_file(char* contents, const FlipChessFile file_type, const char* file_name) {
+    bool ret = false;
+    const char* path;
+    if(file_type == FlipChessFileBoard) {
+        path = FLIPCHESS_BOARD_PATH;
+    } else {
+        char path_buf[FILE_MAX_PATH_LEN] = {0};
+        strcpy(path_buf, FLIPCHESS_APP_BASE_FOLDER); // 22
+        strcpy(path_buf + strlen(path_buf), "/");
+        strcpy(path_buf + strlen(path_buf), file_name);
+        path = path_buf;
+    }
+
+    Storage* fs_api = furi_record_open(RECORD_STORAGE);
+
+    File* settings_file = storage_file_alloc(fs_api);
+    if(storage_file_open(settings_file, path, FSAM_READ, FSOM_OPEN_EXISTING)) {
+        char chr;
+        int i = 0;
+        while((storage_file_read(settings_file, &chr, 1) == 1) &&
+              !storage_file_eof(settings_file)) {
+            if(i < FILE_MAX_CHARS) {
+                contents[i] = chr;
+            }
+            i++;
+        }
+        ret = true;
+    } else {
+        contents[0] = '\0';
+        ret = false;
+    }
+    storage_file_close(settings_file);
+    storage_file_free(settings_file);
+    furi_record_close(RECORD_STORAGE);
+
+    if(strlen(contents) > 0) {
+        Storage* fs_api = furi_record_open(RECORD_STORAGE);
+        FileInfo layout_file_info;
+        FS_Error file_check_err = storage_common_stat(fs_api, path, &layout_file_info);
+        furi_record_close(RECORD_STORAGE);
+        if(file_check_err != FSE_OK) {
+            contents[0] = '\0';
+            ret = false;
+        }
+        // if(layout_file_info.size != 256) {
+        //     memzero(settings, strlen(settings));
+        //     settings[0] = '\0';
+        // }
+    }
+
+    return ret;
+}
+
+bool flipchess_save_file(
+    const char* settings,
+    const FlipChessFile file_type,
+    const char* file_name,
+    const bool append,
+    const bool overwrite) {
+    bool ret = false;
+    const char* path;
+    const char* path_bak;
+    if(file_type == FlipChessFileBoard) {
+        path = FLIPCHESS_BOARD_PATH;
+        path_bak = FLIPCHESS_BOARD_PATH_BAK;
+    } else {
+        char path_buf[FILE_MAX_PATH_LEN] = {0};
+        strcpy(path_buf, FLIPCHESS_APP_BASE_FOLDER); // 22
+        strcpy(path_buf + strlen(path_buf), "/");
+        strcpy(path_buf + strlen(path_buf), file_name);
+        path = path_buf;
+        path_bak = NULL;
+    }
+    int open_mode = FSOM_OPEN_ALWAYS;
+    if(append) {
+        open_mode = FSOM_OPEN_APPEND;
+    }
+
+    Storage* fs_api = furi_record_open(RECORD_STORAGE);
+
+    // try to create the folder
+    storage_simply_mkdir(fs_api, FLIPCHESS_APP_BASE_FOLDER);
+
+    if(overwrite) {
+        storage_simply_remove(fs_api, path);
+    }
+
+    File* settings_file = storage_file_alloc(fs_api);
+    if(storage_file_open(settings_file, path, FSAM_WRITE, open_mode)) {
+        storage_file_write(settings_file, settings, strlen(settings));
+        storage_file_write(settings_file, "\n", 1);
+        ret = true;
+    }
+    storage_file_close(settings_file);
+    storage_file_free(settings_file);
+
+    if(path_bak != NULL) {
+        if(overwrite) {
+            storage_simply_remove(fs_api, path_bak);
+        }
+
+        File* settings_file_bak = storage_file_alloc(fs_api);
+        if(storage_file_open(settings_file_bak, path_bak, FSAM_WRITE, open_mode)) {
+            storage_file_write(settings_file_bak, settings, strlen(settings));
+            storage_file_write(settings_file_bak, "\n", 1);
+        }
+        storage_file_close(settings_file_bak);
+        storage_file_free(settings_file_bak);
+    }
+
+    furi_record_close(RECORD_STORAGE);
+
+    return ret;
+}

+ 15 - 0
chess/helpers/flipchess_file.h

@@ -0,0 +1,15 @@
+#include <stdbool.h>
+
+typedef enum {
+    FlipChessFileBoard,
+    FlipChessFileOther,
+} FlipChessFile;
+
+bool flipchess_has_file(const FlipChessFile file_type, const char* file_name, const bool remove);
+bool flipchess_load_file(char* contents, const FlipChessFile file_type, const char* file_name);
+bool flipchess_save_file(
+    const char* contents,
+    const FlipChessFile file_type,
+    const char* file_name,
+    const bool append,
+    const bool overwrite);

+ 35 - 0
chess/helpers/flipchess_haptic.c

@@ -0,0 +1,35 @@
+#include "flipchess_haptic.h"
+#include "../flipchess.h"
+
+void flipchess_play_happy_bump(void* context) {
+    FlipChess* app = context;
+    if(app->haptic != 1) {
+        return;
+    }
+    notification_message(app->notification, &sequence_set_vibro_on);
+    furi_thread_flags_wait(0, FuriFlagWaitAny, 20);
+    notification_message(app->notification, &sequence_reset_vibro);
+}
+
+void flipchess_play_bad_bump(void* context) {
+    FlipChess* app = context;
+    if(app->haptic != 1) {
+        return;
+    }
+    notification_message(app->notification, &sequence_set_vibro_on);
+    furi_thread_flags_wait(0, FuriFlagWaitAny, 100);
+    notification_message(app->notification, &sequence_reset_vibro);
+}
+
+void flipchess_play_long_bump(void* context) {
+    FlipChess* app = context;
+    if(app->haptic != 1) {
+        return;
+    }
+    for(int i = 0; i < 4; i++) {
+        notification_message(app->notification, &sequence_set_vibro_on);
+        furi_thread_flags_wait(0, FuriFlagWaitAny, 50);
+        notification_message(app->notification, &sequence_reset_vibro);
+        furi_thread_flags_wait(0, FuriFlagWaitAny, 100);
+    }
+}

+ 7 - 0
chess/helpers/flipchess_haptic.h

@@ -0,0 +1,7 @@
+#include <notification/notification_messages.h>
+
+void flipchess_play_happy_bump(void* context);
+
+void flipchess_play_bad_bump(void* context);
+
+void flipchess_play_long_bump(void* context);

+ 37 - 0
chess/helpers/flipchess_voice.cpp

@@ -0,0 +1,37 @@
+#include "flipchess_voice.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include "../sam/stm32_sam.h"
+STM32SAM voice;
+
+void flipchess_voice_shall_we_play() {
+    if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) {
+        voice.begin();
+        voice.say("SHAAL WE PLAY AY GAME?");
+        furi_hal_speaker_release();
+    }
+}
+
+void flipchess_voice_which_side() {
+    if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) {
+        voice.begin();
+        voice.say("WHICH SIDE DO YOU WANT?");
+        furi_hal_speaker_release();
+    }
+}
+
+void flipchess_voice_how_about_chess() {
+    if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) {
+        voice.begin();
+        voice.say("HOW ABOUT A NICE GAME OF CHESS?");
+        furi_hal_speaker_release();
+    }
+}
+
+void flipchess_voice_a_strange_game() {
+    if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(1000)) {
+        voice.begin();
+        voice.say("A STRANGE GAME... THE ONLY WINNING MOVE IS NOT TO PLAY.");
+        furi_hal_speaker_release();
+    }
+}

+ 12 - 0
chess/helpers/flipchess_voice.h

@@ -0,0 +1,12 @@
+#ifdef __cplusplus
+#define EXTERNC extern "C"
+#else
+#define EXTERNC
+#endif
+
+EXTERNC void flipchess_voice_shall_we_play();
+EXTERNC void flipchess_voice_which_side();
+EXTERNC void flipchess_voice_how_about_chess();
+EXTERNC void flipchess_voice_a_strange_game();
+
+#undef EXTERNC

BIN
chess/icons/Background_128x11.png


BIN
chess/icons/FLIPR_128x64.png


+ 5703 - 0
chess/sam/stm32_sam.cpp

@@ -0,0 +1,5703 @@
+
+#include "stm32_sam.h"
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           All
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+char input[256 + 1] = {0}; //tab39445
+//standard sam sound
+
+unsigned char wait1 = 7;
+unsigned char wait2 = 6;
+
+unsigned char A, X, Y;
+unsigned char mem44;
+unsigned char mem47;
+unsigned char mem49;
+unsigned char mem39;
+unsigned char mem50;
+unsigned char mem51;
+unsigned char mem53;
+unsigned char mem56;
+unsigned char mem59 = 0;
+
+unsigned char phonemeIndexOutput[60]; //tab47296
+unsigned char stressOutput[60]; //tab47365
+unsigned char phonemeLengthOutput[60]; //tab47416
+
+// contains the soundbuffer position
+int bufferpos;
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           Sam Tabs
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+//tab40672
+const unsigned char stressInputTable[] = {'*', '1', '2', '3', '4', '5', '6', '7', '8'};
+
+//tab40682
+const unsigned char signInputTable1[] = {
+    ' ', '.', '?', ',', '-', 'I', 'I', 'E', 'A', 'A', 'A', 'A', 'U', 'A', 'I', 'E', 'U',
+    'O', 'R', 'L', 'W', 'Y', 'W', 'R', 'L', 'W', 'Y', 'M', 'N', 'N', 'D', 'Q', 'S', 'S',
+    'F', 'T', '/', '/', 'Z', 'Z', 'V', 'D', 'C', '*', 'J', '*', '*', '*', 'E', 'A', 'O',
+    'A', 'O', 'U', 'B', '*', '*', 'D', '*', '*', 'G', '*', '*', 'G', '*', '*', 'P', '*',
+    '*', 'T', '*', '*', 'K', '*', '*', 'K', '*', '*', 'U', 'U', 'U'};
+
+//tab40763
+const unsigned char signInputTable2[] = {
+    '*', '*', '*', '*', '*', 'Y', 'H', 'H', 'E', 'A', 'H', 'O', 'H', 'X', 'X', 'R', 'X',
+    'H', 'X', 'X', 'X', 'X', 'H', '*', '*', '*', '*', '*', '*', 'X', 'X', '*', '*', 'H',
+    '*', 'H', 'H', 'X', '*', 'H', '*', 'H', 'H', '*', '*', '*', '*', '*', 'Y', 'Y', 'Y',
+    'W', 'W', 'W', '*', '*', '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', '*', '*',
+    '*', '*', '*', '*', '*', '*', '*', 'X', '*', '*', 'L', 'M', 'N'};
+
+//loc_9F8C
+const unsigned char flags[] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0x84, 0x84, 0xA4,
+    0xA4, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x4C,
+    0x4C, 0x4C, 0x48, 0x4C, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, 0x44, 0x44,
+    0x48, 0x40, 0x4C, 0x44, 0x00, 0x00, 0xB4, 0xB4, 0xB4, 0x94, 0x94, 0x94, 0x4E, 0x4E,
+    0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0x4B, 0x4B, 0x4B, 0x4B,
+    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x80, 0xC1, 0xC1
+
+};
+
+//??? flags overlap flags2
+//loc_9FDA
+const unsigned char flags2[] = {
+    0x80, 0xC1, 0xC1, 0xC1, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x08, 0x0C, 0x08, 0x04, 0x40,
+    0x24, 0x20, 0x20, 0x24, 0x00, 0x00, 0x24, 0x20, 0x20, 0x24, 0x20, 0x20, 0x00, 0x20, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+//tab45616???
+const unsigned char phonemeStressedLengthTable[] = {
+    0x00, 0x12, 0x12, 0x12, 8,   0xB, 9,   0xB, 0xE, 0xF, 0xB, 0x10, 0xC, 6, 6, 0xE,
+    0xC,  0xE,  0xC,  0xB,  8,   8,   0xB, 0xA, 9,   8,   8,   8,    8,   8, 3, 5,
+    2,    2,    2,    2,    2,   2,   6,   6,   8,   6,   6,   2,    9,   4, 2, 1,
+    0xE,  0xF,  0xF,  0xF,  0xE, 0xE, 8,   2,   2,   7,   2,   1,    7,   2, 2, 7,
+    2,    2,    8,    2,    2,   6,   2,   2,   7,   2,   4,   7,    1,   4, 5, 5};
+
+//tab45536???
+const unsigned char phonemeLengthTable[] = {
+    0, 0x12, 0x12, 0x12, 8, 8, 8, 8,    8,   0xB, 6,   0xC, 0xA, 5, 5, 0xB, 0xA, 0xA, 0xA,  9,
+    8, 7,    9,    7,    6, 8, 6, 7,    7,   7,   2,   5,   2,   2, 2, 2,   2,   2,   6,    6,
+    7, 6,    6,    2,    8, 3, 1, 0x1E, 0xD, 0xC, 0xC, 0xC, 0xE, 9, 6, 1,   2,   5,   1,    1,
+    6, 1,    2,    6,    1, 2, 8, 2,    2,   4,   2,   2,   6,   1, 4, 6,   1,   4,   0xC7, 0xFF};
+
+/*
+
+  Ind  | phoneme |  flags   |
+  -----|---------|----------|
+  0    |   *     | 00000000 |
+  1    |  .*     | 00000000 |
+  2    |  ?*     | 00000000 |
+  3    |  ,*     | 00000000 |
+  4    |  -*     | 00000000 |
+
+  VOWELS
+  5    |  IY     | 10100100 |
+  6    |  IH     | 10100100 |
+  7    |  EH     | 10100100 |
+  8    |  AE     | 10100100 |
+  9    |  AA     | 10100100 |
+  10   |  AH     | 10100100 |
+  11   |  AO     | 10000100 |
+  17   |  OH     | 10000100 |
+  12   |  UH     | 10000100 |
+  16   |  UX     | 10000100 |
+  15   |  ER     | 10000100 |
+  13   |  AX     | 10100100 |
+  14   |  IX     | 10100100 |
+
+  DIPHTONGS
+  48   |  EY     | 10110100 |
+  49   |  AY     | 10110100 |
+  50   |  OY     | 10110100 |
+  51   |  AW     | 10010100 |
+  52   |  OW     | 10010100 |
+  53   |  UW     | 10010100 |
+
+
+  21   |  YX     | 10000100 |
+  20   |  WX     | 10000100 |
+  18   |  RX     | 10000100 |
+  19   |  LX     | 10000100 |
+  37   |  /X     | 01000000 |
+  30   |  DX     | 01001000 |
+
+
+  22   |  WH     | 01000100 |
+
+
+  VOICED CONSONANTS
+  23   |  R*     | 01000100 |
+  24   |  L*     | 01000100 |
+  25   |  W*     | 01000100 |
+  26   |  Y*     | 01000100 |
+  27   |  M*     | 01001100 |
+  28   |  N*     | 01001100 |
+  29   |  NX     | 01001100 |
+  54   |  B*     | 01001110 |
+  57   |  D*     | 01001110 |
+  60   |  G*     | 01001110 |
+  44   |  J*     | 01001100 |
+  38   |  Z*     | 01000100 |
+  39   |  ZH     | 01000100 |
+  40   |  V*     | 01000100 |
+  41   |  DH     | 01000100 |
+
+  unvoiced CONSONANTS
+  32   |  S*     | 01000000 |
+  33   |  SH     | 01000000 |
+  34   |  F*     | 01000000 |
+  35   |  TH     | 01000000 |
+  66   |  P*     | 01001011 |
+  69   |  T*     | 01001011 |
+  72   |  K*     | 01001011 |
+  42   |  CH     | 01001000 |
+  36   |  /H     | 01000000 |
+
+  43   |  **     | 01000000 |
+  45   |  **     | 01000100 |
+  46   |  **     | 00000000 |
+  47   |  **     | 00000000 |
+
+
+  55   |  **     | 01001110 |
+  56   |  **     | 01001110 |
+  58   |  **     | 01001110 |
+  59   |  **     | 01001110 |
+  61   |  **     | 01001110 |
+  62   |  **     | 01001110 |
+  63   |  GX     | 01001110 |
+  64   |  **     | 01001110 |
+  65   |  **     | 01001110 |
+  67   |  **     | 01001011 |
+  68   |  **     | 01001011 |
+  70   |  **     | 01001011 |
+  71   |  **     | 01001011 |
+  73   |  **     | 01001011 |
+  74   |  **     | 01001011 |
+  75   |  KX     | 01001011 |
+  76   |  **     | 01001011 |
+  77   |  **     | 01001011 |
+
+
+  SPECIAL
+  78   |  UL     | 10000000 |
+  79   |  UM     | 11000001 |
+  80   |  UN     | 11000001 |
+  31   |  Q*     | 01001100 |
+
+*/
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           RenderTabs
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+const unsigned char tab48426[5] = {0x18, 0x1A, 0x17, 0x17, 0x17};
+
+const unsigned char tab47492[] = {0, 0, 0xE0, 0xE6, 0xEC, 0xF3, 0xF9, 0, 6, 0xC, 6};
+
+const unsigned char amplitudeRescale[] = {
+    0,
+    1,
+    2,
+    2,
+    2,
+    3,
+    3,
+    4,
+    4,
+    5,
+    6,
+    8,
+    9,
+    0xB,
+    0xD,
+    0xF,
+    0 //17 elements?
+};
+
+// Used to decide which phoneme's blend lengths. The candidate with the lower score is selected.
+// tab45856
+const unsigned char blendRank[] = {0,    0x1F, 0x1F, 0x1F, 0x1F, 2,    2,    2,    2,    2,
+                                   2,    2,    2,    2,    5,    5,    2,    0xA,  2,    8,
+                                   5,    5,    0xB,  0xA,  9,    8,    8,    0xA0, 8,    8,
+                                   0x17, 0x1F, 0x12, 0x12, 0x12, 0x12, 0x1E, 0x1E, 0x14, 0x14,
+                                   0x14, 0x14, 0x17, 0x17, 0x1A, 0x1A, 0x1D, 0x1D, 2,    2,
+                                   2,    2,    2,    2,    0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B,
+                                   0x1A, 0x1D, 0x1B, 0x1A, 0x1D, 0x1B, 0x17, 0x1D, 0x17, 0x17,
+                                   0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x1D, 0x17, 0x17, 0x17};
+
+// Number of frames at the end of a phoneme devoted to interpolating to next phoneme's final value
+//tab45696
+const unsigned char outBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,    4,
+                                        4, 4, 3, 2, 4, 4, 2, 2, 2, 2, 2, 1, 1, 1, 1,    1,
+                                        1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 1, 0, 1, 0,    5,
+                                        5, 5, 5, 5, 4, 4, 2, 0, 1, 2, 0, 1, 2, 0, 1,    2,
+                                        0, 1, 2, 0, 2, 2, 0, 1, 3, 0, 2, 3, 0, 2, 0xA0, 0xA0};
+
+// Number of frames at beginning of a phoneme devoted to interpolating to phoneme's final value
+// tab45776
+const unsigned char inBlendLength[] = {0, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,    4,
+                                       4, 4, 3, 3, 4, 4, 3, 3, 3, 3, 3, 1, 2, 3, 2,    1,
+                                       3, 3, 3, 3, 1, 1, 3, 3, 3, 2, 2, 3, 2, 3, 0,    0,
+                                       5, 5, 5, 5, 4, 4, 2, 0, 2, 2, 0, 3, 2, 0, 4,    2,
+                                       0, 3, 2, 0, 2, 2, 0, 2, 3, 0, 3, 3, 0, 3, 0xB0, 0xA0};
+
+// Looks like it's used as bit flags
+// High bits masked by 248 (11111000)
+//
+// 32: S*    241         11110001
+// 33: SH    226         11100010
+// 34: F*    211         11010011
+// 35: TH    187         10111011
+// 36: /H    124         01111100
+// 37: /X    149         10010101
+// 38: Z*    1           00000001
+// 39: ZH    2           00000010
+// 40: V*    3           00000011
+// 41: DH    3           00000011
+// 43: **    114         01110010
+// 45: **    2           00000010
+// 67: **    27          00011011
+// 70: **    25          00011001
+// tab45936
+const unsigned char sampledConsonantFlags[] = {
+    0, 0, 0, 0,    0, 0, 0, 0,    0, 0, 0,    0, 0,    0,    0,    0,    0,    0,    0, 0,
+    0, 0, 0, 0,    0, 0, 0, 0,    0, 0, 0,    0, 0xF1, 0xE2, 0xD3, 0xBB, 0x7C, 0x95, 1, 2,
+    3, 3, 0, 0x72, 0, 2, 0, 0,    0, 0, 0,    0, 0,    0,    0,    0,    0,    0,    0, 0,
+    0, 0, 0, 0,    0, 0, 0, 0x1B, 0, 0, 0x19, 0, 0,    0,    0,    0,    0,    0,    0, 0};
+
+//tab45056
+unsigned char freq1data[] = {
+    0x00, 0x13, 0x13, 0x13, 0x13, 0xA,  0xE, 0x12, 0x18, 0x1A, 0x16, 0x14, 0x10, 0x14, 0xE,  0x12,
+    0xE,  0x12, 0x12, 0x10, 0xC,  0xE,  0xA, 0x12, 0xE,  0xA,  8,    6,    6,    6,    6,    0x11,
+    6,    6,    6,    6,    0xE,  0x10, 9,   0xA,  8,    0xA,  6,    6,    6,    5,    6,    0,
+    0x12, 0x1A, 0x14, 0x1A, 0x12, 0xC,  6,   6,    6,    6,    6,    6,    6,    6,    6,    6,
+    6,    6,    6,    6,    6,    6,    6,   6,    6,    0xA,  0xA,  6,    6,    6,    0x2C, 0x13};
+
+//tab451356
+unsigned char freq2data[] = {0x00, 0x43, 0x43, 0x43, 0x43, 0x54, 0x48, 0x42, 0x3E, 0x28,
+                             0x2C, 0x1E, 0x24, 0x2C, 0x48, 0x30, 0x24, 0x1E, 0x32, 0x24,
+                             0x1C, 0x44, 0x18, 0x32, 0x1E, 0x18, 0x52, 0x2E, 0x36, 0x56,
+                             0x36, 0x43, 0x49, 0x4F, 0x1A, 0x42, 0x49, 0x25, 0x33, 0x42,
+                             0x28, 0x2F, 0x4F, 0x4F, 0x42, 0x4F, 0x6E, 0x00, 0x48, 0x26,
+                             0x1E, 0x2A, 0x1E, 0x22, 0x1A, 0x1A, 0x1A, 0x42, 0x42, 0x42,
+                             0x6E, 0x6E, 0x6E, 0x54, 0x54, 0x54, 0x1A, 0x1A, 0x1A, 0x42,
+                             0x42, 0x42, 0x6D, 0x56, 0x6D, 0x54, 0x54, 0x54, 0x7F, 0x7F};
+//tab45216
+unsigned char freq3data[] = {0x00, 0x5B, 0x5B, 0x5B, 0x5B, 0x6E, 0x5D, 0x5B, 0x58, 0x59,
+                             0x57, 0x58, 0x52, 0x59, 0x5D, 0x3E, 0x52, 0x58, 0x3E, 0x6E,
+                             0x50, 0x5D, 0x5A, 0x3C, 0x6E, 0x5A, 0x6E, 0x51, 0x79, 0x65,
+                             0x79, 0x5B, 0x63, 0x6A, 0x51, 0x79, 0x5D, 0x52, 0x5D, 0x67,
+                             0x4C, 0x5D, 0x65, 0x65, 0x79, 0x65, 0x79, 0x00, 0x5A, 0x58,
+                             0x58, 0x58, 0x58, 0x52, 0x51, 0x51, 0x51, 0x79, 0x79, 0x79,
+                             0x70, 0x6E, 0x6E, 0x5E, 0x5E, 0x5E, 0x51, 0x51, 0x51, 0x79,
+                             0x79, 0x79, 0x65, 0x65, 0x70, 0x5E, 0x5E, 0x5E, 0x08, 0x01};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           Reciter
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+unsigned char inputtemp[256]; // secure copy of input tab36096
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           Render
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+//timetable for more accurate c64 simulation
+int timetable[5][5] = {
+    {162, 167, 167, 127, 128},
+    {226, 60, 60, 0, 0},
+    {225, 60, 59, 0, 0},
+    {200, 0, 0, 54, 55},
+    {199, 0, 0, 54, 54}};
+
+unsigned oldtimetableindex;
+
+const unsigned char ampl1data[] = {0,   0,   0,   0,   0,   0xD, 0xD, 0xE, 0xF, 0xF, 0xF, 0xF,
+                                   0xF, 0xC, 0xD, 0xC, 0xF, 0xF, 0xD, 0xD, 0xD, 0xE, 0xD, 0xC,
+                                   0xD, 0xD, 0xD, 0xC, 9,   9,   0,   0,   0,   0,   0,   0,
+                                   0,   0,   0xB, 0xB, 0xB, 0xB, 0,   0,   1,   0xB, 0,   2,
+                                   0xE, 0xF, 0xF, 0xF, 0xF, 0xD, 2,   4,   0,   2,   4,   0,
+                                   1,   4,   0,   1,   4,   0,   0,   0,   0,   0,   0,   0,
+                                   0,   0xC, 0,   0,   0,   0,   0xF, 0xF};
+
+const unsigned char ampl2data[] = {
+    0, 0,   0, 0,   0, 0xA, 0xB, 0xD, 0xE, 0xD, 0xC, 0xC, 0xB, 9,   0xB, 0xB, 0xC, 0xC, 0xC, 8,
+    8, 0xC, 8, 0xA, 8, 8,   0xA, 3,   9,   6,   0,   0,   0,   0,   0,   0,   0,   0,   3,   5,
+    3, 4,   0, 0,   0, 5,   0xA, 2,   0xE, 0xD, 0xC, 0xD, 0xC, 8,   0,   1,   0,   0,   1,   0,
+    0, 1,   0, 0,   1, 0,   0,   0,   0,   0,   0,   0,   0,   0xA, 0,   0,   0xA, 0,   0,   0};
+
+const unsigned char ampl3data[] = {0, 0, 0, 0, 0, 8, 7, 8, 8, 1, 1, 0, 1, 0, 7,    5,
+                                   1, 0, 6, 1, 0, 7, 0, 5, 1, 0, 8, 0, 0, 3, 0,    0,
+                                   0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0xE,  1,
+                                   9, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,    0,
+                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 5, 0, 0x13, 0x10};
+
+//tab42240
+const signed char sinus[256] = {
+    0,    3,    6,    9,    12,   16,   19,   22,   25,   28,   31,   34,   37,   40,   43,   46,
+    49,   51,   54,   57,   60,   63,   65,   68,   71,   73,   76,   78,   81,   83,   85,   88,
+    90,   92,   94,   96,   98,   100,  102,  104,  106,  107,  109,  111,  112,  113,  115,  116,
+    117,  118,  120,  121,  122,  122,  123,  124,  125,  125,  126,  126,  126,  127,  127,  127,
+    127,  127,  127,  127,  126,  126,  126,  125,  125,  124,  123,  122,  122,  121,  120,  118,
+    117,  116,  115,  113,  112,  111,  109,  107,  106,  104,  102,  100,  98,   96,   94,   92,
+    90,   88,   85,   83,   81,   78,   76,   73,   71,   68,   65,   63,   60,   57,   54,   51,
+    49,   46,   43,   40,   37,   34,   31,   28,   25,   22,   19,   16,   12,   9,    6,    3,
+    0,    -3,   -6,   -9,   -12,  -16,  -19,  -22,  -25,  -28,  -31,  -34,  -37,  -40,  -43,  -46,
+    -49,  -51,  -54,  -57,  -60,  -63,  -65,  -68,  -71,  -73,  -76,  -78,  -81,  -83,  -85,  -88,
+    -90,  -92,  -94,  -96,  -98,  -100, -102, -104, -106, -107, -109, -111, -112, -113, -115, -116,
+    -117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127,
+    -127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118,
+    -117, -116, -115, -113, -112, -111, -109, -107, -106, -104, -102, -100, -98,  -96,  -94,  -92,
+    -90,  -88,  -85,  -83,  -81,  -78,  -76,  -73,  -71,  -68,  -65,  -63,  -60,  -57,  -54,  -51,
+    -49,  -46,  -43,  -40,  -37,  -34,  -31,  -28,  -25,  -22,  -19,  -16,  -12,  -9,   -6,   -3};
+
+//tab42496
+const unsigned char rectangle[] = {
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x70};
+
+//random data ?
+const unsigned char sampleTable[0x500] = {
+    //00
+
+    0x38,
+    0x84,
+    0x6B,
+    0x19,
+    0xC6,
+    0x63,
+    0x18,
+    0x86,
+    0x73,
+    0x98,
+    0xC6,
+    0xB1,
+    0x1C,
+    0xCA,
+    0x31,
+    0x8C,
+    0xC7,
+    0x31,
+    0x88,
+    0xC2,
+    0x30,
+    0x98,
+    0x46,
+    0x31,
+    0x18,
+    0xC6,
+    0x35,
+    0xC,
+    0xCA,
+    0x31,
+    0xC,
+    0xC6
+    //20
+    ,
+    0x21,
+    0x10,
+    0x24,
+    0x69,
+    0x12,
+    0xC2,
+    0x31,
+    0x14,
+    0xC4,
+    0x71,
+    8,
+    0x4A,
+    0x22,
+    0x49,
+    0xAB,
+    0x6A,
+    0xA8,
+    0xAC,
+    0x49,
+    0x51,
+    0x32,
+    0xD5,
+    0x52,
+    0x88,
+    0x93,
+    0x6C,
+    0x94,
+    0x22,
+    0x15,
+    0x54,
+    0xD2,
+    0x25
+    //40
+    ,
+    0x96,
+    0xD4,
+    0x50,
+    0xA5,
+    0x46,
+    0x21,
+    8,
+    0x85,
+    0x6B,
+    0x18,
+    0xC4,
+    0x63,
+    0x10,
+    0xCE,
+    0x6B,
+    0x18,
+    0x8C,
+    0x71,
+    0x19,
+    0x8C,
+    0x63,
+    0x35,
+    0xC,
+    0xC6,
+    0x33,
+    0x99,
+    0xCC,
+    0x6C,
+    0xB5,
+    0x4E,
+    0xA2,
+    0x99
+    //60
+    ,
+    0x46,
+    0x21,
+    0x28,
+    0x82,
+    0x95,
+    0x2E,
+    0xE3,
+    0x30,
+    0x9C,
+    0xC5,
+    0x30,
+    0x9C,
+    0xA2,
+    0xB1,
+    0x9C,
+    0x67,
+    0x31,
+    0x88,
+    0x66,
+    0x59,
+    0x2C,
+    0x53,
+    0x18,
+    0x84,
+    0x67,
+    0x50,
+    0xCA,
+    0xE3,
+    0xA,
+    0xAC,
+    0xAB,
+    0x30
+    //80
+    ,
+    0xAC,
+    0x62,
+    0x30,
+    0x8C,
+    0x63,
+    0x10,
+    0x94,
+    0x62,
+    0xB1,
+    0x8C,
+    0x82,
+    0x28,
+    0x96,
+    0x33,
+    0x98,
+    0xD6,
+    0xB5,
+    0x4C,
+    0x62,
+    0x29,
+    0xA5,
+    0x4A,
+    0xB5,
+    0x9C,
+    0xC6,
+    0x31,
+    0x14,
+    0xD6,
+    0x38,
+    0x9C,
+    0x4B,
+    0xB4
+    //A0
+    ,
+    0x86,
+    0x65,
+    0x18,
+    0xAE,
+    0x67,
+    0x1C,
+    0xA6,
+    0x63,
+    0x19,
+    0x96,
+    0x23,
+    0x19,
+    0x84,
+    0x13,
+    8,
+    0xA6,
+    0x52,
+    0xAC,
+    0xCA,
+    0x22,
+    0x89,
+    0x6E,
+    0xAB,
+    0x19,
+    0x8C,
+    0x62,
+    0x34,
+    0xC4,
+    0x62,
+    0x19,
+    0x86,
+    0x63
+    //C0
+    ,
+    0x18,
+    0xC4,
+    0x23,
+    0x58,
+    0xD6,
+    0xA3,
+    0x50,
+    0x42,
+    0x54,
+    0x4A,
+    0xAD,
+    0x4A,
+    0x25,
+    0x11,
+    0x6B,
+    0x64,
+    0x89,
+    0x4A,
+    0x63,
+    0x39,
+    0x8A,
+    0x23,
+    0x31,
+    0x2A,
+    0xEA,
+    0xA2,
+    0xA9,
+    0x44,
+    0xC5,
+    0x12,
+    0xCD,
+    0x42
+    //E0
+    ,
+    0x34,
+    0x8C,
+    0x62,
+    0x18,
+    0x8C,
+    0x63,
+    0x11,
+    0x48,
+    0x66,
+    0x31,
+    0x9D,
+    0x44,
+    0x33,
+    0x1D,
+    0x46,
+    0x31,
+    0x9C,
+    0xC6,
+    0xB1,
+    0xC,
+    0xCD,
+    0x32,
+    0x88,
+    0xC4,
+    0x73,
+    0x18,
+    0x86,
+    0x73,
+    8,
+    0xD6,
+    0x63,
+    0x58
+    //100
+    ,
+    7,
+    0x81,
+    0xE0,
+    0xF0,
+    0x3C,
+    7,
+    0x87,
+    0x90,
+    0x3C,
+    0x7C,
+    0xF,
+    0xC7,
+    0xC0,
+    0xC0,
+    0xF0,
+    0x7C,
+    0x1E,
+    7,
+    0x80,
+    0x80,
+    0,
+    0x1C,
+    0x78,
+    0x70,
+    0xF1,
+    0xC7,
+    0x1F,
+    0xC0,
+    0xC,
+    0xFE,
+    0x1C,
+    0x1F
+    //120
+    ,
+    0x1F,
+    0xE,
+    0xA,
+    0x7A,
+    0xC0,
+    0x71,
+    0xF2,
+    0x83,
+    0x8F,
+    3,
+    0xF,
+    0xF,
+    0xC,
+    0,
+    0x79,
+    0xF8,
+    0x61,
+    0xE0,
+    0x43,
+    0xF,
+    0x83,
+    0xE7,
+    0x18,
+    0xF9,
+    0xC1,
+    0x13,
+    0xDA,
+    0xE9,
+    0x63,
+    0x8F,
+    0xF,
+    0x83
+    //140
+    ,
+    0x83,
+    0x87,
+    0xC3,
+    0x1F,
+    0x3C,
+    0x70,
+    0xF0,
+    0xE1,
+    0xE1,
+    0xE3,
+    0x87,
+    0xB8,
+    0x71,
+    0xE,
+    0x20,
+    0xE3,
+    0x8D,
+    0x48,
+    0x78,
+    0x1C,
+    0x93,
+    0x87,
+    0x30,
+    0xE1,
+    0xC1,
+    0xC1,
+    0xE4,
+    0x78,
+    0x21,
+    0x83,
+    0x83,
+    0xC3
+    //160
+    ,
+    0x87,
+    6,
+    0x39,
+    0xE5,
+    0xC3,
+    0x87,
+    7,
+    0xE,
+    0x1C,
+    0x1C,
+    0x70,
+    0xF4,
+    0x71,
+    0x9C,
+    0x60,
+    0x36,
+    0x32,
+    0xC3,
+    0x1E,
+    0x3C,
+    0xF3,
+    0x8F,
+    0xE,
+    0x3C,
+    0x70,
+    0xE3,
+    0xC7,
+    0x8F,
+    0xF,
+    0xF,
+    0xE,
+    0x3C
+    //180
+    ,
+    0x78,
+    0xF0,
+    0xE3,
+    0x87,
+    6,
+    0xF0,
+    0xE3,
+    7,
+    0xC1,
+    0x99,
+    0x87,
+    0xF,
+    0x18,
+    0x78,
+    0x70,
+    0x70,
+    0xFC,
+    0xF3,
+    0x10,
+    0xB1,
+    0x8C,
+    0x8C,
+    0x31,
+    0x7C,
+    0x70,
+    0xE1,
+    0x86,
+    0x3C,
+    0x64,
+    0x6C,
+    0xB0,
+    0xE1
+    //1A0
+    ,
+    0xE3,
+    0xF,
+    0x23,
+    0x8F,
+    0xF,
+    0x1E,
+    0x3E,
+    0x38,
+    0x3C,
+    0x38,
+    0x7B,
+    0x8F,
+    7,
+    0xE,
+    0x3C,
+    0xF4,
+    0x17,
+    0x1E,
+    0x3C,
+    0x78,
+    0xF2,
+    0x9E,
+    0x72,
+    0x49,
+    0xE3,
+    0x25,
+    0x36,
+    0x38,
+    0x58,
+    0x39,
+    0xE2,
+    0xDE
+    //1C0
+    ,
+    0x3C,
+    0x78,
+    0x78,
+    0xE1,
+    0xC7,
+    0x61,
+    0xE1,
+    0xE1,
+    0xB0,
+    0xF0,
+    0xF0,
+    0xC3,
+    0xC7,
+    0xE,
+    0x38,
+    0xC0,
+    0xF0,
+    0xCE,
+    0x73,
+    0x73,
+    0x18,
+    0x34,
+    0xB0,
+    0xE1,
+    0xC7,
+    0x8E,
+    0x1C,
+    0x3C,
+    0xF8,
+    0x38,
+    0xF0,
+    0xE1
+    //1E0
+    ,
+    0xC1,
+    0x8B,
+    0x86,
+    0x8F,
+    0x1C,
+    0x78,
+    0x70,
+    0xF0,
+    0x78,
+    0xAC,
+    0xB1,
+    0x8F,
+    0x39,
+    0x31,
+    0xDB,
+    0x38,
+    0x61,
+    0xC3,
+    0xE,
+    0xE,
+    0x38,
+    0x78,
+    0x73,
+    0x17,
+    0x1E,
+    0x39,
+    0x1E,
+    0x38,
+    0x64,
+    0xE1,
+    0xF1,
+    0xC1
+    //200
+    ,
+    0x4E,
+    0xF,
+    0x40,
+    0xA2,
+    2,
+    0xC5,
+    0x8F,
+    0x81,
+    0xA1,
+    0xFC,
+    0x12,
+    8,
+    0x64,
+    0xE0,
+    0x3C,
+    0x22,
+    0xE0,
+    0x45,
+    7,
+    0x8E,
+    0xC,
+    0x32,
+    0x90,
+    0xF0,
+    0x1F,
+    0x20,
+    0x49,
+    0xE0,
+    0xF8,
+    0xC,
+    0x60,
+    0xF0
+    //220
+    ,
+    0x17,
+    0x1A,
+    0x41,
+    0xAA,
+    0xA4,
+    0xD0,
+    0x8D,
+    0x12,
+    0x82,
+    0x1E,
+    0x1E,
+    3,
+    0xF8,
+    0x3E,
+    3,
+    0xC,
+    0x73,
+    0x80,
+    0x70,
+    0x44,
+    0x26,
+    3,
+    0x24,
+    0xE1,
+    0x3E,
+    4,
+    0x4E,
+    4,
+    0x1C,
+    0xC1,
+    9,
+    0xCC
+    //240
+    ,
+    0x9E,
+    0x90,
+    0x21,
+    7,
+    0x90,
+    0x43,
+    0x64,
+    0xC0,
+    0xF,
+    0xC6,
+    0x90,
+    0x9C,
+    0xC1,
+    0x5B,
+    3,
+    0xE2,
+    0x1D,
+    0x81,
+    0xE0,
+    0x5E,
+    0x1D,
+    3,
+    0x84,
+    0xB8,
+    0x2C,
+    0xF,
+    0x80,
+    0xB1,
+    0x83,
+    0xE0,
+    0x30,
+    0x41
+    //260
+    ,
+    0x1E,
+    0x43,
+    0x89,
+    0x83,
+    0x50,
+    0xFC,
+    0x24,
+    0x2E,
+    0x13,
+    0x83,
+    0xF1,
+    0x7C,
+    0x4C,
+    0x2C,
+    0xC9,
+    0xD,
+    0x83,
+    0xB0,
+    0xB5,
+    0x82,
+    0xE4,
+    0xE8,
+    6,
+    0x9C,
+    7,
+    0xA0,
+    0x99,
+    0x1D,
+    7,
+    0x3E,
+    0x82,
+    0x8F
+    //280
+    ,
+    0x70,
+    0x30,
+    0x74,
+    0x40,
+    0xCA,
+    0x10,
+    0xE4,
+    0xE8,
+    0xF,
+    0x92,
+    0x14,
+    0x3F,
+    6,
+    0xF8,
+    0x84,
+    0x88,
+    0x43,
+    0x81,
+    0xA,
+    0x34,
+    0x39,
+    0x41,
+    0xC6,
+    0xE3,
+    0x1C,
+    0x47,
+    3,
+    0xB0,
+    0xB8,
+    0x13,
+    0xA,
+    0xC2
+    //2A0
+    ,
+    0x64,
+    0xF8,
+    0x18,
+    0xF9,
+    0x60,
+    0xB3,
+    0xC0,
+    0x65,
+    0x20,
+    0x60,
+    0xA6,
+    0x8C,
+    0xC3,
+    0x81,
+    0x20,
+    0x30,
+    0x26,
+    0x1E,
+    0x1C,
+    0x38,
+    0xD3,
+    1,
+    0xB0,
+    0x26,
+    0x40,
+    0xF4,
+    0xB,
+    0xC3,
+    0x42,
+    0x1F,
+    0x85,
+    0x32
+    //2C0
+    ,
+    0x26,
+    0x60,
+    0x40,
+    0xC9,
+    0xCB,
+    1,
+    0xEC,
+    0x11,
+    0x28,
+    0x40,
+    0xFA,
+    4,
+    0x34,
+    0xE0,
+    0x70,
+    0x4C,
+    0x8C,
+    0x1D,
+    7,
+    0x69,
+    3,
+    0x16,
+    0xC8,
+    4,
+    0x23,
+    0xE8,
+    0xC6,
+    0x9A,
+    0xB,
+    0x1A,
+    3,
+    0xE0
+    //2E0
+    ,
+    0x76,
+    6,
+    5,
+    0xCF,
+    0x1E,
+    0xBC,
+    0x58,
+    0x31,
+    0x71,
+    0x66,
+    0,
+    0xF8,
+    0x3F,
+    4,
+    0xFC,
+    0xC,
+    0x74,
+    0x27,
+    0x8A,
+    0x80,
+    0x71,
+    0xC2,
+    0x3A,
+    0x26,
+    6,
+    0xC0,
+    0x1F,
+    5,
+    0xF,
+    0x98,
+    0x40,
+    0xAE
+    //300
+    ,
+    1,
+    0x7F,
+    0xC0,
+    7,
+    0xFF,
+    0,
+    0xE,
+    0xFE,
+    0,
+    3,
+    0xDF,
+    0x80,
+    3,
+    0xEF,
+    0x80,
+    0x1B,
+    0xF1,
+    0xC2,
+    0,
+    0xE7,
+    0xE0,
+    0x18,
+    0xFC,
+    0xE0,
+    0x21,
+    0xFC,
+    0x80,
+    0x3C,
+    0xFC,
+    0x40,
+    0xE,
+    0x7E
+    //320
+    ,
+    0,
+    0x3F,
+    0x3E,
+    0,
+    0xF,
+    0xFE,
+    0,
+    0x1F,
+    0xFF,
+    0,
+    0x3E,
+    0xF0,
+    7,
+    0xFC,
+    0,
+    0x7E,
+    0x10,
+    0x3F,
+    0xFF,
+    0,
+    0x3F,
+    0x38,
+    0xE,
+    0x7C,
+    1,
+    0x87,
+    0xC,
+    0xFC,
+    0xC7,
+    0,
+    0x3E,
+    4
+    //340
+    ,
+    0xF,
+    0x3E,
+    0x1F,
+    0xF,
+    0xF,
+    0x1F,
+    0xF,
+    2,
+    0x83,
+    0x87,
+    0xCF,
+    3,
+    0x87,
+    0xF,
+    0x3F,
+    0xC0,
+    7,
+    0x9E,
+    0x60,
+    0x3F,
+    0xC0,
+    3,
+    0xFE,
+    0,
+    0x3F,
+    0xE0,
+    0x77,
+    0xE1,
+    0xC0,
+    0xFE,
+    0xE0,
+    0xC3
+    //360
+    ,
+    0xE0,
+    1,
+    0xDF,
+    0xF8,
+    3,
+    7,
+    0,
+    0x7E,
+    0x70,
+    0,
+    0x7C,
+    0x38,
+    0x18,
+    0xFE,
+    0xC,
+    0x1E,
+    0x78,
+    0x1C,
+    0x7C,
+    0x3E,
+    0xE,
+    0x1F,
+    0x1E,
+    0x1E,
+    0x3E,
+    0,
+    0x7F,
+    0x83,
+    7,
+    0xDB,
+    0x87,
+    0x83
+    //380
+    ,
+    7,
+    0xC7,
+    7,
+    0x10,
+    0x71,
+    0xFF,
+    0,
+    0x3F,
+    0xE2,
+    1,
+    0xE0,
+    0xC1,
+    0xC3,
+    0xE1,
+    0,
+    0x7F,
+    0xC0,
+    5,
+    0xF0,
+    0x20,
+    0xF8,
+    0xF0,
+    0x70,
+    0xFE,
+    0x78,
+    0x79,
+    0xF8,
+    2,
+    0x3F,
+    0xC,
+    0x8F,
+    3
+    //3a0
+    ,
+    0xF,
+    0x9F,
+    0xE0,
+    0xC1,
+    0xC7,
+    0x87,
+    3,
+    0xC3,
+    0xC3,
+    0xB0,
+    0xE1,
+    0xE1,
+    0xC1,
+    0xE3,
+    0xE0,
+    0x71,
+    0xF0,
+    0,
+    0xFC,
+    0x70,
+    0x7C,
+    0xC,
+    0x3E,
+    0x38,
+    0xE,
+    0x1C,
+    0x70,
+    0xC3,
+    0xC7,
+    3,
+    0x81,
+    0xC1
+    //3c0
+    ,
+    0xC7,
+    0xE7,
+    0,
+    0xF,
+    0xC7,
+    0x87,
+    0x19,
+    9,
+    0xEF,
+    0xC4,
+    0x33,
+    0xE0,
+    0xC1,
+    0xFC,
+    0xF8,
+    0x70,
+    0xF0,
+    0x78,
+    0xF8,
+    0xF0,
+    0x61,
+    0xC7,
+    0,
+    0x1F,
+    0xF8,
+    1,
+    0x7C,
+    0xF8,
+    0xF0,
+    0x78,
+    0x70,
+    0x3C
+    //3e0
+    ,
+    0x7C,
+    0xCE,
+    0xE,
+    0x21,
+    0x83,
+    0xCF,
+    8,
+    7,
+    0x8F,
+    8,
+    0xC1,
+    0x87,
+    0x8F,
+    0x80,
+    0xC7,
+    0xE3,
+    0,
+    7,
+    0xF8,
+    0xE0,
+    0xEF,
+    0,
+    0x39,
+    0xF7,
+    0x80,
+    0xE,
+    0xF8,
+    0xE1,
+    0xE3,
+    0xF8,
+    0x21,
+    0x9F
+    //400
+    ,
+    0xC0,
+    0xFF,
+    3,
+    0xF8,
+    7,
+    0xC0,
+    0x1F,
+    0xF8,
+    0xC4,
+    4,
+    0xFC,
+    0xC4,
+    0xC1,
+    0xBC,
+    0x87,
+    0xF0,
+    0xF,
+    0xC0,
+    0x7F,
+    5,
+    0xE0,
+    0x25,
+    0xEC,
+    0xC0,
+    0x3E,
+    0x84,
+    0x47,
+    0xF0,
+    0x8E,
+    3,
+    0xF8,
+    3
+    //420
+    ,
+    0xFB,
+    0xC0,
+    0x19,
+    0xF8,
+    7,
+    0x9C,
+    0xC,
+    0x17,
+    0xF8,
+    7,
+    0xE0,
+    0x1F,
+    0xA1,
+    0xFC,
+    0xF,
+    0xFC,
+    1,
+    0xF0,
+    0x3F,
+    0,
+    0xFE,
+    3,
+    0xF0,
+    0x1F,
+    0,
+    0xFD,
+    0,
+    0xFF,
+    0x88,
+    0xD,
+    0xF9,
+    1
+    //440
+    ,
+    0xFF,
+    0,
+    0x70,
+    7,
+    0xC0,
+    0x3E,
+    0x42,
+    0xF3,
+    0xD,
+    0xC4,
+    0x7F,
+    0x80,
+    0xFC,
+    7,
+    0xF0,
+    0x5E,
+    0xC0,
+    0x3F,
+    0,
+    0x78,
+    0x3F,
+    0x81,
+    0xFF,
+    1,
+    0xF8,
+    1,
+    0xC3,
+    0xE8,
+    0xC,
+    0xE4,
+    0x64,
+    0x8F
+    ////460
+    ,
+    0xE4,
+    0xF,
+    0xF0,
+    7,
+    0xF0,
+    0xC2,
+    0x1F,
+    0,
+    0x7F,
+    0xC0,
+    0x6F,
+    0x80,
+    0x7E,
+    3,
+    0xF8,
+    7,
+    0xF0,
+    0x3F,
+    0xC0,
+    0x78,
+    0xF,
+    0x82,
+    7,
+    0xFE,
+    0x22,
+    0x77,
+    0x70,
+    2,
+    0x76,
+    3,
+    0xFE,
+    0
+    //480
+    ,
+    0xFE,
+    0x67,
+    0,
+    0x7C,
+    0xC7,
+    0xF1,
+    0x8E,
+    0xC6,
+    0x3B,
+    0xE0,
+    0x3F,
+    0x84,
+    0xF3,
+    0x19,
+    0xD8,
+    3,
+    0x99,
+    0xFC,
+    9,
+    0xB8,
+    0xF,
+    0xF8,
+    0,
+    0x9D,
+    0x24,
+    0x61,
+    0xF9,
+    0xD,
+    0,
+    0xFD,
+    3,
+    0xF0
+    //4a0
+    ,
+    0x1F,
+    0x90,
+    0x3F,
+    1,
+    0xF8,
+    0x1F,
+    0xD0,
+    0xF,
+    0xF8,
+    0x37,
+    1,
+    0xF8,
+    7,
+    0xF0,
+    0xF,
+    0xC0,
+    0x3F,
+    0,
+    0xFE,
+    3,
+    0xF8,
+    0xF,
+    0xC0,
+    0x3F,
+    0,
+    0xFA,
+    3,
+    0xF0,
+    0xF,
+    0x80,
+    0xFF,
+    1
+    //4c0
+    ,
+    0xB8,
+    7,
+    0xF0,
+    1,
+    0xFC,
+    1,
+    0xBC,
+    0x80,
+    0x13,
+    0x1E,
+    0,
+    0x7F,
+    0xE1,
+    0x40,
+    0x7F,
+    0xA0,
+    0x7F,
+    0xB0,
+    0,
+    0x3F,
+    0xC0,
+    0x1F,
+    0xC0,
+    0x38,
+    0xF,
+    0xF0,
+    0x1F,
+    0x80,
+    0xFF,
+    1,
+    0xFC,
+    3
+    //4e0
+    ,
+    0xF1,
+    0x7E,
+    1,
+    0xFE,
+    1,
+    0xF0,
+    0xFF,
+    0,
+    0x7F,
+    0xC0,
+    0x1D,
+    7,
+    0xF0,
+    0xF,
+    0xC0,
+    0x7E,
+    6,
+    0xE0,
+    7,
+    0xE0,
+    0xF,
+    0xF8,
+    6,
+    0xC1,
+    0xFE,
+    1,
+    0xFC,
+    3,
+    0xE0,
+    0xF,
+    0,
+    0xFC};
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           Render
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+unsigned char pitches[256]; // tab43008
+
+unsigned char frequency1[256];
+unsigned char frequency2[256];
+unsigned char frequency3[256];
+
+unsigned char amplitude1[256];
+unsigned char amplitude2[256];
+unsigned char amplitude3[256];
+
+unsigned char sampledConsonantFlag[256]; // tab44800
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           Sam
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+unsigned char stress[256]; //numbers from 0 to 8
+unsigned char phonemeLength[256]; //tab40160
+unsigned char phonemeindex[256];
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           ReciterTabs
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+//some flags
+const unsigned char tab36376[] = {
+    0,   0,   0,   0,   0,   0,   0,   0, // 0-7
+    0,   0,   0,   0,   0,   0,   0,   0, // 8-15
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   2,   2,   2,   2,   2,   2,   130, // ' ', '!'
+    0,   0,   2,   2,   2,   2,   2,   2,   3,   3,   3,   3,   3,   3,   3,   3,
+    3,   3,   2,   2,   2,   2,   2,   2,   2,   192, 168, 176, 172, 192, 160, 184, // '@', 'A'
+    160, 192, 188, 160, 172, 168, 172, 192, 160, 160, 172, 180, 164, 192, 168, 168,
+    176, 192, 188, 0,   0,   0,   2,   0, // 'X', 'Y', 'Z', '[',
+    32,  32,  155, 32,  192, 185, 32,  205, 163, 76,  138, 142};
+
+const unsigned char rules[] = {
+    ']',        'A' | 0x80, ' ',        '(',        'A',        '.',        ')',        '=',
+    'E',        'H',        '4',        'Y',        '.',        ' ' | 0x80, '(',        'A',
+    ')',        ' ',        '=',        'A',        'H' | 0x80, ' ',        '(',        'A',
+    'R',        'E',        ')',        ' ',        '=',        'A',        'A',        'R' | 0x80,
+    ' ',        '(',        'A',        'R',        ')',        'O',        '=',        'A',
+    'X',        'R' | 0x80, '(',        'A',        'R',        ')',        '#',        '=',
+    'E',        'H',        '4',        'R' | 0x80, ' ',        '^',        '(',        'A',
+    'S',        ')',        '#',        '=',        'E',        'Y',        '4',        'S' | 0x80,
+    '(',        'A',        ')',        'W',        'A',        '=',        'A',        'X' | 0x80,
+    '(',        'A',        'W',        ')',        '=',        'A',        'O',        '5' | 0x80,
+    ' ',        ':',        '(',        'A',        'N',        'Y',        ')',        '=',
+    'E',        'H',        '4',        'N',        'I',        'Y' | 0x80, '(',        'A',
+    ')',        '^',        '+',        '#',        '=',        'E',        'Y',        '5' | 0x80,
+    '#',        ':',        '(',        'A',        'L',        'L',        'Y',        ')',
+    '=',        'U',        'L',        'I',        'Y' | 0x80, ' ',        '(',        'A',
+    'L',        ')',        '#',        '=',        'U',        'L' | 0x80, '(',        'A',
+    'G',        'A',        'I',        'N',        ')',        '=',        'A',        'X',
+    'G',        'E',        'H',        '4',        'N' | 0x80, '#',        ':',        '(',
+    'A',        'G',        ')',        'E',        '=',        'I',        'H',        'J' | 0x80,
+    '(',        'A',        ')',        '^',        '%',        '=',        'E',        'Y' | 0x80,
+    '(',        'A',        ')',        '^',        '+',        ':',        '#',        '=',
+    'A',        'E' | 0x80, ' ',        ':',        '(',        'A',        ')',        '^',
+    '+',        ' ',        '=',        'E',        'Y',        '4' | 0x80, ' ',        '(',
+    'A',        'R',        'R',        ')',        '=',        'A',        'X',        'R' | 0x80,
+    '(',        'A',        'R',        'R',        ')',        '=',        'A',        'E',
+    '4',        'R' | 0x80, ' ',        '^',        '(',        'A',        'R',        ')',
+    ' ',        '=',        'A',        'A',        '5',        'R' | 0x80, '(',        'A',
+    'R',        ')',        '=',        'A',        'A',        '5',        'R' | 0x80, '(',
+    'A',        'I',        'R',        ')',        '=',        'E',        'H',        '4',
+    'R' | 0x80, '(',        'A',        'I',        ')',        '=',        'E',        'Y',
+    '4' | 0x80, '(',        'A',        'Y',        ')',        '=',        'E',        'Y',
+    '5' | 0x80, '(',        'A',        'U',        ')',        '=',        'A',        'O',
+    '4' | 0x80, '#',        ':',        '(',        'A',        'L',        ')',        ' ',
+    '=',        'U',        'L' | 0x80, '#',        ':',        '(',        'A',        'L',
+    'S',        ')',        ' ',        '=',        'U',        'L',        'Z' | 0x80, '(',
+    'A',        'L',        'K',        ')',        '=',        'A',        'O',        '4',
+    'K' | 0x80, '(',        'A',        'L',        ')',        '^',        '=',        'A',
+    'O',        'L' | 0x80, ' ',        ':',        '(',        'A',        'B',        'L',
+    'E',        ')',        '=',        'E',        'Y',        '4',        'B',        'U',
+    'L' | 0x80, '(',        'A',        'B',        'L',        'E',        ')',        '=',
+    'A',        'X',        'B',        'U',        'L' | 0x80, '(',        'A',        ')',
+    'V',        'O',        '=',        'E',        'Y',        '4' | 0x80, '(',        'A',
+    'N',        'G',        ')',        '+',        '=',        'E',        'Y',        '4',
+    'N',        'J' | 0x80, '(',        'A',        'T',        'A',        'R',        'I',
+    ')',        '=',        'A',        'H',        'T',        'A',        'A',        '4',
+    'R',        'I',        'Y' | 0x80, '(',        'A',        ')',        'T',        'O',
+    'M',        '=',        'A',        'E' | 0x80, '(',        'A',        ')',        'T',
+    'T',        'I',        '=',        'A',        'E' | 0x80, ' ',        '(',        'A',
+    'T',        ')',        ' ',        '=',        'A',        'E',        'T' | 0x80, ' ',
+    '(',        'A',        ')',        'T',        '=',        'A',        'H' | 0x80, '(',
+    'A',        ')',        '=',        'A',        'E' | 0x80,
+
+    ']',        'B' | 0x80, ' ',        '(',        'B',        ')',        ' ',        '=',
+    'B',        'I',        'Y',        '4' | 0x80, ' ',        '(',        'B',        'E',
+    ')',        '^',        '#',        '=',        'B',        'I',        'H' | 0x80, '(',
+    'B',        'E',        'I',        'N',        'G',        ')',        '=',        'B',
+    'I',        'Y',        '4',        'I',        'H',        'N',        'X' | 0x80, ' ',
+    '(',        'B',        'O',        'T',        'H',        ')',        ' ',        '=',
+    'B',        'O',        'W',        '4',        'T',        'H' | 0x80, ' ',        '(',
+    'B',        'U',        'S',        ')',        '#',        '=',        'B',        'I',
+    'H',        '4',        'Z' | 0x80, '(',        'B',        'R',        'E',        'A',
+    'K',        ')',        '=',        'B',        'R',        'E',        'Y',        '5',
+    'K' | 0x80, '(',        'B',        'U',        'I',        'L',        ')',        '=',
+    'B',        'I',        'H',        '4',        'L' | 0x80, '(',        'B',        ')',
+    '=',        'B' | 0x80,
+
+    ']',        'C' | 0x80, ' ',        '(',        'C',        ')',        ' ',        '=',
+    'S',        'I',        'Y',        '4' | 0x80, ' ',        '(',        'C',        'H',
+    ')',        '^',        '=',        'K' | 0x80, '^',        'E',        '(',        'C',
+    'H',        ')',        '=',        'K' | 0x80, '(',        'C',        'H',        'A',
+    ')',        'R',        '#',        '=',        'K',        'E',        'H',        '5' | 0x80,
+    '(',        'C',        'H',        ')',        '=',        'C',        'H' | 0x80, ' ',
+    'S',        '(',        'C',        'I',        ')',        '#',        '=',        'S',
+    'A',        'Y',        '4' | 0x80, '(',        'C',        'I',        ')',        'A',
+    '=',        'S',        'H' | 0x80, '(',        'C',        'I',        ')',        'O',
+    '=',        'S',        'H' | 0x80, '(',        'C',        'I',        ')',        'E',
+    'N',        '=',        'S',        'H' | 0x80, '(',        'C',        'I',        'T',
+    'Y',        ')',        '=',        'S',        'I',        'H',        'T',        'I',
+    'Y' | 0x80, '(',        'C',        ')',        '+',        '=',        'S' | 0x80, '(',
+    'C',        'K',        ')',        '=',        'K' | 0x80, '(',        'C',        'O',
+    'M',        'M',        'O',        'D',        'O',        'R',        'E',        ')',
+    '=',        'K',        'A',        'A',        '4',        'M',        'A',        'H',
+    'D',        'O',        'H',        'R' | 0x80, '(',        'C',        'O',        'M',
+    ')',        '=',        'K',        'A',        'H',        'M' | 0x80, '(',        'C',
+    'U',        'I',        'T',        ')',        '=',        'K',        'I',        'H',
+    'T' | 0x80, '(',        'C',        'R',        'E',        'A',        ')',        '=',
+    'K',        'R',        'I',        'Y',        'E',        'Y' | 0x80, '(',        'C',
+    ')',        '=',        'K' | 0x80,
+
+    ']',        'D' | 0x80, ' ',        '(',        'D',        ')',        ' ',        '=',
+    'D',        'I',        'Y',        '4' | 0x80, ' ',        '(',        'D',        'R',
+    '.',        ')',        ' ',        '=',        'D',        'A',        'A',        '4',
+    'K',        'T',        'E',        'R' | 0x80, '#',        ':',        '(',        'D',
+    'E',        'D',        ')',        ' ',        '=',        'D',        'I',        'H',
+    'D' | 0x80, '.',        'E',        '(',        'D',        ')',        ' ',        '=',
+    'D' | 0x80, '#',        ':',        '^',        'E',        '(',        'D',        ')',
+    ' ',        '=',        'T' | 0x80, ' ',        '(',        'D',        'E',        ')',
+    '^',        '#',        '=',        'D',        'I',        'H' | 0x80, ' ',        '(',
+    'D',        'O',        ')',        ' ',        '=',        'D',        'U',        'W' | 0x80,
+    ' ',        '(',        'D',        'O',        'E',        'S',        ')',        '=',
+    'D',        'A',        'H',        'Z' | 0x80, '(',        'D',        'O',        'N',
+    'E',        ')',        ' ',        '=',        'D',        'A',        'H',        '5',
+    'N' | 0x80, '(',        'D',        'O',        'I',        'N',        'G',        ')',
+    '=',        'D',        'U',        'W',        '4',        'I',        'H',        'N',
+    'X' | 0x80, ' ',        '(',        'D',        'O',        'W',        ')',        '=',
+    'D',        'A',        'W' | 0x80, '#',        '(',        'D',        'U',        ')',
+    'A',        '=',        'J',        'U',        'W' | 0x80, '#',        '(',        'D',
+    'U',        ')',        '^',        '#',        '=',        'J',        'A',        'X' | 0x80,
+    '(',        'D',        ')',        '=',        'D' | 0x80,
+
+    ']',        'E' | 0x80, ' ',        '(',        'E',        ')',        ' ',        '=',
+    'I',        'Y',        'I',        'Y',        '4' | 0x80, '#',        ':',        '(',
+    'E',        ')',        ' ',        '=' | 0x80, '\'',       ':',        '^',        '(',
+    'E',        ')',        ' ',        '=' | 0x80, ' ',        ':',        '(',        'E',
+    ')',        ' ',        '=',        'I',        'Y' | 0x80, '#',        '(',        'E',
+    'D',        ')',        ' ',        '=',        'D' | 0x80, '#',        ':',        '(',
+    'E',        ')',        'D',        ' ',        '=' | 0x80, '(',        'E',        'V',
+    ')',        'E',        'R',        '=',        'E',        'H',        '4',        'V' | 0x80,
+    '(',        'E',        ')',        '^',        '%',        '=',        'I',        'Y',
+    '4' | 0x80, '(',        'E',        'R',        'I',        ')',        '#',        '=',
+    'I',        'Y',        '4',        'R',        'I',        'Y' | 0x80, '(',        'E',
+    'R',        'I',        ')',        '=',        'E',        'H',        '4',        'R',
+    'I',        'H' | 0x80, '#',        ':',        '(',        'E',        'R',        ')',
+    '#',        '=',        'E',        'R' | 0x80, '(',        'E',        'R',        'R',
+    'O',        'R',        ')',        '=',        'E',        'H',        '4',        'R',
+    'O',        'H',        'R' | 0x80, '(',        'E',        'R',        'A',        'S',
+    'E',        ')',        '=',        'I',        'H',        'R',        'E',        'Y',
+    '5',        'S' | 0x80, '(',        'E',        'R',        ')',        '#',        '=',
+    'E',        'H',        'R' | 0x80, '(',        'E',        'R',        ')',        '=',
+    'E',        'R' | 0x80, ' ',        '(',        'E',        'V',        'E',        'N',
+    ')',        '=',        'I',        'Y',        'V',        'E',        'H',        'N' | 0x80,
+    '#',        ':',        '(',        'E',        ')',        'W',        '=' | 0x80, '@',
+    '(',        'E',        'W',        ')',        '=',        'U',        'W' | 0x80, '(',
+    'E',        'W',        ')',        '=',        'Y',        'U',        'W' | 0x80, '(',
+    'E',        ')',        'O',        '=',        'I',        'Y' | 0x80, '#',        ':',
+    '&',        '(',        'E',        'S',        ')',        ' ',        '=',        'I',
+    'H',        'Z' | 0x80, '#',        ':',        '(',        'E',        ')',        'S',
+    ' ',        '=' | 0x80, '#',        ':',        '(',        'E',        'L',        'Y',
+    ')',        ' ',        '=',        'L',        'I',        'Y' | 0x80, '#',        ':',
+    '(',        'E',        'M',        'E',        'N',        'T',        ')',        '=',
+    'M',        'E',        'H',        'N',        'T' | 0x80, '(',        'E',        'F',
+    'U',        'L',        ')',        '=',        'F',        'U',        'H',        'L' | 0x80,
+    '(',        'E',        'E',        ')',        '=',        'I',        'Y',        '4' | 0x80,
+    '(',        'E',        'A',        'R',        'N',        ')',        '=',        'E',
+    'R',        '5',        'N' | 0x80, ' ',        '(',        'E',        'A',        'R',
+    ')',        '^',        '=',        'E',        'R',        '5' | 0x80, '(',        'E',
+    'A',        'D',        ')',        '=',        'E',        'H',        'D' | 0x80, '#',
+    ':',        '(',        'E',        'A',        ')',        ' ',        '=',        'I',
+    'Y',        'A',        'X' | 0x80, '(',        'E',        'A',        ')',        'S',
+    'U',        '=',        'E',        'H',        '5' | 0x80, '(',        'E',        'A',
+    ')',        '=',        'I',        'Y',        '5' | 0x80, '(',        'E',        'I',
+    'G',        'H',        ')',        '=',        'E',        'Y',        '4' | 0x80, '(',
+    'E',        'I',        ')',        '=',        'I',        'Y',        '4' | 0x80, ' ',
+    '(',        'E',        'Y',        'E',        ')',        '=',        'A',        'Y',
+    '4' | 0x80, '(',        'E',        'Y',        ')',        '=',        'I',        'Y' | 0x80,
+    '(',        'E',        'U',        ')',        '=',        'Y',        'U',        'W',
+    '5' | 0x80, '(',        'E',        'Q',        'U',        'A',        'L',        ')',
+    '=',        'I',        'Y',        '4',        'K',        'W',        'U',        'L' | 0x80,
+    '(',        'E',        ')',        '=',        'E',        'H' | 0x80,
+
+    ']',        'F' | 0x80, ' ',        '(',        'F',        ')',        ' ',        '=',
+    'E',        'H',        '4',        'F' | 0x80, '(',        'F',        'U',        'L',
+    ')',        '=',        'F',        'U',        'H',        'L' | 0x80, '(',        'F',
+    'R',        'I',        'E',        'N',        'D',        ')',        '=',        'F',
+    'R',        'E',        'H',        '5',        'N',        'D' | 0x80, '(',        'F',
+    'A',        'T',        'H',        'E',        'R',        ')',        '=',        'F',
+    'A',        'A',        '4',        'D',        'H',        'E',        'R' | 0x80, '(',
+    'F',        ')',        'F',        '=' | 0x80, '(',        'F',        ')',        '=',
+    'F' | 0x80,
+
+    ']',        'G' | 0x80, ' ',        '(',        'G',        ')',        ' ',        '=',
+    'J',        'I',        'Y',        '4' | 0x80, '(',        'G',        'I',        'V',
+    ')',        '=',        'G',        'I',        'H',        '5',        'V' | 0x80, ' ',
+    '(',        'G',        ')',        'I',        '^',        '=',        'G' | 0x80, '(',
+    'G',        'E',        ')',        'T',        '=',        'G',        'E',        'H',
+    '5' | 0x80, 'S',        'U',        '(',        'G',        'G',        'E',        'S',
+    ')',        '=',        'G',        'J',        'E',        'H',        '4',        'S' | 0x80,
+    '(',        'G',        'G',        ')',        '=',        'G' | 0x80, ' ',        'B',
+    '#',        '(',        'G',        ')',        '=',        'G' | 0x80, '(',        'G',
+    ')',        '+',        '=',        'J' | 0x80, '(',        'G',        'R',        'E',
+    'A',        'T',        ')',        '=',        'G',        'R',        'E',        'Y',
+    '4',        'T' | 0x80, '(',        'G',        'O',        'N',        ')',        'E',
+    '=',        'G',        'A',        'O',        '5',        'N' | 0x80, '#',        '(',
+    'G',        'H',        ')',        '=' | 0x80, ' ',        '(',        'G',        'N',
+    ')',        '=',        'N' | 0x80, '(',        'G',        ')',        '=',        'G' | 0x80,
+
+    ']',        'H' | 0x80, ' ',        '(',        'H',        ')',        ' ',        '=',
+    'E',        'Y',        '4',        'C',        'H' | 0x80, ' ',        '(',        'H',
+    'A',        'V',        ')',        '=',        '/',        'H',        'A',        'E',
+    '6',        'V' | 0x80, ' ',        '(',        'H',        'E',        'R',        'E',
+    ')',        '=',        '/',        'H',        'I',        'Y',        'R' | 0x80, ' ',
+    '(',        'H',        'O',        'U',        'R',        ')',        '=',        'A',
+    'W',        '5',        'E',        'R' | 0x80, '(',        'H',        'O',        'W',
+    ')',        '=',        '/',        'H',        'A',        'W' | 0x80, '(',        'H',
+    ')',        '#',        '=',        '/',        'H' | 0x80, '(',        'H',        ')',
+    '=' | 0x80,
+
+    ']',        'I' | 0x80, ' ',        '(',        'I',        'N',        ')',        '=',
+    'I',        'H',        'N' | 0x80, ' ',        '(',        'I',        ')',        ' ',
+    '=',        'A',        'Y',        '4' | 0x80, '(',        'I',        ')',        ' ',
+    '=',        'A',        'Y' | 0x80, '(',        'I',        'N',        ')',        'D',
+    '=',        'A',        'Y',        '5',        'N' | 0x80, 'S',        'E',        'M',
+    '(',        'I',        ')',        '=',        'I',        'Y' | 0x80, ' ',        'A',
+    'N',        'T',        '(',        'I',        ')',        '=',        'A',        'Y' | 0x80,
+    '(',        'I',        'E',        'R',        ')',        '=',        'I',        'Y',
+    'E',        'R' | 0x80, '#',        ':',        'R',        '(',        'I',        'E',
+    'D',        ')',        ' ',        '=',        'I',        'Y',        'D' | 0x80, '(',
+    'I',        'E',        'D',        ')',        ' ',        '=',        'A',        'Y',
+    '5',        'D' | 0x80, '(',        'I',        'E',        'N',        ')',        '=',
+    'I',        'Y',        'E',        'H',        'N' | 0x80, '(',        'I',        'E',
+    ')',        'T',        '=',        'A',        'Y',        '4',        'E',        'H' | 0x80,
+    '(',        'I',        '\'',       ')',        '=',        'A',        'Y',        '5' | 0x80,
+    ' ',        ':',        '(',        'I',        ')',        '^',        '%',        '=',
+    'A',        'Y',        '5' | 0x80, ' ',        ':',        '(',        'I',        'E',
+    ')',        ' ',        '=',        'A',        'Y',        '4' | 0x80, '(',        'I',
+    ')',        '%',        '=',        'I',        'Y' | 0x80, '(',        'I',        'E',
+    ')',        '=',        'I',        'Y',        '4' | 0x80, ' ',        '(',        'I',
+    'D',        'E',        'A',        ')',        '=',        'A',        'Y',        'D',
+    'I',        'Y',        '5',        'A',        'H' | 0x80, '(',        'I',        ')',
+    '^',        '+',        ':',        '#',        '=',        'I',        'H' | 0x80, '(',
+    'I',        'R',        ')',        '#',        '=',        'A',        'Y',        'R' | 0x80,
+    '(',        'I',        'Z',        ')',        '%',        '=',        'A',        'Y',
+    'Z' | 0x80, '(',        'I',        'S',        ')',        '%',        '=',        'A',
+    'Y',        'Z' | 0x80, 'I',        '^',        '(',        'I',        ')',        '^',
+    '#',        '=',        'I',        'H' | 0x80, '+',        '^',        '(',        'I',
+    ')',        '^',        '+',        '=',        'A',        'Y' | 0x80, '#',        ':',
+    '^',        '(',        'I',        ')',        '^',        '+',        '=',        'I',
+    'H' | 0x80, '(',        'I',        ')',        '^',        '+',        '=',        'A',
+    'Y' | 0x80, '(',        'I',        'R',        ')',        '=',        'E',        'R' | 0x80,
+    '(',        'I',        'G',        'H',        ')',        '=',        'A',        'Y',
+    '4' | 0x80, '(',        'I',        'L',        'D',        ')',        '=',        'A',
+    'Y',        '5',        'L',        'D' | 0x80, ' ',        '(',        'I',        'G',
+    'N',        ')',        '=',        'I',        'H',        'G',        'N' | 0x80, '(',
+    'I',        'G',        'N',        ')',        ' ',        '=',        'A',        'Y',
+    '4',        'N' | 0x80, '(',        'I',        'G',        'N',        ')',        '^',
+    '=',        'A',        'Y',        '4',        'N' | 0x80, '(',        'I',        'G',
+    'N',        ')',        '%',        '=',        'A',        'Y',        '4',        'N' | 0x80,
+    '(',        'I',        'C',        'R',        'O',        ')',        '=',        'A',
+    'Y',        '4',        'K',        'R',        'O',        'H' | 0x80, '(',        'I',
+    'Q',        'U',        'E',        ')',        '=',        'I',        'Y',        '4',
+    'K' | 0x80, '(',        'I',        ')',        '=',        'I',        'H' | 0x80,
+
+    ']',        'J' | 0x80, ' ',        '(',        'J',        ')',        ' ',        '=',
+    'J',        'E',        'Y',        '4' | 0x80, '(',        'J',        ')',        '=',
+    'J' | 0x80,
+
+    ']',        'K' | 0x80, ' ',        '(',        'K',        ')',        ' ',        '=',
+    'K',        'E',        'Y',        '4' | 0x80, ' ',        '(',        'K',        ')',
+    'N',        '=' | 0x80, '(',        'K',        ')',        '=',        'K' | 0x80,
+
+    ']',        'L' | 0x80, ' ',        '(',        'L',        ')',        ' ',        '=',
+    'E',        'H',        '4',        'L' | 0x80, '(',        'L',        'O',        ')',
+    'C',        '#',        '=',        'L',        'O',        'W' | 0x80, 'L',        '(',
+    'L',        ')',        '=' | 0x80, '#',        ':',        '^',        '(',        'L',
+    ')',        '%',        '=',        'U',        'L' | 0x80, '(',        'L',        'E',
+    'A',        'D',        ')',        '=',        'L',        'I',        'Y',        'D' | 0x80,
+    ' ',        '(',        'L',        'A',        'U',        'G',        'H',        ')',
+    '=',        'L',        'A',        'E',        '4',        'F' | 0x80, '(',        'L',
+    ')',        '=',        'L' | 0x80,
+
+    ']',        'M' | 0x80, ' ',        '(',        'M',        ')',        ' ',        '=',
+    'E',        'H',        '4',        'M' | 0x80, ' ',        '(',        'M',        'R',
+    '.',        ')',        ' ',        '=',        'M',        'I',        'H',        '4',
+    'S',        'T',        'E',        'R' | 0x80, ' ',        '(',        'M',        'S',
+    '.',        ')',        '=',        'M',        'I',        'H',        '5',        'Z' | 0x80,
+    ' ',        '(',        'M',        'R',        'S',        '.',        ')',        ' ',
+    '=',        'M',        'I',        'H',        '4',        'S',        'I',        'X',
+    'Z' | 0x80, '(',        'M',        'O',        'V',        ')',        '=',        'M',
+    'U',        'W',        '4',        'V' | 0x80, '(',        'M',        'A',        'C',
+    'H',        'I',        'N',        ')',        '=',        'M',        'A',        'H',
+    'S',        'H',        'I',        'Y',        '5',        'N' | 0x80, 'M',        '(',
+    'M',        ')',        '=' | 0x80, '(',        'M',        ')',        '=',        'M' | 0x80,
+
+    ']',        'N' | 0x80, ' ',        '(',        'N',        ')',        ' ',        '=',
+    'E',        'H',        '4',        'N' | 0x80, 'E',        '(',        'N',        'G',
+    ')',        '+',        '=',        'N',        'J' | 0x80, '(',        'N',        'G',
+    ')',        'R',        '=',        'N',        'X',        'G' | 0x80, '(',        'N',
+    'G',        ')',        '#',        '=',        'N',        'X',        'G' | 0x80, '(',
+    'N',        'G',        'L',        ')',        '%',        '=',        'N',        'X',
+    'G',        'U',        'L' | 0x80, '(',        'N',        'G',        ')',        '=',
+    'N',        'X' | 0x80, '(',        'N',        'K',        ')',        '=',        'N',
+    'X',        'K' | 0x80, ' ',        '(',        'N',        'O',        'W',        ')',
+    ' ',        '=',        'N',        'A',        'W',        '4' | 0x80, 'N',        '(',
+    'N',        ')',        '=' | 0x80, '(',        'N',        'O',        'N',        ')',
+    'E',        '=',        'N',        'A',        'H',        '4',        'N' | 0x80, '(',
+    'N',        ')',        '=',        'N' | 0x80,
+
+    ']',        'O' | 0x80, ' ',        '(',        'O',        ')',        ' ',        '=',
+    'O',        'H',        '4',        'W' | 0x80, '(',        'O',        'F',        ')',
+    ' ',        '=',        'A',        'H',        'V' | 0x80, ' ',        '(',        'O',
+    'H',        ')',        ' ',        '=',        'O',        'W',        '5' | 0x80, '(',
+    'O',        'R',        'O',        'U',        'G',        'H',        ')',        '=',
+    'E',        'R',        '4',        'O',        'W' | 0x80, '#',        ':',        '(',
+    'O',        'R',        ')',        ' ',        '=',        'E',        'R' | 0x80, '#',
+    ':',        '(',        'O',        'R',        'S',        ')',        ' ',        '=',
+    'E',        'R',        'Z' | 0x80, '(',        'O',        'R',        ')',        '=',
+    'A',        'O',        'R' | 0x80, ' ',        '(',        'O',        'N',        'E',
+    ')',        '=',        'W',        'A',        'H',        'N' | 0x80, '#',        '(',
+    'O',        'N',        'E',        ')',        ' ',        '=',        'W',        'A',
+    'H',        'N' | 0x80, '(',        'O',        'W',        ')',        '=',        'O',
+    'W' | 0x80, ' ',        '(',        'O',        'V',        'E',        'R',        ')',
+    '=',        'O',        'W',        '5',        'V',        'E',        'R' | 0x80, 'P',
+    'R',        '(',        'O',        ')',        'V',        '=',        'U',        'W',
+    '4' | 0x80, '(',        'O',        'V',        ')',        '=',        'A',        'H',
+    '4',        'V' | 0x80, '(',        'O',        ')',        '^',        '%',        '=',
+    'O',        'W',        '5' | 0x80, '(',        'O',        ')',        '^',        'E',
+    'N',        '=',        'O',        'W' | 0x80, '(',        'O',        ')',        '^',
+    'I',        '#',        '=',        'O',        'W',        '5' | 0x80, '(',        'O',
+    'L',        ')',        'D',        '=',        'O',        'W',        '4',        'L' | 0x80,
+    '(',        'O',        'U',        'G',        'H',        'T',        ')',        '=',
+    'A',        'O',        '5',        'T' | 0x80, '(',        'O',        'U',        'G',
+    'H',        ')',        '=',        'A',        'H',        '5',        'F' | 0x80, ' ',
+    '(',        'O',        'U',        ')',        '=',        'A',        'W' | 0x80, 'H',
+    '(',        'O',        'U',        ')',        'S',        '#',        '=',        'A',
+    'W',        '4' | 0x80, '(',        'O',        'U',        'S',        ')',        '=',
+    'A',        'X',        'S' | 0x80, '(',        'O',        'U',        'R',        ')',
+    '=',        'O',        'H',        'R' | 0x80, '(',        'O',        'U',        'L',
+    'D',        ')',        '=',        'U',        'H',        '5',        'D' | 0x80, '(',
+    'O',        'U',        ')',        '^',        'L',        '=',        'A',        'H',
+    '5' | 0x80, '(',        'O',        'U',        'P',        ')',        '=',        'U',
+    'W',        '5',        'P' | 0x80, '(',        'O',        'U',        ')',        '=',
+    'A',        'W' | 0x80, '(',        'O',        'Y',        ')',        '=',        'O',
+    'Y' | 0x80, '(',        'O',        'I',        'N',        'G',        ')',        '=',
+    'O',        'W',        '4',        'I',        'H',        'N',        'X' | 0x80, '(',
+    'O',        'I',        ')',        '=',        'O',        'Y',        '5' | 0x80, '(',
+    'O',        'O',        'R',        ')',        '=',        'O',        'H',        '5',
+    'R' | 0x80, '(',        'O',        'O',        'K',        ')',        '=',        'U',
+    'H',        '5',        'K' | 0x80, 'F',        '(',        'O',        'O',        'D',
+    ')',        '=',        'U',        'W',        '5',        'D' | 0x80, 'L',        '(',
+    'O',        'O',        'D',        ')',        '=',        'A',        'H',        '5',
+    'D' | 0x80, 'M',        '(',        'O',        'O',        'D',        ')',        '=',
+    'U',        'W',        '5',        'D' | 0x80, '(',        'O',        'O',        'D',
+    ')',        '=',        'U',        'H',        '5',        'D' | 0x80, 'F',        '(',
+    'O',        'O',        'T',        ')',        '=',        'U',        'H',        '5',
+    'T' | 0x80, '(',        'O',        'O',        ')',        '=',        'U',        'W',
+    '5' | 0x80, '(',        'O',        '\'',       ')',        '=',        'O',        'H' | 0x80,
+    '(',        'O',        ')',        'E',        '=',        'O',        'W' | 0x80, '(',
+    'O',        ')',        ' ',        '=',        'O',        'W' | 0x80, '(',        'O',
+    'A',        ')',        '=',        'O',        'W',        '4' | 0x80, ' ',        '(',
+    'O',        'N',        'L',        'Y',        ')',        '=',        'O',        'W',
+    '4',        'N',        'L',        'I',        'Y' | 0x80, ' ',        '(',        'O',
+    'N',        'C',        'E',        ')',        '=',        'W',        'A',        'H',
+    '4',        'N',        'S' | 0x80, '(',        'O',        'N',        '\'',       'T',
+    ')',        '=',        'O',        'W',        '4',        'N',        'T' | 0x80, 'C',
+    '(',        'O',        ')',        'N',        '=',        'A',        'A' | 0x80, '(',
+    'O',        ')',        'N',        'G',        '=',        'A',        'O' | 0x80, ' ',
+    ':',        '^',        '(',        'O',        ')',        'N',        '=',        'A',
+    'H' | 0x80, 'I',        '(',        'O',        'N',        ')',        '=',        'U',
+    'N' | 0x80, '#',        ':',        '(',        'O',        'N',        ')',        '=',
+    'U',        'N' | 0x80, '#',        '^',        '(',        'O',        'N',        ')',
+    '=',        'U',        'N' | 0x80, '(',        'O',        ')',        'S',        'T',
+    '=',        'O',        'W' | 0x80, '(',        'O',        'F',        ')',        '^',
+    '=',        'A',        'O',        '4',        'F' | 0x80, '(',        'O',        'T',
+    'H',        'E',        'R',        ')',        '=',        'A',        'H',        '5',
+    'D',        'H',        'E',        'R' | 0x80, 'R',        '(',        'O',        ')',
+    'B',        '=',        'R',        'A',        'A' | 0x80, '^',        'R',        '(',
+    'O',        ')',        ':',        '#',        '=',        'O',        'W',        '5' | 0x80,
+    '(',        'O',        'S',        'S',        ')',        ' ',        '=',        'A',
+    'O',        '5',        'S' | 0x80, '#',        ':',        '^',        '(',        'O',
+    'M',        ')',        '=',        'A',        'H',        'M' | 0x80, '(',        'O',
+    ')',        '=',        'A',        'A' | 0x80,
+
+    ']',        'P' | 0x80, ' ',        '(',        'P',        ')',        ' ',        '=',
+    'P',        'I',        'Y',        '4' | 0x80, '(',        'P',        'H',        ')',
+    '=',        'F' | 0x80, '(',        'P',        'E',        'O',        'P',        'L',
+    ')',        '=',        'P',        'I',        'Y',        '5',        'P',        'U',
+    'L' | 0x80, '(',        'P',        'O',        'W',        ')',        '=',        'P',
+    'A',        'W',        '4' | 0x80, '(',        'P',        'U',        'T',        ')',
+    ' ',        '=',        'P',        'U',        'H',        'T' | 0x80, '(',        'P',
+    ')',        'P',        '=' | 0x80, '(',        'P',        ')',        'S',        '=' | 0x80,
+    '(',        'P',        ')',        'N',        '=' | 0x80, '(',        'P',        'R',
+    'O',        'F',        '.',        ')',        '=',        'P',        'R',        'O',
+    'H',        'F',        'E',        'H',        '4',        'S',        'E',        'R' | 0x80,
+    '(',        'P',        ')',        '=',        'P' | 0x80,
+
+    ']',        'Q' | 0x80, ' ',        '(',        'Q',        ')',        ' ',        '=',
+    'K',        'Y',        'U',        'W',        '4' | 0x80, '(',        'Q',        'U',
+    'A',        'R',        ')',        '=',        'K',        'W',        'O',        'H',
+    '5',        'R' | 0x80, '(',        'Q',        'U',        ')',        '=',        'K',
+    'W' | 0x80, '(',        'Q',        ')',        '=',        'K' | 0x80, ']',        'R' | 0x80,
+    ' ',        '(',        'R',        ')',        ' ',        '=',        'A',        'A',
+    '5',        'R' | 0x80, ' ',        '(',        'R',        'E',        ')',        '^',
+    '#',        '=',        'R',        'I',        'Y' | 0x80, '(',        'R',        ')',
+    'R',        '=' | 0x80, '(',        'R',        ')',        '=',        'R' | 0x80,
+
+    ']',        'S' | 0x80, ' ',        '(',        'S',        ')',        ' ',        '=',
+    'E',        'H',        '4',        'S' | 0x80, '(',        'S',        'H',        ')',
+    '=',        'S',        'H' | 0x80, '#',        '(',        'S',        'I',        'O',
+    'N',        ')',        '=',        'Z',        'H',        'U',        'N' | 0x80, '(',
+    'S',        'O',        'M',        'E',        ')',        '=',        'S',        'A',
+    'H',        'M' | 0x80, '#',        '(',        'S',        'U',        'R',        ')',
+    '#',        '=',        'Z',        'H',        'E',        'R' | 0x80, '(',        'S',
+    'U',        'R',        ')',        '#',        '=',        'S',        'H',        'E',
+    'R' | 0x80, '#',        '(',        'S',        'U',        ')',        '#',        '=',
+    'Z',        'H',        'U',        'W' | 0x80, '#',        '(',        'S',        'S',
+    'U',        ')',        '#',        '=',        'S',        'H',        'U',        'W' | 0x80,
+    '#',        '(',        'S',        'E',        'D',        ')',        '=',        'Z',
+    'D' | 0x80, '#',        '(',        'S',        ')',        '#',        '=',        'Z' | 0x80,
+    '(',        'S',        'A',        'I',        'D',        ')',        '=',        'S',
+    'E',        'H',        'D' | 0x80, '^',        '(',        'S',        'I',        'O',
+    'N',        ')',        '=',        'S',        'H',        'U',        'N' | 0x80, '(',
+    'S',        ')',        'S',        '=' | 0x80, '.',        '(',        'S',        ')',
+    ' ',        '=',        'Z' | 0x80, '#',        ':',        '.',        'E',        '(',
+    'S',        ')',        ' ',        '=',        'Z' | 0x80, '#',        ':',        '^',
+    '#',        '(',        'S',        ')',        ' ',        '=',        'S' | 0x80, 'U',
+    '(',        'S',        ')',        ' ',        '=',        'S' | 0x80, ' ',        ':',
+    '#',        '(',        'S',        ')',        ' ',        '=',        'Z' | 0x80, '#',
+    '#',        '(',        'S',        ')',        ' ',        '=',        'Z' | 0x80, ' ',
+    '(',        'S',        'C',        'H',        ')',        '=',        'S',        'K' | 0x80,
+    '(',        'S',        ')',        'C',        '+',        '=' | 0x80, '#',        '(',
+    'S',        'M',        ')',        '=',        'Z',        'U',        'M' | 0x80, '#',
+    '(',        'S',        'N',        ')',        '\'',       '=',        'Z',        'U',
+    'M' | 0x80, '(',        'S',        'T',        'L',        'E',        ')',        '=',
+    'S',        'U',        'L' | 0x80, '(',        'S',        ')',        '=',        'S' | 0x80,
+
+    ']',        'T' | 0x80, ' ',        '(',        'T',        ')',        ' ',        '=',
+    'T',        'I',        'Y',        '4' | 0x80, ' ',        '(',        'T',        'H',
+    'E',        ')',        ' ',        '#',        '=',        'D',        'H',        'I',
+    'Y' | 0x80, ' ',        '(',        'T',        'H',        'E',        ')',        ' ',
+    '=',        'D',        'H',        'A',        'X' | 0x80, '(',        'T',        'O',
+    ')',        ' ',        '=',        'T',        'U',        'X' | 0x80, ' ',        '(',
+    'T',        'H',        'A',        'T',        ')',        '=',        'D',        'H',
+    'A',        'E',        'T' | 0x80, ' ',        '(',        'T',        'H',        'I',
+    'S',        ')',        ' ',        '=',        'D',        'H',        'I',        'H',
+    'S' | 0x80, ' ',        '(',        'T',        'H',        'E',        'Y',        ')',
+    '=',        'D',        'H',        'E',        'Y' | 0x80, ' ',        '(',        'T',
+    'H',        'E',        'R',        'E',        ')',        '=',        'D',        'H',
+    'E',        'H',        'R' | 0x80, '(',        'T',        'H',        'E',        'R',
+    ')',        '=',        'D',        'H',        'E',        'R' | 0x80, '(',        'T',
+    'H',        'E',        'I',        'R',        ')',        '=',        'D',        'H',
+    'E',        'H',        'R' | 0x80, ' ',        '(',        'T',        'H',        'A',
+    'N',        ')',        ' ',        '=',        'D',        'H',        'A',        'E',
+    'N' | 0x80, ' ',        '(',        'T',        'H',        'E',        'M',        ')',
+    ' ',        '=',        'D',        'H',        'A',        'E',        'N' | 0x80, '(',
+    'T',        'H',        'E',        'S',        'E',        ')',        ' ',        '=',
+    'D',        'H',        'I',        'Y',        'Z' | 0x80, ' ',        '(',        'T',
+    'H',        'E',        'N',        ')',        '=',        'D',        'H',        'E',
+    'H',        'N' | 0x80, '(',        'T',        'H',        'R',        'O',        'U',
+    'G',        'H',        ')',        '=',        'T',        'H',        'R',        'U',
+    'W',        '4' | 0x80, '(',        'T',        'H',        'O',        'S',        'E',
+    ')',        '=',        'D',        'H',        'O',        'H',        'Z' | 0x80, '(',
+    'T',        'H',        'O',        'U',        'G',        'H',        ')',        ' ',
+    '=',        'D',        'H',        'O',        'W' | 0x80, '(',        'T',        'O',
+    'D',        'A',        'Y',        ')',        '=',        'T',        'U',        'X',
+    'D',        'E',        'Y' | 0x80, '(',        'T',        'O',        'M',        'O',
+    ')',        'R',        'R',        'O',        'W',        '=',        'T',        'U',
+    'M',        'A',        'A',        '5' | 0x80, '(',        'T',        'O',        ')',
+    'T',        'A',        'L',        '=',        'T',        'O',        'W',        '5' | 0x80,
+    ' ',        '(',        'T',        'H',        'U',        'S',        ')',        '=',
+    'D',        'H',        'A',        'H',        '4',        'S' | 0x80, '(',        'T',
+    'H',        ')',        '=',        'T',        'H' | 0x80, '#',        ':',        '(',
+    'T',        'E',        'D',        ')',        '=',        'T',        'I',        'X',
+    'D' | 0x80, 'S',        '(',        'T',        'I',        ')',        '#',        'N',
+    '=',        'C',        'H' | 0x80, '(',        'T',        'I',        ')',        'O',
+    '=',        'S',        'H' | 0x80, '(',        'T',        'I',        ')',        'A',
+    '=',        'S',        'H' | 0x80, '(',        'T',        'I',        'E',        'N',
+    ')',        '=',        'S',        'H',        'U',        'N' | 0x80, '(',        'T',
+    'U',        'R',        ')',        '#',        '=',        'C',        'H',        'E',
+    'R' | 0x80, '(',        'T',        'U',        ')',        'A',        '=',        'C',
+    'H',        'U',        'W' | 0x80, ' ',        '(',        'T',        'W',        'O',
+    ')',        '=',        'T',        'U',        'W' | 0x80, '&',        '(',        'T',
+    ')',        'E',        'N',        ' ',        '=' | 0x80, '(',        'T',        ')',
+    '=',        'T' | 0x80,
+
+    ']',        'U' | 0x80, ' ',        '(',        'U',        ')',        ' ',        '=',
+    'Y',        'U',        'W',        '4' | 0x80, ' ',        '(',        'U',        'N',
+    ')',        'I',        '=',        'Y',        'U',        'W',        'N' | 0x80, ' ',
+    '(',        'U',        'N',        ')',        '=',        'A',        'H',        'N' | 0x80,
+    ' ',        '(',        'U',        'P',        'O',        'N',        ')',        '=',
+    'A',        'X',        'P',        'A',        'O',        'N' | 0x80, '@',        '(',
+    'U',        'R',        ')',        '#',        '=',        'U',        'H',        '4',
+    'R' | 0x80, '(',        'U',        'R',        ')',        '#',        '=',        'Y',
+    'U',        'H',        '4',        'R' | 0x80, '(',        'U',        'R',        ')',
+    '=',        'E',        'R' | 0x80, '(',        'U',        ')',        '^',        ' ',
+    '=',        'A',        'H' | 0x80, '(',        'U',        ')',        '^',        '^',
+    '=',        'A',        'H',        '5' | 0x80, '(',        'U',        'Y',        ')',
+    '=',        'A',        'Y',        '5' | 0x80, ' ',        'G',        '(',        'U',
+    ')',        '#',        '=' | 0x80, 'G',        '(',        'U',        ')',        '%',
+    '=' | 0x80, 'G',        '(',        'U',        ')',        '#',        '=',        'W' | 0x80,
+    '#',        'N',        '(',        'U',        ')',        '=',        'Y',        'U',
+    'W' | 0x80, '@',        '(',        'U',        ')',        '=',        'U',        'W' | 0x80,
+    '(',        'U',        ')',        '=',        'Y',        'U',        'W' | 0x80,
+
+    ']',        'V' | 0x80, ' ',        '(',        'V',        ')',        ' ',        '=',
+    'V',        'I',        'Y',        '4' | 0x80, '(',        'V',        'I',        'E',
+    'W',        ')',        '=',        'V',        'Y',        'U',        'W',        '5' | 0x80,
+    '(',        'V',        ')',        '=',        'V' | 0x80,
+
+    ']',        'W' | 0x80, ' ',        '(',        'W',        ')',        ' ',        '=',
+    'D',        'A',        'H',        '4',        'B',        'U',        'L',        'Y',
+    'U',        'W' | 0x80, ' ',        '(',        'W',        'E',        'R',        'E',
+    ')',        '=',        'W',        'E',        'R' | 0x80, '(',        'W',        'A',
+    ')',        'S',        'H',        '=',        'W',        'A',        'A' | 0x80, '(',
+    'W',        'A',        ')',        'S',        'T',        '=',        'W',        'E',
+    'Y' | 0x80, '(',        'W',        'A',        ')',        'S',        '=',        'W',
+    'A',        'H' | 0x80, '(',        'W',        'A',        ')',        'T',        '=',
+    'W',        'A',        'A' | 0x80, '(',        'W',        'H',        'E',        'R',
+    'E',        ')',        '=',        'W',        'H',        'E',        'H',        'R' | 0x80,
+    '(',        'W',        'H',        'A',        'T',        ')',        '=',        'W',
+    'H',        'A',        'H',        'T' | 0x80, '(',        'W',        'H',        'O',
+    'L',        ')',        '=',        '/',        'H',        'O',        'W',        'L' | 0x80,
+    '(',        'W',        'H',        'O',        ')',        '=',        '/',        'H',
+    'U',        'W' | 0x80, '(',        'W',        'H',        ')',        '=',        'W',
+    'H' | 0x80, '(',        'W',        'A',        'R',        ')',        '#',        '=',
+    'W',        'E',        'H',        'R' | 0x80, '(',        'W',        'A',        'R',
+    ')',        '=',        'W',        'A',        'O',        'R' | 0x80, '(',        'W',
+    'O',        'R',        ')',        '^',        '=',        'W',        'E',        'R' | 0x80,
+    '(',        'W',        'R',        ')',        '=',        'R' | 0x80, '(',        'W',
+    'O',        'M',        ')',        'A',        '=',        'W',        'U',        'H',
+    'M' | 0x80, '(',        'W',        'O',        'M',        ')',        'E',        '=',
+    'W',        'I',        'H',        'M' | 0x80, '(',        'W',        'E',        'A',
+    ')',        'R',        '=',        'W',        'E',        'H' | 0x80, '(',        'W',
+    'A',        'N',        'T',        ')',        '=',        'W',        'A',        'A',
+    '5',        'N',        'T' | 0x80, 'A',        'N',        'S',        '(',        'W',
+    'E',        'R',        ')',        '=',        'E',        'R' | 0x80, '(',        'W',
+    ')',        '=',        'W' | 0x80,
+
+    ']',        'X' | 0x80, ' ',        '(',        'X',        ')',        ' ',        '=',
+    'E',        'H',        '4',        'K',        'R' | 0x80, ' ',        '(',        'X',
+    ')',        '=',        'Z' | 0x80, '(',        'X',        ')',        '=',        'K',
+    'S' | 0x80,
+
+    ']',        'Y' | 0x80, ' ',        '(',        'Y',        ')',        ' ',        '=',
+    'W',        'A',        'Y',        '4' | 0x80, '(',        'Y',        'O',        'U',
+    'N',        'G',        ')',        '=',        'Y',        'A',        'H',        'N',
+    'X' | 0x80, ' ',        '(',        'Y',        'O',        'U',        'R',        ')',
+    '=',        'Y',        'O',        'H',        'R' | 0x80, ' ',        '(',        'Y',
+    'O',        'U',        ')',        '=',        'Y',        'U',        'W' | 0x80, ' ',
+    '(',        'Y',        'E',        'S',        ')',        '=',        'Y',        'E',
+    'H',        'S' | 0x80, ' ',        '(',        'Y',        ')',        '=',        'Y' | 0x80,
+    'F',        '(',        'Y',        ')',        '=',        'A',        'Y' | 0x80, 'P',
+    'S',        '(',        'Y',        'C',        'H',        ')',        '=',        'A',
+    'Y',        'K' | 0x80, '#',        ':',        '^',        '(',        'Y',        ')',
+    '=',        'I',        'Y' | 0x80, '#',        ':',        '^',        '(',        'Y',
+    ')',        'I',        '=',        'I',        'Y' | 0x80, ' ',        ':',        '(',
+    'Y',        ')',        ' ',        '=',        'A',        'Y' | 0x80, ' ',        ':',
+    '(',        'Y',        ')',        '#',        '=',        'A',        'Y' | 0x80, ' ',
+    ':',        '(',        'Y',        ')',        '^',        '+',        ':',        '#',
+    '=',        'I',        'H' | 0x80, ' ',        ':',        '(',        'Y',        ')',
+    '^',        '#',        '=',        'A',        'Y' | 0x80, '(',        'Y',        ')',
+    '=',        'I',        'H' | 0x80,
+
+    ']',        'Z' | 0x80, ' ',        '(',        'Z',        ')',        ' ',        '=',
+    'Z',        'I',        'Y',        '4' | 0x80, '(',        'Z',        ')',        '=',
+    'Z' | 0x80, 'j' | 0x80};
+
+const unsigned char rules2[] = {
+    '(',        'A',        ')',        '=' | 0x80, '(',        '!',        ')',        '=',
+    '.' | 0x80, '(',        '"',        ')',        ' ',        '=',        '-',        'A',
+    'H',        '5',        'N',        'K',        'W',        'O',        'W',        'T',
+    '-' | 0x80, '(',        '"',        ')',        '=',        'K',        'W',        'O',
+    'W',        '4',        'T',        '-' | 0x80, '(',        '#',        ')',        '=',
+    ' ',        'N',        'A',        'H',        '4',        'M',        'B',        'E',
+    'R' | 0x80, '(',        '$',        ')',        '=',        ' ',        'D',        'A',
+    'A',        '4',        'L',        'E',        'R' | 0x80, '(',        '%',        ')',
+    '=',        ' ',        'P',        'E',        'R',        'S',        'E',        'H',
+    '4',        'N',        'T' | 0x80, '(',        '&',        ')',        '=',        ' ',
+    'A',        'E',        'N',        'D' | 0x80, '(',        '\'',       ')',        '=' | 0x80,
+    '(',        '*',        ')',        '=',        ' ',        'A',        'E',        '4',
+    'S',        'T',        'E',        'R',        'I',        'H',        'S',        'K' | 0x80,
+    '(',        '+',        ')',        '=',        ' ',        'P',        'L',        'A',
+    'H',        '4',        'S' | 0x80, '(',        ',',        ')',        '=',        ',' | 0x80,
+    ' ',        '(',        '-',        ')',        ' ',        '=',        '-' | 0x80, '(',
+    '-',        ')',        '=' | 0x80, '(',        '.',        ')',        '=',        ' ',
+    'P',        'O',        'Y',        'N',        'T' | 0x80, '(',        '/',        ')',
+    '=',        ' ',        'S',        'L',        'A',        'E',        '4',        'S',
+    'H' | 0x80, '(',        '0',        ')',        '=',        ' ',        'Z',        'I',
+    'Y',        '4',        'R',        'O',        'W' | 0x80, ' ',        '(',        '1',
+    'S',        'T',        ')',        '=',        'F',        'E',        'R',        '4',
+    'S',        'T' | 0x80, ' ',        '(',        '1',        '0',        'T',        'H',
+    ')',        '=',        'T',        'E',        'H',        '4',        'N',        'T',
+    'H' | 0x80, '(',        '1',        ')',        '=',        ' ',        'W',        'A',
+    'H',        '4',        'N' | 0x80, ' ',        '(',        '2',        'N',        'D',
+    ')',        '=',        'S',        'E',        'H',        '4',        'K',        'U',
+    'N',        'D' | 0x80, '(',        '2',        ')',        '=',        ' ',        'T',
+    'U',        'W',        '4' | 0x80, ' ',        '(',        '3',        'R',        'D',
+    ')',        '=',        'T',        'H',        'E',        'R',        '4',        'D' | 0x80,
+    '(',        '3',        ')',        '=',        ' ',        'T',        'H',        'R',
+    'I',        'Y',        '4' | 0x80, '(',        '4',        ')',        '=',        ' ',
+    'F',        'O',        'H',        '4',        'R' | 0x80, ' ',        '(',        '5',
+    'T',        'H',        ')',        '=',        'F',        'I',        'H',        '4',
+    'F',        'T',        'H' | 0x80, '(',        '5',        ')',        '=',        ' ',
+    'F',        'A',        'Y',        '4',        'V' | 0x80, ' ',        '(',        '6',
+    '4',        ')',        ' ',        '=',        'S',        'I',        'H',        '4',
+    'K',        'S',        'T',        'I',        'Y',        ' ',        'F',        'O',
+    'H',        'R' | 0x80, '(',        '6',        ')',        '=',        ' ',        'S',
+    'I',        'H',        '4',        'K',        'S' | 0x80, '(',        '7',        ')',
+    '=',        ' ',        'S',        'E',        'H',        '4',        'V',        'U',
+    'N' | 0x80, ' ',        '(',        '8',        'T',        'H',        ')',        '=',
+    'E',        'Y',        '4',        'T',        'H' | 0x80, '(',        '8',        ')',
+    '=',        ' ',        'E',        'Y',        '4',        'T' | 0x80, '(',        '9',
+    ')',        '=',        ' ',        'N',        'A',        'Y',        '4',        'N' | 0x80,
+    '(',        ':',        ')',        '=',        '.' | 0x80, '(',        ';',        ')',
+    '=',        '.' | 0x80, '(',        '<',        ')',        '=',        ' ',        'L',
+    'E',        'H',        '4',        'S',        ' ',        'D',        'H',        'A',
+    'E',        'N' | 0x80, '(',        '=',        ')',        '=',        ' ',        'I',
+    'Y',        '4',        'K',        'W',        'U',        'L',        'Z' | 0x80, '(',
+    '>',        ')',        '=',        ' ',        'G',        'R',        'E',        'Y',
+    '4',        'T',        'E',        'R',        ' ',        'D',        'H',        'A',
+    'E',        'N' | 0x80, '(',        '?',        ')',        '=',        '?' | 0x80, '(',
+    '@',        ')',        '=',        ' ',        'A',        'E',        '6',        'T' | 0x80,
+    '(',        '^',        ')',        '=',        ' ',        'K',        'A',        'E',
+    '4',        'R',        'I',        'X',        'T' | 0x80, ']',        'A' | 0x80};
+
+//26 items. From 'A' to 'Z'
+// positions for mem62 and mem63 for each character
+const unsigned char tab37489[] = {0,   149, 247, 162, 57, 197, 6,  126, 199, 38, 55, 78, 145,
+                                  241, 85,  161, 254, 36, 69,  45, 167, 54,  83, 46, 71, 218};
+
+const unsigned char tab37515[] = {125, 126, 126, 127, 128, 129, 130, 130, 130, 132, 132, 132, 132,
+                                  132, 133, 135, 135, 136, 136, 137, 138, 139, 139, 140, 140, 140};
+
+void STM32SAM::Output8BitAry(int index, unsigned char ary[5]) {
+    int k;
+
+    uint32_t bufferposOld = bufferpos;
+
+    bufferpos += timetable[oldtimetableindex][index];
+    oldtimetableindex = index;
+
+    int sample_uS = bufferpos - bufferposOld;
+
+    uint32_t f = 0;
+
+    // write a little bit in advance
+    for(k = 0; k < 5; k++) {
+        //   buffer[bufferpos / 50 + k] = ary[k];
+
+        // f = micros() + sample_uS / (_STM32SAM_SPEED + 1);
+        // while(micros() < f) {
+        // };
+        f = sample_uS / (_STM32SAM_SPEED + 1);
+        furi_delay_us(f);
+        SetAUDIO(ary[k]);
+        //  delayMicroseconds(sample_uS / 5 );
+    }
+
+    //  SetAUDIO(ary[0]);
+}
+
+void STM32SAM::Output8Bit(int index, unsigned char A) {
+    unsigned char ary[5] = {A, A, A, A, A};
+    Output8BitAry(index, ary);
+}
+
+//written by me because of different table positions.
+// mem[47] = ...
+// 168=pitches
+// 169=frequency1
+// 170=frequency2
+// 171=frequency3
+// 172=amplitude1
+// 173=amplitude2
+// 174=amplitude3
+unsigned char STM32SAM::Read(unsigned char p, unsigned char Y) {
+    switch(p) {
+    case 168:
+        return pitches[Y];
+    case 169:
+        return frequency1[Y];
+    case 170:
+        return frequency2[Y];
+    case 171:
+        return frequency3[Y];
+    case 172:
+        return amplitude1[Y];
+    case 173:
+        return amplitude2[Y];
+    case 174:
+        return amplitude3[Y];
+    }
+    // Serial1.println("Error reading to tables");
+    return 0;
+}
+
+void STM32SAM::Write(unsigned char p, unsigned char Y, unsigned char value) {
+    switch(p) {
+    case 168:
+        pitches[Y] = value;
+        return;
+    case 169:
+        frequency1[Y] = value;
+        return;
+    case 170:
+        frequency2[Y] = value;
+        return;
+    case 171:
+        frequency3[Y] = value;
+        return;
+    case 172:
+        amplitude1[Y] = value;
+        return;
+    case 173:
+        amplitude2[Y] = value;
+        return;
+    case 174:
+        amplitude3[Y] = value;
+        return;
+    }
+    //Serial1.println("Error writing to tables\n");
+}
+
+// -------------------------------------------------------------------------
+//Code48227
+// Render a sampled sound from the sampleTable.
+//
+//   Phoneme   Sample Start   Sample End
+//   32: S*    15             255
+//   33: SH    257            511
+//   34: F*    559            767
+//   35: TH    583            767
+//   36: /H    903            1023
+//   37: /X    1135           1279
+//   38: Z*    84             119
+//   39: ZH    340            375
+//   40: V*    596            639
+//   41: DH    596            631
+//
+//   42: CH
+//   43: **    399            511
+//
+//   44: J*
+//   45: **    257            276
+//   46: **
+//
+//   66: P*
+//   67: **    743            767
+//   68: **
+//
+//   69: T*
+//   70: **    231            255
+//   71: **
+//
+// The SampledPhonemesTable[] holds flags indicating if a phoneme is
+// voiced or not. If the upper 5 bits are zero, the sample is voiced.
+//
+// Samples in the sampleTable are compressed, with bits being converted to
+// bytes from high bit to low, as follows:
+//
+//   unvoiced 0 bit   -> X
+//   unvoiced 1 bit   -> 5
+//
+//   voiced 0 bit     -> 6
+//   voiced 1 bit     -> 24
+//
+// Where X is a value from the table:
+//
+//   { 0x18, 0x1A, 0x17, 0x17, 0x17 };
+//
+// The index into this table is determined by masking off the lower
+// 3 bits from the SampledPhonemesTable:
+//
+//        index = (SampledPhonemesTable[i] & 7) - 1;
+//
+// For voices samples, samples are interleaved between voiced output.
+
+// Code48227()
+void STM32SAM::RenderSample(unsigned char* mem66) {
+    int tempA;
+    // current phoneme's index
+    mem49 = Y;
+
+    // mask low three bits and subtract 1 get value to
+    // convert 0 bits on unvoiced samples.
+    A = mem39 & 7;
+    X = A - 1;
+
+    // store the result
+    mem56 = X;
+
+    // determine which offset to use from table { 0x18, 0x1A, 0x17, 0x17, 0x17 }
+    // T, S, Z                0          0x18
+    // CH, J, SH, ZH          1          0x1A
+    // P, F*, V, TH, DH       2          0x17
+    // /H                     3          0x17
+    // /X                     4          0x17
+
+    // get value from the table
+    mem53 = tab48426[X];
+    mem47 = X; //46016+mem[56]*256
+
+    // voiced sample?
+    A = mem39 & 248;
+    if(A == 0) {
+        // voiced phoneme: Z*, ZH, V*, DH
+        Y = mem49;
+        A = pitches[mem49] >> 4;
+
+        // jump to voiced portion
+        goto pos48315;
+    }
+
+    Y = A ^ 255;
+pos48274:
+
+    // step through the 8 bits in the sample
+    mem56 = 8;
+
+    // get the next sample from the table
+    // mem47*256 = offset to start of samples
+    A = sampleTable[mem47 * 256 + Y];
+pos48280:
+
+    // left shift to get the high bit
+    tempA = A;
+    A = A << 1;
+    //48281: BCC 48290
+
+    // bit not set?
+    if((tempA & 128) == 0) {
+        // convert the bit to value from table
+        X = mem53;
+        //mem[54296] = X;
+        // output the byte
+        Output8Bit(1, (X & 0x0f) * 16);
+        // if X != 0, exit loop
+        if(X != 0) goto pos48296;
+    }
+
+    // output a 5 for the on bit
+    Output8Bit(2, 5 * 16);
+
+    //48295: NOP
+pos48296:
+
+    X = 0;
+
+    // decrement counter
+    mem56--;
+
+    // if not done, jump to top of loop
+    if(mem56 != 0) goto pos48280;
+
+    // increment position
+    Y++;
+    if(Y != 0) goto pos48274;
+
+    // restore values and return
+    mem44 = 1;
+    Y = mem49;
+    return;
+
+    unsigned char phase1;
+
+pos48315:
+    // handle voiced samples here
+
+    // number of samples?
+    phase1 = A ^ 255;
+
+    Y = *mem66;
+    do {
+        //pos48321:
+
+        // shift through all 8 bits
+        mem56 = 8;
+        //A = Read(mem47, Y);
+
+        // fetch value from table
+        A = sampleTable[mem47 * 256 + Y];
+
+        // loop 8 times
+        //pos48327:
+        do {
+            //48327: ASL A
+            //48328: BCC 48337
+
+            // left shift and check high bit
+            tempA = A;
+            A = A << 1;
+            if((tempA & 128) != 0) {
+                // if bit set, output 26
+                X = 26;
+                Output8Bit(3, (X & 0xf) * 16);
+            } else {
+                //timetable 4
+                // bit is not set, output a 6
+                X = 6;
+                Output8Bit(4, (X & 0xf) * 16);
+            }
+
+            mem56--;
+        } while(mem56 != 0);
+
+        // move ahead in the table
+        Y++;
+
+        // continue until counter done
+        phase1++;
+
+    } while(phase1 != 0);
+    //  if (phase1 != 0) goto pos48321;
+
+    // restore values and return
+    A = 1;
+    mem44 = 1;
+    *mem66 = Y;
+    Y = mem49;
+    return;
+}
+
+// RENDER THE PHONEMES IN THE LIST
+//
+// The phoneme list is converted into sound through the steps:
+//
+// 1. Copy each phoneme <length> number of times into the frames list,
+//    where each frame represents 10 milliseconds of sound.
+//
+// 2. Determine the transitions lengths between phonemes, and linearly
+//    interpolate the values across the frames.
+//
+// 3. Offset the pitches by the fundamental frequency.
+//
+// 4. Render the each frame.
+
+//void Code47574()
+void STM32SAM::Render() {
+    unsigned char phase1 = 0; //mem43
+    unsigned char phase2 = 0;
+    unsigned char phase3 = 0;
+    unsigned char mem66 = 0;
+    unsigned char mem38 = 0;
+    unsigned char mem40 = 0;
+    unsigned char speedcounter = 0; //mem45
+    unsigned char mem48 = 0;
+    int i;
+    if(phonemeIndexOutput[0] == 255) return; //exit if no data
+
+    A = 0;
+    X = 0;
+    mem44 = 0;
+
+    // CREATE FRAMES
+    //
+    // The length parameter in the list corresponds to the number of frames
+    // to expand the phoneme to. Each frame represents 10 milliseconds of time.
+    // So a phoneme with a length of 7 = 7 frames = 70 milliseconds duration.
+    //
+    // The parameters are copied from the phoneme to the frame verbatim.
+
+    // pos47587:
+    do {
+        // get the index
+        Y = mem44;
+        // get the phoneme at the index
+        A = phonemeIndexOutput[mem44];
+        mem56 = A;
+
+        // if terminal phoneme, exit the loop
+        if(A == 255) break;
+
+        // period phoneme *.
+        if(A == 1) {
+            // add rising inflection
+            A = 1;
+            mem48 = 1;
+            //goto pos48376;
+            AddInflection(mem48, phase1);
+        }
+        /*
+      if (A == 2) goto pos48372;
+    */
+
+        // question mark phoneme?
+        if(A == 2) {
+            // create falling inflection
+            mem48 = 255;
+            AddInflection(mem48, phase1);
+        }
+        //  pos47615:
+
+        // get the stress amount (more stress = higher pitch)
+        phase1 = tab47492[stressOutput[Y] + 1];
+
+        // get number of frames to write
+        phase2 = phonemeLengthOutput[Y];
+        Y = mem56;
+
+        // copy from the source to the frames list
+        do {
+            frequency1[X] = freq1data[Y]; // F1 frequency
+            frequency2[X] = freq2data[Y]; // F2 frequency
+            frequency3[X] = freq3data[Y]; // F3 frequency
+            amplitude1[X] = ampl1data[Y]; // F1 amplitude
+            amplitude2[X] = ampl2data[Y]; // F2 amplitude
+            amplitude3[X] = ampl3data[Y]; // F3 amplitude
+            sampledConsonantFlag[X] =
+                sampledConsonantFlags[Y]; // phoneme data for sampled consonants
+            pitches[X] = pitch + phase1; // pitch
+            X++;
+            phase2--;
+        } while(phase2 != 0);
+        mem44++;
+    } while(mem44 != 0);
+    // -------------------
+    //pos47694:
+
+    // CREATE TRANSITIONS
+    //
+    // Linear transitions are now created to smoothly connect the
+    // end of one sustained portion of a phoneme to the following
+    // phoneme.
+    //
+    // To do this, three tables are used:
+    //
+    //  Table         Purpose
+    //  =========     ==================================================
+    //  blendRank     Determines which phoneme's blend values are used.
+    //
+    //  blendOut      The number of frames at the end of the phoneme that
+    //                will be used to transition to the following phoneme.
+    //
+    //  blendIn       The number of frames of the following phoneme that
+    //                will be used to transition into that phoneme.
+    //
+    // In creating a transition between two phonemes, the phoneme
+    // with the HIGHEST rank is used. Phonemes are ranked on how much
+    // their identity is based on their transitions. For example,
+    // vowels are and diphthongs are identified by their sustained portion,
+    // rather than the transitions, so they are given low values. In contrast,
+    // stop consonants (P, B, T, K) and glides (Y, L) are almost entirely
+    // defined by their transitions, and are given high rank values.
+    //
+    // Here are the rankings used by SAM:
+    //
+    //     Rank    Type                         Phonemes
+    //     2       All vowels                   IY, IH, etc.
+    //     5       Diphthong endings            YX, WX, ER
+    //     8       Terminal liquid consonants   LX, WX, YX, N, NX
+    //     9       Liquid consonants            L, RX, W
+    //     10      Glide                        R, OH
+    //     11      Glide                        WH
+    //     18      Voiceless fricatives         S, SH, F, TH
+    //     20      Voiced fricatives            Z, ZH, V, DH
+    //     23      Plosives, stop consonants    P, T, K, KX, DX, CH
+    //     26      Stop consonants              J, GX, B, D, G
+    //     27-29   Stop consonants (internal)   **
+    //     30      Unvoiced consonants          /H, /X and Q*
+    //     160     Nasal                        M
+    //
+    // To determine how many frames to use, the two phonemes are
+    // compared using the blendRank[] table. The phoneme with the
+    // higher rank is selected. In case of a tie, a blend of each is used:
+    //
+    //      if blendRank[phoneme1] ==  blendRank[phomneme2]
+    //          // use lengths from each phoneme
+    //          outBlendFrames = outBlend[phoneme1]
+    //          inBlendFrames = outBlend[phoneme2]
+    //      else if blendRank[phoneme1] > blendRank[phoneme2]
+    //          // use lengths from first phoneme
+    //          outBlendFrames = outBlendLength[phoneme1]
+    //          inBlendFrames = inBlendLength[phoneme1]
+    //      else
+    //          // use lengths from the second phoneme
+    //          // note that in and out are SWAPPED!
+    //          outBlendFrames = inBlendLength[phoneme2]
+    //          inBlendFrames = outBlendLength[phoneme2]
+    //
+    // Blend lengths can't be less than zero.
+    //
+    // Transitions are assumed to be symetrical, so if the transition
+    // values for the second phoneme are used, the inBlendLength and
+    // outBlendLength values are SWAPPED.
+    //
+    // For most of the parameters, SAM interpolates over the range of the last
+    // outBlendFrames-1 and the first inBlendFrames.
+    //
+    // The exception to this is the Pitch[] parameter, which is interpolates the
+    // pitch from the CENTER of the current phoneme to the CENTER of the next
+    // phoneme.
+    //
+    // Here are two examples. First, For example, consider the word "SUN" (S AH N)
+    //
+    //    Phoneme   Duration    BlendWeight    OutBlendFrames    InBlendFrames
+    //    S         2           18             1                 3
+    //    AH        8           2              4                 4
+    //    N         7           8              1                 2
+    //
+    // The formant transitions for the output frames are calculated as follows:
+    //
+    //     flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch
+    //    ------------------------------------------------
+    // S
+    //    241     0     6     0    73     0    99    61   Use S (weight 18) for transition instead of AH (weight 2)
+    //    241     0     6     0    73     0    99    61   <-- (OutBlendFrames-1) = (1-1) = 0 frames
+    // AH
+    //      0     2    10     2    66     0    96    59 * <-- InBlendFrames = 3 frames
+    //      0     4    14     3    59     0    93    57 *
+    //      0     8    18     5    52     0    90    55 *
+    //      0    15    22     9    44     1    87    53
+    //      0    15    22     9    44     1    87    53
+    //      0    15    22     9    44     1    87    53   Use N (weight 8) for transition instead of AH (weight 2).
+    //      0    15    22     9    44     1    87    53   Since N is second phoneme, reverse the IN and OUT values.
+    //      0    11    17     8    47     1    98    56 * <-- (InBlendFrames-1) = (2-1) = 1 frames
+    // N
+    //      0     8    12     6    50     1   109    58 * <-- OutBlendFrames = 1
+    //      0     5     6     5    54     0   121    61
+    //      0     5     6     5    54     0   121    61
+    //      0     5     6     5    54     0   121    61
+    //      0     5     6     5    54     0   121    61
+    //      0     5     6     5    54     0   121    61
+    //      0     5     6     5    54     0   121    61
+    //
+    // Now, consider the reverse "NUS" (N AH S):
+    //
+    //     flags ampl1 freq1 ampl2 freq2 ampl3 freq3 pitch
+    //    ------------------------------------------------
+    // N
+    //     0     5     6     5    54     0   121    61
+    //     0     5     6     5    54     0   121    61
+    //     0     5     6     5    54     0   121    61
+    //     0     5     6     5    54     0   121    61
+    //     0     5     6     5    54     0   121    61
+    //     0     5     6     5    54     0   121    61   Use N (weight 8) for transition instead of AH (weight 2)
+    //     0     5     6     5    54     0   121    61   <-- (OutBlendFrames-1) = (1-1) = 0 frames
+    // AH
+    //     0     8    11     6    51     0   110    59 * <-- InBlendFrames = 2
+    //     0    11    16     8    48     0    99    56 *
+    //     0    15    22     9    44     1    87    53   Use S (weight 18) for transition instead of AH (weight 2)
+    //     0    15    22     9    44     1    87    53   Since S is second phoneme, reverse the IN and OUT values.
+    //     0     9    18     5    51     1    90    55 * <-- (InBlendFrames-1) = (3-1) = 2
+    //     0     4    14     3    58     1    93    57 *
+    // S
+    //   241     2    10     2    65     1    96    59 * <-- OutBlendFrames = 1
+    //   241     0     6     0    73     0    99    61
+
+    A = 0;
+    mem44 = 0;
+    mem49 = 0; // mem49 starts at as 0
+    X = 0;
+    while(1) //while No. 1
+    {
+        // get the current and following phoneme
+        Y = phonemeIndexOutput[X];
+        A = phonemeIndexOutput[X + 1];
+        X++;
+
+        // exit loop at end token
+        if(A == 255) break; //goto pos47970;
+
+        // get the ranking of each phoneme
+        X = A;
+        mem56 = blendRank[A];
+        A = blendRank[Y];
+
+        // compare the rank - lower rank value is stronger
+        if(A == mem56) {
+            // same rank, so use out blend lengths from each phoneme
+            phase1 = outBlendLength[Y];
+            phase2 = outBlendLength[X];
+        } else if(A < mem56) {
+            // first phoneme is stronger, so us it's blend lengths
+            phase1 = inBlendLength[X];
+            phase2 = outBlendLength[X];
+        } else {
+            // second phoneme is stronger, so use it's blend lengths
+            // note the out/in are swapped
+            phase1 = outBlendLength[Y];
+            phase2 = inBlendLength[Y];
+        }
+
+        Y = mem44;
+        A = mem49 + phonemeLengthOutput[mem44]; // A is mem49 + length
+        mem49 = A; // mem49 now holds length + position
+        A = A + phase2; //Maybe Problem because of carry flag
+
+        //47776: ADC 42
+        speedcounter = A;
+        mem47 = 168;
+        phase3 = mem49 - phase1; // what is mem49
+        A = phase1 + phase2; // total transition?
+        mem38 = A;
+
+        X = A;
+        X -= 2;
+        if((X & 128) == 0)
+            do //while No. 2
+            {
+                //pos47810:
+
+                // mem47 is used to index the tables:
+                // 168  pitches[]
+                // 169  frequency1
+                // 170  frequency2
+                // 171  frequency3
+                // 172  amplitude1
+                // 173  amplitude2
+                // 174  amplitude3
+
+                mem40 = mem38;
+
+                if(mem47 == 168) // pitch
+                {
+                    // unlike the other values, the pitches[] interpolates from
+                    // the middle of the current phoneme to the middle of the
+                    // next phoneme
+
+                    unsigned char mem36, mem37;
+                    // half the width of the current phoneme
+                    mem36 = phonemeLengthOutput[mem44] >> 1;
+                    // half the width of the next phoneme
+                    mem37 = phonemeLengthOutput[mem44 + 1] >> 1;
+                    // sum the values
+                    mem40 = mem36 + mem37; // length of both halves
+                    mem37 += mem49; // center of next phoneme
+                    mem36 = mem49 - mem36; // center index of current phoneme
+                    A = Read(
+                        mem47, mem37); // value at center of next phoneme - end interpolation value
+                    //A = mem[address];
+
+                    Y = mem36; // start index of interpolation
+                    mem53 = A - Read(mem47, mem36); // value to center of current phoneme
+                } else {
+                    // value to interpolate to
+                    A = Read(mem47, speedcounter);
+                    // position to start interpolation from
+                    Y = phase3;
+                    // value to interpolate from
+                    mem53 = A - Read(mem47, phase3);
+                }
+
+                //Code47503(mem40);
+                // ML : Code47503 is division with remainder, and mem50 gets the sign
+
+                // calculate change per frame
+                signed char m53 = (signed char)mem53;
+                mem50 = mem53 & 128;
+                unsigned char m53abs = abs(m53);
+                mem51 = m53abs % mem40; //abs((char)m53) % mem40;
+                mem53 = (unsigned char)((signed char)(m53) / mem40);
+
+                // interpolation range
+                X = mem40; // number of frames to interpolate over
+                Y = phase3; // starting frame
+
+                // linearly interpolate values
+
+                mem56 = 0;
+                //47907: CLC
+                //pos47908:
+                while(1) //while No. 3
+                {
+                    A = Read(mem47, Y) + mem53; //carry alway cleared
+
+                    mem48 = A;
+                    Y++;
+                    X--;
+                    if(X == 0) break;
+
+                    mem56 += mem51;
+                    if(mem56 >= mem40) //???
+                    {
+                        mem56 -= mem40; //carry? is set
+                        //if ((mem56 & 128)==0)
+                        if((mem50 & 128) == 0) {
+                            //47935: BIT 50
+                            //47937: BMI 47943
+                            if(mem48 != 0) mem48++;
+                        } else
+                            mem48--;
+                    }
+                    //pos47945:
+                    Write(mem47, Y, mem48);
+                } //while No. 3
+
+                //pos47952:
+                mem47++;
+                //if (mem47 != 175) goto pos47810;
+            } while(mem47 != 175); //while No. 2
+        //pos47963:
+        mem44++;
+        X = mem44;
+    } //while No. 1
+
+    //goto pos47701;
+    //pos47970:
+
+    // add the length of this phoneme
+    mem48 = mem49 + phonemeLengthOutput[mem44];
+
+    // ASSIGN PITCH CONTOUR
+    //
+    // This subtracts the F1 frequency from the pitch to create a
+    // pitch contour. Without this, the output would be at a single
+    // pitch level (monotone).
+
+    // don't adjust pitch if in sing mode
+    if(!singmode) {
+        // iterate through the buffer
+        for(i = 0; i < 256; i++) {
+            // subtract half the frequency of the formant 1.
+            // this adds variety to the voice
+            pitches[i] -= (frequency1[i] >> 1);
+        }
+    }
+
+    phase1 = 0;
+    phase2 = 0;
+    phase3 = 0;
+    mem49 = 0;
+    speedcounter = 72; //sam standard speed
+
+    // RESCALE AMPLITUDE
+    //
+    // Rescale volume from a linear scale to decibels.
+    //
+
+    //amplitude rescaling
+    for(i = 255; i >= 0; i--) {
+        amplitude1[i] = amplitudeRescale[amplitude1[i]];
+        amplitude2[i] = amplitudeRescale[amplitude2[i]];
+        amplitude3[i] = amplitudeRescale[amplitude3[i]];
+    }
+
+    Y = 0;
+    A = pitches[0];
+    mem44 = A;
+    X = A;
+    mem38 = A - (A >> 2); // 3/4*A ???
+
+    // PROCESS THE FRAMES
+    //
+    // In traditional vocal synthesis, the glottal pulse drives filters, which
+    // are attenuated to the frequencies of the formants.
+    //
+    // SAM generates these formants directly with sin and rectangular waves.
+    // To simulate them being driven by the glottal pulse, the waveforms are
+    // reset at the beginning of each glottal pulse.
+
+    //finally the loop for sound output
+    //pos48078:
+    while(1) {
+        // get the sampled information on the phoneme
+        A = sampledConsonantFlag[Y];
+        mem39 = A;
+
+        // unvoiced sampled phoneme?
+        A = A & 248;
+        if(A != 0) {
+            // render the sample for the phoneme
+            RenderSample(&mem66);
+
+            // skip ahead two in the phoneme buffer
+            Y += 2;
+            mem48 -= 2;
+        } else {
+            // simulate the glottal pulse and formants
+            unsigned char ary[5];
+            unsigned int p1 =
+                phase1 * 256; // Fixed point integers because we need to divide later on
+            unsigned int p2 = phase2 * 256;
+            unsigned int p3 = phase3 * 256;
+            int k;
+            for(k = 0; k < 5; k++) {
+                signed char sp1 = (signed char)sinus[0xff & (p1 >> 8)];
+                signed char sp2 = (signed char)sinus[0xff & (p2 >> 8)];
+                signed char rp3 = (signed char)rectangle[0xff & (p3 >> 8)];
+                signed int sin1 = sp1 * ((unsigned char)amplitude1[Y] & 0x0f);
+                signed int sin2 = sp2 * ((unsigned char)amplitude2[Y] & 0x0f);
+                signed int rect = rp3 * ((unsigned char)amplitude3[Y] & 0x0f);
+                signed int mux = sin1 + sin2 + rect;
+                mux /= 32;
+                mux += 128; // Go from signed to unsigned amplitude
+                ary[k] = mux;
+                p1 += frequency1[Y] * 256 / 4; // Compromise, this becomes a shift and works well
+                p2 += frequency2[Y] * 256 / 4;
+                p3 += frequency3[Y] * 256 / 4;
+            }
+            // output the accumulated value
+            Output8BitAry(0, ary);
+            speedcounter--;
+            if(speedcounter != 0) goto pos48155;
+            Y++; //go to next amplitude
+
+            // decrement the frame count
+            mem48--;
+        }
+
+        // if the frame count is zero, exit the loop
+        if(mem48 == 0) return;
+        speedcounter = speed;
+    pos48155:
+
+        // decrement the remaining length of the glottal pulse
+        mem44--;
+
+        // finished with a glottal pulse?
+        if(mem44 == 0) {
+        pos48159:
+            // fetch the next glottal pulse length
+            A = pitches[Y];
+            mem44 = A;
+            A = A - (A >> 2);
+            mem38 = A;
+
+            // reset the formant wave generators to keep them in
+            // sync with the glottal pulse
+            phase1 = 0;
+            phase2 = 0;
+            phase3 = 0;
+            continue;
+        }
+
+        // decrement the count
+        mem38--;
+
+        // is the count non-zero and the sampled flag is zero?
+        if((mem38 != 0) || (mem39 == 0)) {
+            // reset the phase of the formants to match the pulse
+            phase1 += frequency1[Y];
+            phase2 += frequency2[Y];
+            phase3 += frequency3[Y];
+            continue;
+        }
+
+        // voiced sampled phonemes interleave the sample with the
+        // glottal pulse. The sample flag is non-zero, so render
+        // the sample for the phoneme.
+        RenderSample(&mem66);
+        goto pos48159;
+    } //while
+
+    // The following code is never reached. It's left over from when
+    // the voiced sample code was part of this loop, instead of part
+    // of RenderSample();
+
+    //pos48315:
+    int tempA;
+    phase1 = A ^ 255;
+    Y = mem66;
+    do {
+        //pos48321:
+
+        mem56 = 8;
+        A = Read(mem47, Y);
+
+        //pos48327:
+        do {
+            //48327: ASL A
+            //48328: BCC 48337
+            tempA = A;
+            A = A << 1;
+            if((tempA & 128) != 0) {
+                X = 26;
+                // mem[54296] = X;
+                bufferpos += 150;
+                //
+                //
+                //        buffer[bufferpos / 50] = (X & 15) * 16;
+                //
+                //
+
+            } else {
+                //mem[54296] = 6;
+                X = 6;
+                bufferpos += 150;
+                //
+                //        buffer[bufferpos / 50] = (X & 15) * 16;
+                //
+                //
+            }
+
+            for(X = wait2; X > 0; X--)
+                ; //wait
+            mem56--;
+        } while(mem56 != 0);
+
+        Y++;
+        phase1++;
+
+    } while(phase1 != 0);
+    //  if (phase1 != 0) goto pos48321;
+    A = 1;
+    mem44 = 1;
+    mem66 = Y;
+    Y = mem49;
+    return;
+}
+
+// Create a rising or falling inflection 30 frames prior to
+// index X. A rising inflection is used for questions, and
+// a falling inflection is used for statements.
+
+void STM32SAM::AddInflection(unsigned char mem48, unsigned char phase1) {
+    //pos48372:
+    //  mem48 = 255;
+    //pos48376:
+
+    // store the location of the punctuation
+    mem49 = X;
+    A = X;
+    int Atemp = A;
+
+    // backup 30 frames
+    A = A - 30;
+    // if index is before buffer, point to start of buffer
+    if(Atemp <= 30) A = 0;
+    X = A;
+
+    // FIXME: Explain this fix better, it's not obvious
+    // ML : A =, fixes a problem with invalid pitch with '.'
+    while((A = pitches[X]) == 127) X++;
+
+pos48398:
+    //48398: CLC
+    //48399: ADC 48
+
+    // add the inflection direction
+    A += mem48;
+    phase1 = A;
+
+    // set the inflection
+    pitches[X] = A;
+pos48406:
+
+    // increment the position
+    X++;
+
+    // exit if the punctuation has been reached
+    if(X == mem49) return; //goto pos47615;
+    if(pitches[X] == 255) goto pos48406;
+    A = phase1;
+    goto pos48398;
+}
+
+/*
+    SAM's voice can be altered by changing the frequencies of the
+    mouth formant (F1) and the throat formant (F2). Only the voiced
+    phonemes (5-29 and 48-53) are altered.
+*/
+void STM32SAM::SetMouthThroat() {
+    unsigned char initialFrequency;
+    unsigned char newFrequency = 0;
+    //unsigned char mouth; //mem38880
+    //unsigned char throat; //mem38881
+
+    // mouth formants (F1) 5..29
+    unsigned char mouthFormants5_29[30] = {0,  0,  0,  0,  0,  10, 14, 19, 24, 27,
+                                           23, 21, 16, 20, 14, 18, 14, 18, 18, 16,
+                                           13, 15, 11, 18, 14, 11, 9,  6,  6,  6};
+
+    // throat formants (F2) 5..29
+    unsigned char throatFormants5_29[30] = {255, 255, 255, 255, 255, 84, 73, 67, 63, 40,
+                                            44,  31,  37,  45,  73,  49, 36, 30, 51, 37,
+                                            29,  69,  24,  50,  30,  24, 83, 46, 54, 86};
+
+    // there must be no zeros in this 2 tables
+    // formant 1 frequencies (mouth) 48..53
+    unsigned char mouthFormants48_53[6] = {19, 27, 21, 27, 18, 13};
+
+    // formant 2 frequencies (throat) 48..53
+    unsigned char throatFormants48_53[6] = {72, 39, 31, 43, 30, 34};
+
+    unsigned char pos = 5; //mem39216
+    //pos38942:
+    // recalculate formant frequencies 5..29 for the mouth (F1) and throat (F2)
+    while(pos != 30) {
+        // recalculate mouth frequency
+        initialFrequency = mouthFormants5_29[pos];
+        if(initialFrequency != 0) newFrequency = trans(mouth, initialFrequency);
+        freq1data[pos] = newFrequency;
+
+        // recalculate throat frequency
+        initialFrequency = throatFormants5_29[pos];
+        if(initialFrequency != 0) newFrequency = trans(throat, initialFrequency);
+        freq2data[pos] = newFrequency;
+        pos++;
+    }
+
+    //pos39059:
+    // recalculate formant frequencies 48..53
+    pos = 48;
+    Y = 0;
+    while(pos != 54) {
+        // recalculate F1 (mouth formant)
+        initialFrequency = mouthFormants48_53[Y];
+        newFrequency = trans(mouth, initialFrequency);
+        freq1data[pos] = newFrequency;
+
+        // recalculate F2 (throat formant)
+        initialFrequency = throatFormants48_53[Y];
+        newFrequency = trans(throat, initialFrequency);
+        freq2data[pos] = newFrequency;
+        Y++;
+        pos++;
+    }
+}
+
+//return = (mem39212*mem39213) >> 1
+unsigned char STM32SAM::trans(unsigned char mem39212, unsigned char mem39213) {
+    //pos39008:
+    unsigned char carry;
+    int temp;
+    unsigned char mem39214, mem39215;
+    A = 0;
+    mem39215 = 0;
+    mem39214 = 0;
+    X = 8;
+    do {
+        carry = mem39212 & 1;
+        mem39212 = mem39212 >> 1;
+        if(carry != 0) {
+            /*
+                  39018: LSR 39212
+                  39021: BCC 39033
+      */
+            carry = 0;
+            A = mem39215;
+            temp = (int)A + (int)mem39213;
+            A = A + mem39213;
+            if(temp > 255) carry = 1;
+            mem39215 = A;
+        }
+        temp = mem39215 & 1;
+        mem39215 = (mem39215 >> 1) | (carry ? 128 : 0);
+        carry = temp;
+        //39033: ROR 39215
+        X--;
+    } while(X != 0);
+    temp = mem39214 & 128;
+    mem39214 = (mem39214 << 1) | (carry ? 1 : 0);
+    carry = temp;
+    temp = mem39215 & 128;
+    mem39215 = (mem39215 << 1) | (carry ? 1 : 0);
+    carry = temp;
+
+    return mem39215;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           Sam
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+//char input[]={"/HAALAOAO MAYN NAAMAEAE IHSTT SAEBAASTTIHAAN \x9b\x9b\0"};
+//unsigned char input[]={"/HAALAOAO \x9b\0"};
+//unsigned char input[]={"AA \x9b\0"};
+//unsigned char input[] = {"GUH5DEHN TAEG\x9b\0"};
+
+//unsigned char input[]={"AY5 AEM EY TAO4LXKIHNX KAX4MPYUX4TAH. GOW4 AH/HEH3D PAHNK.MEYK MAY8 DEY.\x9b\0"};
+//unsigned char input[]={"/HEH3LOW2, /HAW AH YUX2 TUXDEY. AY /HOH3P YUX AH FIYLIHNX OW4 KEY.\x9b\0"};
+//unsigned char input[]={"/HEY2, DHIHS IH3Z GREY2T. /HAH /HAH /HAH.AYL BIY5 BAEK.\x9b\0"};
+//unsigned char input[]={"/HAH /HAH /HAH \x9b\0"};
+//unsigned char input[]={"/HAH /HAH /HAH.\x9b\0"};
+//unsigned char input[]={".TUW BIY5Y3,, OHR NAA3T - TUW BIY5IYIY., DHAE4T IHZ DHAH KWEH4SCHAHN.\x9b\0"};
+//unsigned char input[]={"/HEY2, DHIHS \x9b\0"};
+
+//unsigned char input[]={" IYIHEHAEAAAHAOOHUHUXERAXIX  \x9b\0"};
+//unsigned char input[]={" RLWWYMNNXBDGJZZHVDH \x9b\0"};
+//unsigned char input[]={" SSHFTHPTKCH/H \x9b\0"};
+
+//unsigned char input[]={" EYAYOYAWOWUW ULUMUNQ YXWXRXLX/XDX\x9b\0"};
+
+void STM32SAM::SetInput(char* _input) {
+    int i, l;
+    l = strlen(_input);
+    if(l > 254) l = 254;
+    for(i = 0; i < l; i++) {
+        input[i] = _input[i];
+    }
+    input[l] = 0;
+}
+
+// 168=pitches
+// 169=frequency1
+// 170=frequency2
+// 171=frequency3
+// 172=amplitude1
+// 173=amplitude2
+// 174=amplitude3
+
+void STM32SAM::Init() {
+    bufferpos = 0;
+    int i;
+    SetMouthThroat();
+
+    bufferpos = 0;
+    // TODO, check for free the memory, 10 seconds of output should be more than enough
+    //buffer = malloc(22050*10);
+
+    // buffer = (char*) calloc(1, sizeof(char));
+
+    /*
+    freq2data = &mem[45136];
+    freq1data = &mem[45056];
+    freq3data = &mem[45216];
+  */
+    //pitches = &mem[43008];
+    /*
+    frequency1 = &mem[43264];
+    frequency2 = &mem[43520];
+    frequency3 = &mem[43776];
+  */
+    /*
+    amplitude1 = &mem[44032];
+    amplitude2 = &mem[44288];
+    amplitude3 = &mem[44544];
+  */
+    //phoneme = &mem[39904];
+    /*
+    ampl1data = &mem[45296];
+    ampl2data = &mem[45376];
+    ampl3data = &mem[45456];
+  */
+
+    for(i = 0; i < 256; i++) {
+        stress[i] = 0;
+        phonemeLength[i] = 0;
+    }
+
+    for(i = 0; i < 60; i++) {
+        phonemeIndexOutput[i] = 0;
+        stressOutput[i] = 0;
+        phonemeLengthOutput[i] = 0;
+    }
+    phonemeindex[255] =
+        255; //to prevent buffer overflow // ML : changed from 32 to 255 to stop freezing with long inputs
+}
+
+//int Code39771()
+int STM32SAM::SAMMain() {
+    Init();
+    phonemeindex[255] = 32; //to prevent buffer overflow
+
+    if(!Parser1()) {
+        return 0;
+    }
+
+    Parser2();
+    CopyStress();
+    SetPhonemeLength();
+    AdjustLengths();
+    Code41240();
+    do {
+        A = phonemeindex[X];
+        if(A > 80) {
+            phonemeindex[X] = 255;
+            break; // error: delete all behind it
+        }
+        X++;
+    } while(X != 0);
+
+    //pos39848:
+    InsertBreath();
+
+    //mem[40158] = 255;
+
+    PrepareOutput();
+
+    return 1;
+}
+
+//void Code48547()
+void STM32SAM::PrepareOutput() {
+    A = 0;
+    X = 0;
+    Y = 0;
+
+    //pos48551:
+    while(1) {
+        A = phonemeindex[X];
+        if(A == 255) {
+            A = 255;
+            phonemeIndexOutput[Y] = 255;
+            Render();
+            return;
+        }
+        if(A == 254) {
+            X++;
+            int temp = X;
+            //mem[48546] = X;
+            phonemeIndexOutput[Y] = 255;
+            Render();
+            //X = mem[48546];
+            X = temp;
+            Y = 0;
+            continue;
+        }
+
+        if(A == 0) {
+            X++;
+            continue;
+        }
+
+        phonemeIndexOutput[Y] = A;
+        phonemeLengthOutput[Y] = phonemeLength[X];
+        stressOutput[Y] = stress[X];
+        X++;
+        Y++;
+    }
+}
+
+//void Code41014()
+void STM32SAM::Insert(
+    unsigned char position /*var57*/,
+    unsigned char mem60,
+    unsigned char mem59,
+    unsigned char mem58) {
+    int i;
+    for(i = 253; i >= position; i--) // ML : always keep last safe-guarding 255
+    {
+        phonemeindex[i + 1] = phonemeindex[i];
+        phonemeLength[i + 1] = phonemeLength[i];
+        stress[i + 1] = stress[i];
+    }
+
+    phonemeindex[position] = mem60;
+    phonemeLength[position] = mem59;
+    stress[position] = mem58;
+    return;
+}
+
+//void Code48431()
+void STM32SAM::InsertBreath() {
+    unsigned char mem54;
+    unsigned char mem55;
+    unsigned char index; //variable Y
+    mem54 = 255;
+    X++;
+    mem55 = 0;
+    unsigned char mem66 = 0;
+    while(1) {
+        //pos48440:
+        X = mem66;
+        index = phonemeindex[X];
+        if(index == 255) return;
+        mem55 += phonemeLength[X];
+
+        if(mem55 < 232) {
+            if(index != 254) // ML : Prevents an index out of bounds problem
+            {
+                A = flags2[index] & 1;
+                if(A != 0) {
+                    X++;
+                    mem55 = 0;
+                    Insert(X, 254, mem59, 0);
+                    mem66++;
+                    mem66++;
+                    continue;
+                }
+            }
+            if(index == 0) mem54 = X;
+            mem66++;
+            continue;
+        }
+        X = mem54;
+        phonemeindex[X] = 31; // 'Q*' glottal stop
+        phonemeLength[X] = 4;
+        stress[X] = 0;
+        X++;
+        mem55 = 0;
+        Insert(X, 254, mem59, 0);
+        X++;
+        mem66 = X;
+    }
+}
+
+// Iterates through the phoneme buffer, copying the stress value from
+// the following phoneme under the following circumstance:
+
+//     1. The current phoneme is voiced, excluding plosives and fricatives
+//     2. The following phoneme is voiced, excluding plosives and fricatives, and
+//     3. The following phoneme is stressed
+//
+//  In those cases, the stress value+1 from the following phoneme is copied.
+//
+// For example, the word LOITER is represented as LOY5TER, with as stress
+// of 5 on the diphtong OY. This routine will copy the stress value of 6 (5+1)
+// to the L that precedes it.
+
+//void Code41883()
+void STM32SAM::CopyStress() {
+    // loop thought all the phonemes to be output
+    unsigned char pos = 0; //mem66
+    while(1) {
+        // get the phomene
+        Y = phonemeindex[pos];
+
+        // exit at end of buffer
+        if(Y == 255) return;
+
+        // if CONSONANT_FLAG set, skip - only vowels get stress
+        if((flags[Y] & 64) == 0) {
+            pos++;
+            continue;
+        }
+        // get the next phoneme
+        Y = phonemeindex[pos + 1];
+        if(Y == 255) //prevent buffer overflow
+        {
+            pos++;
+            continue;
+        } else
+            // if the following phoneme is a vowel, skip
+            if((flags[Y] & 128) == 0) {
+                pos++;
+                continue;
+            }
+
+        // get the stress value at the next position
+        Y = stress[pos + 1];
+
+        // if next phoneme is not stressed, skip
+        if(Y == 0) {
+            pos++;
+            continue;
+        }
+
+        // if next phoneme is not a VOWEL OR ER, skip
+        if((Y & 128) != 0) {
+            pos++;
+            continue;
+        }
+
+        // copy stress from prior phoneme to this one
+        stress[pos] = Y + 1;
+
+        // advance pointer
+        pos++;
+    }
+}
+
+// The input[] buffer contains a string of phonemes and stress markers along
+// the lines of:
+//
+//     DHAX KAET IHZ AH5GLIY. <0x9B>
+//
+// The byte 0x9B marks the end of the buffer. Some phonemes are 2 bytes
+// long, such as "DH" and "AX". Others are 1 byte long, such as "T" and "Z".
+// There are also stress markers, such as "5" and ".".
+//
+// The first character of the phonemes are stored in the table signInputTable1[].
+// The second character of the phonemes are stored in the table signInputTable2[].
+// The stress characters are arranged in low to high stress order in stressInputTable[].
+//
+// The following process is used to parse the input[] buffer:
+//
+// Repeat until the <0x9B> character is reached:
+//
+//        First, a search is made for a 2 character match for phonemes that do not
+//        end with the '*' (wildcard) character. On a match, the index of the phoneme
+//        is added to phonemeIndex[] and the buffer position is advanced 2 bytes.
+//
+//        If this fails, a search is made for a 1 character match against all
+//        phoneme names ending with a '*' (wildcard). If this succeeds, the
+//        phoneme is added to phonemeIndex[] and the buffer position is advanced
+//        1 byte.
+//
+//        If this fails, search for a 1 character match in the stressInputTable[].
+//        If this succeeds, the stress value is placed in the last stress[] table
+//        at the same index of the last added phoneme, and the buffer position is
+//        advanced by 1 byte.
+//
+//        If this fails, return a 0.
+//
+// On success:
+//
+//    1. phonemeIndex[] will contain the index of all the phonemes.
+//    2. The last index in phonemeIndex[] will be 255.
+//    3. stress[] will contain the stress value for each phoneme
+
+// input[] holds the string of phonemes, each two bytes wide
+// signInputTable1[] holds the first character of each phoneme
+// signInputTable2[] holds te second character of each phoneme
+// phonemeIndex[] holds the indexes of the phonemes after parsing input[]
+//
+// The parser scans through the input[], finding the names of the phonemes
+// by searching signInputTable1[] and signInputTable2[]. On a match, it
+// copies the index of the phoneme into the phonemeIndexTable[].
+//
+// The character <0x9B> marks the end of text in input[]. When it is reached,
+// the index 255 is placed at the end of the phonemeIndexTable[], and the
+// function returns with a 1 indicating success.
+int STM32SAM::Parser1() {
+    int i;
+    unsigned char sign1;
+    unsigned char sign2;
+    unsigned char position = 0;
+    X = 0;
+    A = 0;
+    Y = 0;
+
+    // CLEAR THE STRESS TABLE
+    for(i = 0; i < 256; i++) stress[i] = 0;
+
+    // THIS CODE MATCHES THE PHONEME LETTERS TO THE TABLE
+    // pos41078:
+    while(1) {
+        // GET THE FIRST CHARACTER FROM THE PHONEME BUFFER
+        sign1 = input[X];
+        // TEST FOR 155 (�) END OF LINE MARKER
+        if(sign1 == 155) {
+            // MARK ENDPOINT AND RETURN
+            phonemeindex[position] = 255; //mark endpoint
+            // REACHED END OF PHONEMES, SO EXIT
+            return 1; //all ok
+        }
+
+        // GET THE NEXT CHARACTER FROM THE BUFFER
+        X++;
+        sign2 = input[X];
+
+        // NOW sign1 = FIRST CHARACTER OF PHONEME, AND sign2 = SECOND CHARACTER OF PHONEME
+
+        // TRY TO MATCH PHONEMES ON TWO TWO-CHARACTER NAME
+        // IGNORE PHONEMES IN TABLE ENDING WITH WILDCARDS
+
+        // SET INDEX TO 0
+        Y = 0;
+    pos41095:
+
+        // GET FIRST CHARACTER AT POSITION Y IN signInputTable
+        // --> should change name to PhonemeNameTable1
+        A = signInputTable1[Y];
+
+        // FIRST CHARACTER MATCHES?
+        if(A == sign1) {
+            // GET THE CHARACTER FROM THE PhonemeSecondLetterTable
+            A = signInputTable2[Y];
+            // NOT A SPECIAL AND MATCHES SECOND CHARACTER?
+            if((A != '*') && (A == sign2)) {
+                // STORE THE INDEX OF THE PHONEME INTO THE phomeneIndexTable
+                phonemeindex[position] = Y;
+
+                // ADVANCE THE POINTER TO THE phonemeIndexTable
+                position++;
+                // ADVANCE THE POINTER TO THE phonemeInputBuffer
+                X++;
+
+                // CONTINUE PARSING
+                continue;
+            }
+        }
+
+        // NO MATCH, TRY TO MATCH ON FIRST CHARACTER TO WILDCARD NAMES (ENDING WITH '*')
+
+        // ADVANCE TO THE NEXT POSITION
+        Y++;
+        // IF NOT END OF TABLE, CONTINUE
+        if(Y != 81) goto pos41095;
+
+        // REACHED END OF TABLE WITHOUT AN EXACT (2 CHARACTER) MATCH.
+        // THIS TIME, SEARCH FOR A 1 CHARACTER MATCH AGAINST THE WILDCARDS
+
+        // RESET THE INDEX TO POINT TO THE START OF THE PHONEME NAME TABLE
+        Y = 0;
+    pos41134:
+        // DOES THE PHONEME IN THE TABLE END WITH '*'?
+        if(signInputTable2[Y] == '*') {
+            // DOES THE FIRST CHARACTER MATCH THE FIRST LETTER OF THE PHONEME
+            if(signInputTable1[Y] == sign1) {
+                // SAVE THE POSITION AND MOVE AHEAD
+                phonemeindex[position] = Y;
+
+                // ADVANCE THE POINTER
+                position++;
+
+                // CONTINUE THROUGH THE LOOP
+                continue;
+            }
+        }
+        Y++;
+        if(Y != 81) goto pos41134; //81 is size of PHONEME NAME table
+
+        // FAILED TO MATCH WITH A WILDCARD. ASSUME THIS IS A STRESS
+        // CHARACTER. SEARCH THROUGH THE STRESS TABLE
+
+        // SET INDEX TO POSITION 8 (END OF STRESS TABLE)
+        Y = 8;
+
+        // WALK BACK THROUGH TABLE LOOKING FOR A MATCH
+        while((sign1 != stressInputTable[Y]) && (Y > 0)) {
+            // DECREMENT INDEX
+            Y--;
+        }
+
+        // REACHED THE END OF THE SEARCH WITHOUT BREAKING OUT OF LOOP?
+        if(Y == 0) {
+            //mem[39444] = X;
+            //41181: JSR 42043 //Error
+            // FAILED TO MATCH ANYTHING, RETURN 0 ON FAILURE
+            return 0;
+        }
+        // SET THE STRESS FOR THE PRIOR PHONEME
+        stress[position - 1] = Y;
+    } //while
+}
+
+//change phonemelength depedendent on stress
+//void Code41203()
+void STM32SAM::SetPhonemeLength() {
+    unsigned char A;
+    int position = 0;
+    while(phonemeindex[position] != 255) {
+        A = stress[position];
+        //41218: BMI 41229
+        if((A == 0) || ((A & 128) != 0)) {
+            phonemeLength[position] = phonemeLengthTable[phonemeindex[position]];
+        } else {
+            phonemeLength[position] = phonemeStressedLengthTable[phonemeindex[position]];
+        }
+        position++;
+    }
+}
+
+void STM32SAM::Code41240() {
+    unsigned char pos = 0;
+
+    while(phonemeindex[pos] != 255) {
+        unsigned char index; //register AC
+        X = pos;
+        index = phonemeindex[pos];
+        if((flags[index] & 2) == 0) {
+            pos++;
+            continue;
+        } else if((flags[index] & 1) == 0) {
+            Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]);
+            Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]);
+            pos += 3;
+            continue;
+        }
+
+        do {
+            X++;
+            A = phonemeindex[X];
+        } while(A == 0);
+
+        if(A != 255) {
+            if((flags[A] & 8) != 0) {
+                pos++;
+                continue;
+            }
+            if((A == 36) || (A == 37)) {
+                pos++; // '/H' '/X'
+                continue;
+            }
+        }
+
+        Insert(pos + 1, index + 1, phonemeLengthTable[index + 1], stress[pos]);
+        Insert(pos + 2, index + 2, phonemeLengthTable[index + 2], stress[pos]);
+        pos += 3;
+    }
+}
+
+// Rewrites the phonemes using the following rules:
+//
+//       <DIPHTONG ENDING WITH WX> -> <DIPHTONG ENDING WITH WX> WX
+//       <DIPHTONG NOT ENDING WITH WX> -> <DIPHTONG NOT ENDING WITH WX> YX
+//       UL -> AX L
+//       UM -> AX M
+//       <STRESSED VOWEL> <SILENCE> <STRESSED VOWEL> -> <STRESSED VOWEL> <SILENCE> Q <VOWEL>
+//       T R -> CH R
+//       D R -> J R
+//       <VOWEL> R -> <VOWEL> RX
+//       <VOWEL> L -> <VOWEL> LX
+//       G S -> G Z
+//       K <VOWEL OR DIPHTONG NOT ENDING WITH IY> -> KX <VOWEL OR DIPHTONG NOT ENDING WITH IY>
+//       G <VOWEL OR DIPHTONG NOT ENDING WITH IY> -> GX <VOWEL OR DIPHTONG NOT ENDING WITH IY>
+//       S P -> S B
+//       S T -> S D
+//       S K -> S G
+//       S KX -> S GX
+//       <ALVEOLAR> UW -> <ALVEOLAR> UX
+//       CH -> CH CH' (CH requires two phonemes to represent it)
+//       J -> J J' (J requires two phonemes to represent it)
+//       <UNSTRESSED VOWEL> T <PAUSE> -> <UNSTRESSED VOWEL> DX <PAUSE>
+//       <UNSTRESSED VOWEL> D <PAUSE>  -> <UNSTRESSED VOWEL> DX <PAUSE>
+
+//void Code41397()
+void STM32SAM::Parser2() {
+    unsigned char pos = 0; //mem66;
+    unsigned char mem58 = 0;
+
+    // Loop through phonemes
+    while(1) {
+        // SET X TO THE CURRENT POSITION
+        X = pos;
+        // GET THE PHONEME AT THE CURRENT POSITION
+        A = phonemeindex[pos];
+
+        // Is phoneme pause?
+        if(A == 0) {
+            // Move ahead to the
+            pos++;
+            continue;
+        }
+
+        // If end of phonemes flag reached, exit routine
+        if(A == 255) return;
+
+        // Copy the current phoneme index to Y
+        Y = A;
+
+        // RULE:
+        //       <DIPHTONG ENDING WITH WX> -> <DIPHTONG ENDING WITH WX> WX
+        //       <DIPHTONG NOT ENDING WITH WX> -> <DIPHTONG NOT ENDING WITH WX> YX
+        // Example: OIL, COW
+
+        // Check for DIPHTONG
+        if((flags[A] & 16) == 0) goto pos41457;
+
+        // Not a diphthong. Get the stress
+        mem58 = stress[pos];
+
+        // End in IY sound?
+        A = flags[Y] & 32;
+
+        // If ends with IY, use YX, else use WX
+        if(A == 0)
+            A = 20;
+        else
+            A = 21; // 'WX' = 20 'YX' = 21
+        //pos41443:
+        // Insert at WX or YX following, copying the stress
+
+        Insert(pos + 1, A, mem59, mem58);
+        X = pos;
+        // Jump to ???
+        goto pos41749;
+
+    pos41457:
+
+        // RULE:
+        //       UL -> AX L
+        // Example: MEDDLE
+
+        // Get phoneme
+        A = phonemeindex[X];
+        // Skip this rule if phoneme is not UL
+        if(A != 78) goto pos41487; // 'UL'
+        A = 24; // 'L'                 //change 'UL' to 'AX L'
+
+    pos41466:
+        // Get current phoneme stress
+        mem58 = stress[X];
+
+        // Change UL to AX
+        phonemeindex[X] = 13; // 'AX'
+        // Perform insert. Note code below may jump up here with different values
+        Insert(X + 1, A, mem59, mem58);
+        pos++;
+        // Move to next phoneme
+        continue;
+
+    pos41487:
+
+        // RULE:
+        //       UM -> AX M
+        // Example: ASTRONOMY
+
+        // Skip rule if phoneme != UM
+        if(A != 79) goto pos41495; // 'UM'
+        // Jump up to branch - replaces current phoneme with AX and continues
+        A = 27; // 'M'  //change 'UM' to  'AX M'
+
+        goto pos41466;
+    pos41495:
+
+        // RULE:
+        //       UN -> AX N
+        // Example: FUNCTION
+
+        // Skip rule if phoneme != UN
+        if(A != 80) goto pos41503; // 'UN'
+
+        // Jump up to branch - replaces current phoneme with AX and continues
+        A = 28; // 'N' //change UN to 'AX N'
+
+        goto pos41466;
+    pos41503:
+
+        // RULE:
+        //       <STRESSED VOWEL> <SILENCE> <STRESSED VOWEL> -> <STRESSED VOWEL> <SILENCE> Q <VOWEL>
+        // EXAMPLE: AWAY EIGHT
+
+        Y = A;
+        // VOWEL set?
+        A = flags[A] & 128;
+
+        // Skip if not a vowel
+        if(A != 0) {
+            // Get the stress
+            A = stress[X];
+
+            // If stressed...
+            if(A != 0) {
+                // Get the following phoneme
+                X++;
+                A = phonemeindex[X];
+                // If following phoneme is a pause
+
+                if(A == 0) {
+                    // Get the phoneme following pause
+                    X++;
+                    Y = phonemeindex[X];
+
+                    // Check for end of buffer flag
+                    if(Y == 255) //buffer overflow
+                        // ??? Not sure about these flags
+                        A = 65 & 128;
+                    else
+                        // And VOWEL flag to current phoneme's flags
+                        A = flags[Y] & 128;
+
+                    // If following phonemes is not a pause
+                    if(A != 0) {
+                        // If the following phoneme is not stressed
+                        A = stress[X];
+                        if(A != 0) {
+                            // 31 = 'Q'
+                            Insert(X, 31, mem59, 0);
+                            pos++;
+                            continue;
+                        }
+                    }
+                }
+            }
+        }
+
+        // RULES FOR PHONEMES BEFORE R
+        //        T R -> CH R
+        // Example: TRACK
+
+        // Get current position and phoneme
+        X = pos;
+        A = phonemeindex[pos];
+        if(A != 23) goto pos41611; // 'R'
+
+        // Look at prior phoneme
+        X--;
+        A = phonemeindex[pos - 1];
+        //pos41567:
+        if(A == 69) // 'T'
+        {
+            phonemeindex[pos - 1] = 42;
+            goto pos41779;
+        }
+
+        // RULES FOR PHONEMES BEFORE R
+        //        D R -> J R
+        // Example: DRY
+
+        // Prior phonemes D?
+        if(A == 57) // 'D'
+        {
+            // Change D to J
+            phonemeindex[pos - 1] = 44;
+
+            goto pos41788;
+        }
+
+        // RULES FOR PHONEMES BEFORE R
+        //        <VOWEL> R -> <VOWEL> RX
+        // Example: ART
+
+        // If vowel flag is set change R to RX
+        A = flags[A] & 128;
+
+        if(A != 0) phonemeindex[pos] = 18; // 'RX'
+
+        // continue to next phoneme
+        pos++;
+        continue;
+
+    pos41611:
+
+        // RULE:
+        //       <VOWEL> L -> <VOWEL> LX
+        // Example: ALL
+
+        // Is phoneme L?
+        if(A == 24) // 'L'
+        {
+            // If prior phoneme does not have VOWEL flag set, move to next phoneme
+            if((flags[phonemeindex[pos - 1]] & 128) == 0) {
+                pos++;
+                continue;
+            }
+            // Prior phoneme has VOWEL flag set, so change L to LX and move to next phoneme
+
+            phonemeindex[X] = 19; // 'LX'
+            pos++;
+            continue;
+        }
+
+        // RULE:
+        //       G S -> G Z
+        //
+        // Can't get to fire -
+        //       1. The G -> GX rule intervenes
+        //       2. Reciter already replaces GS -> GZ
+
+        // Is current phoneme S?
+        if(A == 32) // 'S'
+        {
+            // If prior phoneme is not G, move to next phoneme
+            if(phonemeindex[pos - 1] != 60) {
+                pos++;
+                continue;
+            }
+            // Replace S with Z and move on
+
+            phonemeindex[pos] = 38; // 'Z'
+            pos++;
+            continue;
+        }
+
+        // RULE:
+        //             K <VOWEL OR DIPHTONG NOT ENDING WITH IY> -> KX <VOWEL OR DIPHTONG NOT ENDING WITH IY>
+        // Example: COW
+
+        // Is current phoneme K?
+        if(A == 72) // 'K'
+        {
+            // Get next phoneme
+            Y = phonemeindex[pos + 1];
+            // If at end, replace current phoneme with KX
+            if(Y == 255)
+                phonemeindex[pos] = 75; // ML : prevents an index out of bounds problem
+            else {
+                // VOWELS AND DIPHTONGS ENDING WITH IY SOUND flag set?
+                A = flags[Y] & 32;
+
+                // Replace with KX
+                if(A == 0) phonemeindex[pos] = 75; // 'KX'
+            }
+        } else
+
+            // RULE:
+            //             G <VOWEL OR DIPHTONG NOT ENDING WITH IY> -> GX <VOWEL OR DIPHTONG NOT ENDING WITH IY>
+            // Example: GO
+
+            // Is character a G?
+            if(A == 60) // 'G'
+            {
+                // Get the following character
+                unsigned char index = phonemeindex[pos + 1];
+
+                // At end of buffer?
+                if(index == 255) //prevent buffer overflow
+                {
+                    pos++;
+                    continue;
+                } else
+                    // If diphtong ending with YX, move continue processing next phoneme
+                    if((flags[index] & 32) != 0) {
+                        pos++;
+                        continue;
+                    }
+                // replace G with GX and continue processing next phoneme
+
+                phonemeindex[pos] = 63; // 'GX'
+                pos++;
+                continue;
+            }
+
+        // RULE:
+        //      S P -> S B
+        //      S T -> S D
+        //      S K -> S G
+        //      S KX -> S GX
+        // Examples: SPY, STY, SKY, SCOWL
+
+        Y = phonemeindex[pos];
+        //pos41719:
+        // Replace with softer version?
+        A = flags[Y] & 1;
+        if(A == 0) goto pos41749;
+        A = phonemeindex[pos - 1];
+        if(A != 32) // 'S'
+        {
+            A = Y;
+            goto pos41812;
+        }
+        // Replace with softer version
+
+        phonemeindex[pos] = Y - 12;
+        pos++;
+        continue;
+
+    pos41749:
+
+        // RULE:
+        //      <ALVEOLAR> UW -> <ALVEOLAR> UX
+        //
+        // Example: NEW, DEW, SUE, ZOO, THOO, TOO
+
+        //       UW -> UX
+
+        A = phonemeindex[X];
+        if(A == 53) // 'UW'
+        {
+            // ALVEOLAR flag set?
+            Y = phonemeindex[X - 1];
+            A = flags2[Y] & 4;
+            // If not set, continue processing next phoneme
+            if(A == 0) {
+                pos++;
+                continue;
+            }
+
+            phonemeindex[X] = 16;
+            pos++;
+            continue;
+        }
+    pos41779:
+
+        // RULE:
+        //       CH -> CH CH' (CH requires two phonemes to represent it)
+        // Example: CHEW
+
+        if(A == 42) // 'CH'
+        {
+            //        pos41783:
+
+            Insert(X + 1, A + 1, mem59, stress[X]);
+            pos++;
+            continue;
+        }
+
+    pos41788:
+
+        // RULE:
+        //       J -> J J' (J requires two phonemes to represent it)
+        // Example: JAY
+
+        if(A == 44) // 'J'
+        {
+            Insert(X + 1, A + 1, mem59, stress[X]);
+            pos++;
+            continue;
+        }
+
+        // Jump here to continue
+    pos41812:
+
+        // RULE: Soften T following vowel
+        // NOTE: This rule fails for cases such as "ODD"
+        //       <UNSTRESSED VOWEL> T <PAUSE> -> <UNSTRESSED VOWEL> DX <PAUSE>
+        //       <UNSTRESSED VOWEL> D <PAUSE>  -> <UNSTRESSED VOWEL> DX <PAUSE>
+        // Example: PARTY, TARDY
+
+        // Past this point, only process if phoneme is T or D
+
+        if(A != 69) // 'T'
+            if(A != 57) {
+                pos++; // 'D'
+                continue;
+            }
+        //pos41825:
+
+        // If prior phoneme is not a vowel, continue processing phonemes
+        if((flags[phonemeindex[X - 1]] & 128) == 0) {
+            pos++;
+            continue;
+        }
+
+        // Get next phoneme
+        X++;
+        A = phonemeindex[X];
+        //pos41841
+        // Is the next phoneme a pause?
+        if(A != 0) {
+            // If next phoneme is not a pause, continue processing phonemes
+            if((flags[A] & 128) == 0) {
+                pos++;
+                continue;
+            }
+            // If next phoneme is stressed, continue processing phonemes
+            // FIXME: How does a pause get stressed?
+            if(stress[X] != 0) {
+                pos++;
+                continue;
+            }
+            //pos41856:
+            // Set phonemes to DX
+
+            phonemeindex[pos] = 30; // 'DX'
+        } else {
+            A = phonemeindex[X + 1];
+            if(A == 255) //prevent buffer overflow
+                A = 65 & 128;
+            else
+                // Is next phoneme a vowel or ER?
+                A = flags[A] & 128;
+
+            if(A != 0) phonemeindex[pos] = 30; // 'DX'
+        }
+
+        pos++;
+
+    } // while
+} // parser 2
+
+// Applies various rules that adjust the lengths of phonemes
+//
+//         Lengthen <FRICATIVE> or <VOICED> between <VOWEL> and <PUNCTUATION> by 1.5
+//         <VOWEL> <RX | LX> <CONSONANT> - decrease <VOWEL> length by 1
+//         <VOWEL> <UNVOICED PLOSIVE> - decrease vowel by 1/8th
+//         <VOWEL> <UNVOICED CONSONANT> - increase vowel by 1/2 + 1
+//         <NASAL> <STOP CONSONANT> - set nasal = 5, consonant = 6
+//         <VOICED STOP CONSONANT> {optional silence} <STOP CONSONANT> - shorten both to 1/2 + 1
+//         <LIQUID CONSONANT> <DIPHTONG> - decrease by 2
+
+//void Code48619()
+void STM32SAM::AdjustLengths() {
+    // LENGTHEN VOWELS PRECEDING PUNCTUATION
+    //
+    // Search for punctuation. If found, back up to the first vowel, then
+    // process all phonemes between there and up to (but not including) the punctuation.
+    // If any phoneme is found that is a either a fricative or voiced, the duration is
+    // increased by (length * 1.5) + 1
+
+    // loop index
+    X = 0;
+    unsigned char index;
+
+    // iterate through the phoneme list
+    unsigned char loopIndex = 0;
+    while(1) {
+        // get a phoneme
+        index = phonemeindex[X];
+
+        // exit loop if end on buffer token
+        if(index == 255) break;
+
+        // not punctuation?
+        if((flags2[index] & 1) == 0) {
+            // skip
+            X++;
+            continue;
+        }
+
+        // hold index
+        loopIndex = X;
+
+        // Loop backwards from this point
+    pos48644:
+
+        // back up one phoneme
+        X--;
+
+        // stop once the beginning is reached
+        if(X == 0) break;
+
+        // get the preceding phoneme
+        index = phonemeindex[X];
+
+        if(index != 255) //inserted to prevent access overrun
+            if((flags[index] & 128) == 0) goto pos48644; // if not a vowel, continue looping
+
+        //pos48657:
+        do {
+            // test for vowel
+            index = phonemeindex[X];
+
+            if(index != 255) //inserted to prevent access overrun
+                // test for fricative/unvoiced or not voiced
+                if(((flags2[index] & 32) == 0) || ((flags[index] & 4) != 0)) //nochmal �berpr�fen
+                {
+                    //A = flags[Y] & 4;
+                    //if(A == 0) goto pos48688;
+
+                    // get the phoneme length
+                    A = phonemeLength[X];
+
+                    // change phoneme length to (length * 1.5) + 1
+                    A = (A >> 1) + A + 1;
+
+                    phonemeLength[X] = A;
+                }
+            // keep moving forward
+            X++;
+        } while(X != loopIndex);
+        //  if (X != loopIndex) goto pos48657;
+        X++;
+    } // while
+
+    // Similar to the above routine, but shorten vowels under some circumstances
+
+    // Loop throught all phonemes
+    loopIndex = 0;
+    //pos48697
+
+    while(1) {
+        // get a phoneme
+        X = loopIndex;
+        index = phonemeindex[X];
+
+        // exit routine at end token
+        if(index == 255) return;
+
+        // vowel?
+        A = flags[index] & 128;
+        if(A != 0) {
+            // get next phoneme
+            X++;
+            index = phonemeindex[X];
+
+            // get flags
+            if(index == 255)
+                mem56 = 65; // use if end marker
+            else
+                mem56 = flags[index];
+
+            // not a consonant
+            if((flags[index] & 64) == 0) {
+                // RX or LX?
+                if((index == 18) || (index == 19)) // 'RX' & 'LX'
+                {
+                    // get the next phoneme
+                    X++;
+                    index = phonemeindex[X];
+
+                    // next phoneme a consonant?
+                    if((flags[index] & 64) != 0) {
+                        // RULE: <VOWEL> RX | LX <CONSONANT>
+
+                        // decrease length of vowel by 1 frame
+                        phonemeLength[loopIndex]--;
+                    }
+                    // move ahead
+                    loopIndex++;
+                    continue;
+                }
+                // move ahead
+                loopIndex++;
+                continue;
+            }
+
+            // Got here if not <VOWEL>
+
+            // not voiced
+            if((mem56 & 4) == 0) {
+                // Unvoiced
+                // *, .*, ?*, ,*, -*, DX, S*, SH, F*, TH, /H, /X, CH, P*, T*, K*, KX
+
+                // not an unvoiced plosive?
+                if((mem56 & 1) == 0) {
+                    // move ahead
+                    loopIndex++;
+                    continue;
+                }
+
+                // P*, T*, K*, KX
+
+                // RULE: <VOWEL> <UNVOICED PLOSIVE>
+                // <VOWEL> <P*, T*, K*, KX>
+
+                // move back
+                X--;
+
+                // decrease length by 1/8th
+                mem56 = phonemeLength[X] >> 3;
+                phonemeLength[X] -= mem56;
+
+                // move ahead
+                loopIndex++;
+                continue;
+            }
+
+            // RULE: <VOWEL> <VOICED CONSONANT>
+            // <VOWEL> <WH, R*, L*, W*, Y*, M*, N*, NX, DX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX>
+
+            // decrease length
+            A = phonemeLength[X - 1];
+            phonemeLength[X - 1] = (A >> 2) + A + 1; // 5/4*A + 1
+
+            // move ahead
+            loopIndex++;
+            continue;
+        }
+
+        // WH, R*, L*, W*, Y*, M*, N*, NX, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX
+
+        //pos48821:
+
+        // RULE: <NASAL> <STOP CONSONANT>
+        //       Set punctuation length to 6
+        //       Set stop consonant length to 5
+
+        // nasal?
+        if((flags2[index] & 8) != 0) {
+            // M*, N*, NX,
+
+            // get the next phoneme
+            X++;
+            index = phonemeindex[X];
+
+            // end of buffer?
+            if(index == 255)
+                A = 65 & 2; //prevent buffer overflow
+            else
+                A = flags[index] & 2; // check for stop consonant
+
+            // is next phoneme a stop consonant?
+            if(A != 0)
+
+            // B*, D*, G*, GX, P*, T*, K*, KX
+
+            {
+                // set stop consonant length to 6
+                phonemeLength[X] = 6;
+
+                // set nasal length to 5
+                phonemeLength[X - 1] = 5;
+            }
+            // move to next phoneme
+            loopIndex++;
+            continue;
+        }
+
+        // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, B*, D*, G*, GX
+
+        // RULE: <VOICED STOP CONSONANT> {optional silence} <STOP CONSONANT>
+        //       Shorten both to (length/2 + 1)
+
+        // (voiced) stop consonant?
+        if((flags[index] & 2) != 0) {
+            // B*, D*, G*, GX
+
+            // move past silence
+            do {
+                // move ahead
+                X++;
+                index = phonemeindex[X];
+            } while(index == 0);
+
+            // check for end of buffer
+            if(index == 255) //buffer overflow
+            {
+                // ignore, overflow code
+                if((65 & 2) == 0) {
+                    loopIndex++;
+                    continue;
+                }
+            } else if((flags[index] & 2) == 0) {
+                // if another stop consonant, move ahead
+                loopIndex++;
+                continue;
+            }
+
+            // RULE: <UNVOICED STOP CONSONANT> {optional silence} <STOP CONSONANT>
+
+            // X gets overwritten, so hold prior X value for debug statement
+            // int debugX = X;
+            // shorten the prior phoneme length to (length/2 + 1)
+            phonemeLength[X] = (phonemeLength[X] >> 1) + 1;
+            X = loopIndex;
+
+            // also shorten this phoneme length to (length/2 +1)
+            phonemeLength[loopIndex] = (phonemeLength[loopIndex] >> 1) + 1;
+
+            // move ahead
+            loopIndex++;
+            continue;
+        }
+
+        // WH, R*, L*, W*, Y*, Q*, Z*, ZH, V*, DH, J*, **,
+
+        // RULE: <VOICED NON-VOWEL> <DIPHTONG>
+        //       Decrease <DIPHTONG> by 2
+
+        // liquic consonant?
+        if((flags2[index] & 16) != 0) {
+            // R*, L*, W*, Y*
+
+            // get the prior phoneme
+            index = phonemeindex[X - 1];
+
+            // prior phoneme a stop consonant>
+            if((flags[index] & 2) != 0) {
+                // Rule: <LIQUID CONSONANT> <DIPHTONG>
+
+                // decrease the phoneme length by 2 frames (20 ms)
+                phonemeLength[X] -= 2;
+            }
+        }
+
+        // move to next phoneme
+        loopIndex++;
+        continue;
+    }
+    //            goto pos48701;
+}
+
+// -------------------------------------------------------------------------
+// ML : Code47503 is division with remainder, and mem50 gets the sign
+void STM32SAM::Code47503(unsigned char mem52) {
+    Y = 0;
+    if((mem53 & 128) != 0) {
+        mem53 = -mem53;
+        Y = 128;
+    }
+    mem50 = Y;
+    A = 0;
+    for(X = 8; X > 0; X--) {
+        int temp = mem53;
+        mem53 = mem53 << 1;
+        A = A << 1;
+        if(temp >= 128) A++;
+        if(A >= mem52) {
+            A = A - mem52;
+            mem53++;
+        }
+    }
+
+    mem51 = A;
+    if((mem50 & 128) != 0) mem53 = -mem53;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           Reciter
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::Code37055(unsigned char mem59) {
+    X = mem59;
+    X--;
+    A = inputtemp[X];
+    Y = A;
+    A = tab36376[Y];
+    return;
+}
+
+void STM32SAM::Code37066(unsigned char mem58) {
+    X = mem58;
+    X++;
+    A = inputtemp[X];
+    Y = A;
+    A = tab36376[Y];
+}
+
+unsigned char STM32SAM::GetRuleByte(unsigned short mem62, unsigned char Y) {
+    unsigned int address = mem62;
+
+    if(mem62 >= 37541) {
+        address -= 37541;
+        return rules2[address + Y];
+    }
+    address -= 32000;
+    return rules[address + Y];
+}
+
+int STM32SAM::TextToPhonemes(unsigned char* input) // Code36484
+{
+    //unsigned char *tab39445 = &mem[39445];   //input and output
+    //unsigned char mem29;
+    unsigned char mem56; //output position for phonemes
+    unsigned char mem57;
+    unsigned char mem58;
+    unsigned char mem59;
+    unsigned char mem60;
+    unsigned char mem61;
+    unsigned short mem62; // memory position of current rule
+
+    unsigned char mem64; // position of '=' or current character
+    unsigned char mem65; // position of ')'
+    unsigned char mem66; // position of '('
+    unsigned char mem36653;
+
+    inputtemp[0] = 32;
+
+    // secure copy of input
+    // because input will be overwritten by phonemes
+    X = 1;
+    Y = 0;
+    do {
+        //pos36499:
+        A = input[Y] & 127;
+        if(A >= 112)
+            A = A & 95;
+        else if(A >= 96)
+            A = A & 79;
+
+        inputtemp[X] = A;
+        X++;
+        Y++;
+    } while(Y != 255);
+
+    X = 255;
+    inputtemp[X] = 27;
+    mem61 = 255;
+
+pos36550:
+    A = 255;
+    mem56 = 255;
+
+pos36554:
+    while(1) {
+        mem61++;
+        X = mem61;
+        A = inputtemp[X];
+        mem64 = A;
+        if(A == '[') {
+            mem56++;
+            X = mem56;
+            A = 155;
+            input[X] = 155;
+            //goto pos36542;
+            //          Code39771();    //Code39777();
+            return 1;
+        }
+
+        //pos36579:
+        if(A != '.') break;
+        X++;
+        Y = inputtemp[X];
+        A = tab36376[Y] & 1;
+        if(A != 0) break;
+        mem56++;
+        X = mem56;
+        A = '.';
+        input[X] = '.';
+    } //while
+
+    //pos36607:
+    A = mem64;
+    Y = A;
+    A = tab36376[A];
+    mem57 = A;
+    if((A & 2) != 0) {
+        mem62 = 37541;
+        goto pos36700;
+    }
+
+    //pos36630:
+    A = mem57;
+    if(A != 0) goto pos36677;
+    A = 32;
+    inputtemp[X] = ' ';
+    mem56++;
+    X = mem56;
+    if(X > 120) goto pos36654;
+    input[X] = A;
+    goto pos36554;
+
+    // -----
+
+    //36653 is unknown. Contains position
+
+pos36654:
+    input[X] = 155;
+    A = mem61;
+    mem36653 = A;
+    //  mem29 = A; // not used
+    //  Code36538(); das ist eigentlich
+    return 1;
+    //Code39771();
+    //go on if there is more input ???
+    mem61 = mem36653;
+    goto pos36550;
+
+pos36677:
+    A = mem57 & 128;
+    if(A == 0) {
+        //36683: BRK
+        return 0;
+    }
+
+    // go to the right rules for this character.
+    X = mem64 - 'A';
+    mem62 = tab37489[X] | (tab37515[X] << 8);
+
+    // -------------------------------------
+    // go to next rule
+    // -------------------------------------
+
+pos36700:
+
+    // find next rule
+    Y = 0;
+    do {
+        mem62 += 1;
+        A = GetRuleByte(mem62, Y);
+    } while((A & 128) == 0);
+    Y++;
+
+    //pos36720:
+    // find '('
+    while(1) {
+        A = GetRuleByte(mem62, Y);
+        if(A == '(') break;
+        Y++;
+    }
+    mem66 = Y;
+
+    //pos36732:
+    // find ')'
+    do {
+        Y++;
+        A = GetRuleByte(mem62, Y);
+    } while(A != ')');
+    mem65 = Y;
+
+    //pos36741:
+    // find '='
+    do {
+        Y++;
+        A = GetRuleByte(mem62, Y);
+        A = A & 127;
+    } while(A != '=');
+    mem64 = Y;
+
+    X = mem61;
+    mem60 = X;
+
+    // compare the string within the bracket
+    Y = mem66;
+    Y++;
+    //pos36759:
+    while(1) {
+        mem57 = inputtemp[X];
+        A = GetRuleByte(mem62, Y);
+        if(A != mem57) goto pos36700;
+        Y++;
+        if(Y == mem65) break;
+        X++;
+        mem60 = X;
+    }
+
+    // the string in the bracket is correct
+
+    //pos36787:
+    A = mem61;
+    mem59 = mem61;
+
+pos36791:
+    while(1) {
+        mem66--;
+        Y = mem66;
+        A = GetRuleByte(mem62, Y);
+        mem57 = A;
+        //36800: BPL 36805
+        if((A & 128) != 0) goto pos37180;
+        X = A & 127;
+        A = tab36376[X] & 128;
+        if(A == 0) break;
+        X = mem59 - 1;
+        A = inputtemp[X];
+        if(A != mem57) goto pos36700;
+        mem59 = X;
+    }
+
+    //pos36833:
+    A = mem57;
+    if(A == ' ') goto pos36895;
+    if(A == '#') goto pos36910;
+    if(A == '.') goto pos36920;
+    if(A == '&') goto pos36935;
+    if(A == '@') goto pos36967;
+    if(A == '^') goto pos37004;
+    if(A == '+') goto pos37019;
+    if(A == ':') goto pos37040;
+    //  Code42041();    //Error
+    //36894: BRK
+    return 0;
+
+    // --------------
+
+pos36895:
+    Code37055(mem59);
+    A = A & 128;
+    if(A != 0) goto pos36700;
+pos36905:
+    mem59 = X;
+    goto pos36791;
+
+    // --------------
+
+pos36910:
+    Code37055(mem59);
+    A = A & 64;
+    if(A != 0) goto pos36905;
+    goto pos36700;
+
+    // --------------
+
+pos36920:
+    Code37055(mem59);
+    A = A & 8;
+    if(A == 0) goto pos36700;
+pos36930:
+    mem59 = X;
+    goto pos36791;
+
+    // --------------
+
+pos36935:
+    Code37055(mem59);
+    A = A & 16;
+    if(A != 0) goto pos36930;
+    A = inputtemp[X];
+    if(A != 72) goto pos36700;
+    X--;
+    A = inputtemp[X];
+    if((A == 67) || (A == 83)) goto pos36930;
+    goto pos36700;
+
+    // --------------
+
+pos36967:
+    Code37055(mem59);
+    A = A & 4;
+    if(A != 0) goto pos36930;
+    A = inputtemp[X];
+    if(A != 72) goto pos36700;
+    if((A != 84) && (A != 67) && (A != 83)) goto pos36700;
+    mem59 = X;
+    goto pos36791;
+
+    // --------------
+
+pos37004:
+    Code37055(mem59);
+    A = A & 32;
+    if(A == 0) goto pos36700;
+
+pos37014:
+    mem59 = X;
+    goto pos36791;
+
+    // --------------
+
+pos37019:
+    X = mem59;
+    X--;
+    A = inputtemp[X];
+    if((A == 'E') || (A == 'I') || (A == 'Y')) goto pos37014;
+    goto pos36700;
+    // --------------
+
+pos37040:
+    Code37055(mem59);
+    A = A & 32;
+    if(A == 0) goto pos36791;
+    mem59 = X;
+    goto pos37040;
+
+    //---------------------------------------
+
+pos37077:
+    X = mem58 + 1;
+    A = inputtemp[X];
+    if(A != 'E') goto pos37157;
+    X++;
+    Y = inputtemp[X];
+    X--;
+    A = tab36376[Y] & 128;
+    if(A == 0) goto pos37108;
+    X++;
+    A = inputtemp[X];
+    if(A != 'R') goto pos37113;
+pos37108:
+    mem58 = X;
+    goto pos37184;
+pos37113:
+    if((A == 83) || (A == 68)) goto pos37108; // 'S' 'D'
+    if(A != 76) goto pos37135; // 'L'
+    X++;
+    A = inputtemp[X];
+    if(A != 89) goto pos36700;
+    goto pos37108;
+
+pos37135:
+    if(A != 70) goto pos36700;
+    X++;
+    A = inputtemp[X];
+    if(A != 85) goto pos36700;
+    X++;
+    A = inputtemp[X];
+    if(A == 76) goto pos37108;
+    goto pos36700;
+
+pos37157:
+    if(A != 73) goto pos36700;
+    X++;
+    A = inputtemp[X];
+    if(A != 78) goto pos36700;
+    X++;
+    A = inputtemp[X];
+    if(A == 71) goto pos37108;
+    //pos37177:
+    goto pos36700;
+
+    // -----------------------------------------
+
+pos37180:
+
+    A = mem60;
+    mem58 = A;
+
+pos37184:
+    Y = mem65 + 1;
+
+    //37187: CPY 64
+    //  if(? != 0) goto pos37194;
+    if(Y == mem64) goto pos37455;
+    mem65 = Y;
+    //37196: LDA (62),y
+    A = GetRuleByte(mem62, Y);
+    mem57 = A;
+    X = A;
+    A = tab36376[X] & 128;
+    if(A == 0) goto pos37226;
+    X = mem58 + 1;
+    A = inputtemp[X];
+    if(A != mem57) goto pos36700;
+    mem58 = X;
+    goto pos37184;
+pos37226:
+    A = mem57;
+    if(A == 32) goto pos37295; // ' '
+    if(A == 35) goto pos37310; // '#'
+    if(A == 46) goto pos37320; // '.'
+    if(A == 38) goto pos37335; // '&'
+    if(A == 64) goto pos37367; // ''
+    if(A == 94) goto pos37404; // ''
+    if(A == 43) goto pos37419; // '+'
+    if(A == 58) goto pos37440; // ':'
+    if(A == 37) goto pos37077; // '%'
+    //pos37291:
+    //  Code42041(); //Error
+    //37294: BRK
+    return 0;
+
+    // --------------
+pos37295:
+    Code37066(mem58);
+    A = A & 128;
+    if(A != 0) goto pos36700;
+pos37305:
+    mem58 = X;
+    goto pos37184;
+
+    // --------------
+
+pos37310:
+    Code37066(mem58);
+    A = A & 64;
+    if(A != 0) goto pos37305;
+    goto pos36700;
+
+    // --------------
+
+pos37320:
+    Code37066(mem58);
+    A = A & 8;
+    if(A == 0) goto pos36700;
+
+pos37330:
+    mem58 = X;
+    goto pos37184;
+
+    // --------------
+
+pos37335:
+    Code37066(mem58);
+    A = A & 16;
+    if(A != 0) goto pos37330;
+    A = inputtemp[X];
+    if(A != 72) goto pos36700;
+    X++;
+    A = inputtemp[X];
+    if((A == 67) || (A == 83)) goto pos37330;
+    goto pos36700;
+
+    // --------------
+
+pos37367:
+    Code37066(mem58);
+    A = A & 4;
+    if(A != 0) goto pos37330;
+    A = inputtemp[X];
+    if(A != 72) goto pos36700;
+    if((A != 84) && (A != 67) && (A != 83)) goto pos36700;
+    mem58 = X;
+    goto pos37184;
+
+    // --------------
+
+pos37404:
+    Code37066(mem58);
+    A = A & 32;
+    if(A == 0) goto pos36700;
+pos37414:
+    mem58 = X;
+    goto pos37184;
+
+    // --------------
+
+pos37419:
+    X = mem58;
+    X++;
+    A = inputtemp[X];
+    if((A == 69) || (A == 73) || (A == 89)) goto pos37414;
+    goto pos36700;
+
+    // ----------------------
+
+pos37440:
+
+    Code37066(mem58);
+    A = A & 32;
+    if(A == 0) goto pos37184;
+    mem58 = X;
+    goto pos37440;
+pos37455:
+    Y = mem64;
+    mem61 = mem60;
+
+pos37461:
+    //37461: LDA (62),y
+    A = GetRuleByte(mem62, Y);
+    mem57 = A;
+    A = A & 127;
+    if(A != '=') {
+        mem56++;
+        X = mem56;
+        input[X] = A;
+    }
+
+    //37478: BIT 57
+    //37480: BPL 37485  //not negative flag
+    if((mem57 & 128) == 0) goto pos37485; //???
+    goto pos36554;
+pos37485:
+    Y++;
+    goto pos37461;
+}
+
+// Constructor
+
+STM32SAM::STM32SAM(uint32_t STM32SAM_SPEED /* = 5 */) {
+    STM32SAM_SPEED = STM32SAM_SPEED & 0x1f; // limit it from 0 to 31
+
+    _STM32SAM_SPEED = STM32SAM_SPEED;
+
+    // set default voice
+
+    speed = 72;
+    pitch = 64;
+    mouth = 128;
+    throat = 128;
+
+    phonetic = 0;
+    singmode = 0;
+
+    wait1 = 7;
+    wait2 = 6;
+
+    mem59 = 0;
+
+    oldtimetableindex = 0;
+}
+
+STM32SAM::STM32SAM() {
+    _STM32SAM_SPEED = 7;
+
+    // set default voice
+
+    speed = 72;
+    pitch = 64;
+    mouth = 128;
+    throat = 128;
+
+    phonetic = 0;
+    singmode = 0;
+
+    wait1 = 7;
+    wait2 = 6;
+
+    mem59 = 0;
+
+    oldtimetableindex = 0;
+}
+
+/*
+  STM32SAM::~STM32SAM() {
+  {
+  // TODO: end();
+  }
+*/
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM sam  (variable string,  phonetic, sing, pitch, speed, mouth, throat)
+//           STM32SAM say (sing off, phonetic off) (const string)
+//           STM32SAM say (sing off, phonetic off) (variable string)
+//           STM32SAM sing (sing on, phonetic off) (const string)
+//           STM32SAM sing (sing on, phonetic off) (variable string)
+//           STM32SAM sayPhonetic (sing off, phonetic on) (const string)
+//           STM32SAM sayPhonetic (sing off, phonetic on) (variable string)
+//           STM32SAM singPhonetic (sing on, phonetic on) (const string)
+//           STM32SAM singPhonetic (sing on, phonetic on) (variable string)
+//           STM32SAM voice (pitch, speed, mouth, throat)
+//           STM32SAM setPitch (pitch)
+//           STM32SAM setSpeed (speed)
+//           STM32SAM setMouth (mouth)
+//           STM32SAM setThroat (throat)
+//
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM sam  (const string,  phonetic, sing, pitch, speed, mouth, throat)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+char to_upper_case(char c) {
+    if(c >= 'a' && c <= 'z') {
+        return c - 'a' + 'A';
+    }
+    return c;
+}
+
+void STM32SAM::sam(
+    const char* argv,
+    unsigned char _phonetic,
+    unsigned char _singmode,
+    unsigned char _pitch,
+    unsigned char _speed,
+    unsigned char _mouth,
+    unsigned char _throat) {
+    phonetic = _phonetic;
+    singmode = _singmode;
+    pitch = _pitch;
+    speed = _speed;
+    mouth = _mouth;
+    throat = _throat;
+
+    int i;
+
+    for(i = 0; i < 256; i++) {
+        input[i] = argv[i];
+    }
+
+    for(i = 0; input[i] != 0; i++) {
+        if(i != 0) {
+            input[i] = to_upper_case((int)argv[i]);
+        }
+    }
+
+    if(!phonetic) {
+        strncat(input, "[", 256);
+        if(!TextToPhonemes((unsigned char*)input)) {
+            // PrintUsage();
+            return;
+        }
+
+    } else {
+        strncat(input, "\x9b", 256);
+    }
+
+    SetInput(input);
+
+    if(!SAMMain()) {
+        return;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM sam  (variable string,  phonetic, sing, pitch, speed, mouth, throat)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::sam(
+    char* argv,
+    unsigned char _phonetic,
+    unsigned char _singmode,
+    unsigned char _pitch,
+    unsigned char _speed,
+    unsigned char _mouth,
+    unsigned char _throat) {
+    phonetic = _phonetic;
+    singmode = _singmode;
+    pitch = _pitch;
+    speed = _speed;
+    mouth = _mouth;
+    throat = _throat;
+
+    int i;
+
+    for(i = 0; i < 256; i++) {
+        input[i] = argv[i];
+    }
+
+    for(i = 0; input[i] != 0; i++) {
+        if(i != 0) {
+            input[i] = to_upper_case((int)argv[i]);
+        }
+    }
+
+    if(i < 256) {
+        input[i] = phonetic ? '\x9b' : '[';
+    }
+
+    if(!phonetic) {
+        if(!TextToPhonemes((unsigned char*)input)) {
+            return;
+        }
+    }
+
+    SetInput(input);
+
+    if(!SAMMain()) {
+        return;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM say(sing off, phonetic off) (const string)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::say(const char* argv) {
+    int i;
+
+    phonetic = 0;
+    singmode = 0;
+
+    char const_input[256];
+
+    for(i = 0; i < 256; i++) {
+        const_input[i] = argv[i];
+    }
+
+    sam(const_input, phonetic, singmode, pitch, speed, mouth, throat);
+}
+
+void STM32SAM::say(char* argv) {
+    int i;
+
+    phonetic = 0;
+    singmode = 0;
+
+    char const_input[256];
+
+    for(i = 0; i < 256; i++) {
+        const_input[i] = argv[i];
+    }
+
+    sam(const_input, phonetic, singmode, pitch, speed, mouth, throat);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM sing (sing on, phonetic off)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::sing(const char* argv) {
+    int i;
+
+    phonetic = 0;
+    singmode = 1;
+
+    char const_input[256];
+
+    for(i = 0; i < 256; i++) {
+        const_input[i] = argv[i];
+    }
+
+    sam(const_input, phonetic, singmode, pitch, speed, mouth, throat);
+}
+
+void STM32SAM::sing(char* argv) {
+    int i;
+
+    phonetic = 0;
+    singmode = 1;
+
+    char const_input[256];
+
+    for(i = 0; i < 256; i++) {
+        const_input[i] = argv[i];
+    }
+
+    sam(const_input, phonetic, singmode, pitch, speed, mouth, throat);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM sayPhonetic (sing off, phonetic on)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::sayPhonetic(const char* argv) {
+    int i;
+
+    phonetic = 1;
+    singmode = 0;
+
+    char const_input[256];
+
+    for(i = 0; i < 256; i++) {
+        const_input[i] = argv[i];
+    }
+
+    sam(const_input, phonetic, singmode, pitch, speed, mouth, throat);
+}
+
+void STM32SAM::sayPhonetic(char* argv) {
+    int i;
+
+    phonetic = 1;
+    singmode = 0;
+
+    char const_input[256];
+
+    for(i = 0; i < 256; i++) {
+        const_input[i] = argv[i];
+    }
+
+    sam(const_input, phonetic, singmode, pitch, speed, mouth, throat);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM singPhonetic (sing on, phonetic on)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::singPhonetic(const char* argv) {
+    int i;
+
+    phonetic = 1;
+    singmode = 1;
+
+    char const_input[256];
+
+    for(i = 0; i < 256; i++) {
+        const_input[i] = argv[i];
+    }
+
+    sam(const_input, phonetic, singmode, pitch, speed, mouth, throat);
+}
+
+void STM32SAM::singPhonetic(char* argv) {
+    int i;
+
+    phonetic = 1;
+    singmode = 0;
+
+    char const_input[256];
+
+    for(i = 0; i < 256; i++) {
+        const_input[i] = argv[i];
+    }
+
+    sam(const_input, phonetic, singmode, pitch, speed, mouth, throat);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM voice (pitch, speed, mouth, throat)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::setVoice(
+    unsigned char _pitch /* = 64 */,
+    unsigned char _speed /* = 72 */,
+    unsigned char _mouth /* = 128 */,
+    unsigned char _throat /* = 128 */) {
+    pitch = _pitch;
+    speed = _speed;
+    mouth = _mouth;
+    throat = _throat;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM setPitch (pitch)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::setPitch(unsigned char _pitch /* = 64 */) {
+    pitch = _pitch;
+}
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM setSpeed (speed)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::setSpeed(unsigned char _speed /* = 72 */) {
+    speed = _speed;
+}
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM setMouth (mouth)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::setMouth(unsigned char _mouth /* = 128 */) {
+    mouth = _mouth;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           STM32SAM setThroat (throat)
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+
+void STM32SAM::setThroat(unsigned char _throat /* = 128 */) {
+    throat = _throat;
+}
+////////////////////////////////////////////////////////////////////////////////////////////
+//
+//           Hardware
+//
+////////////////////////////////////////////////////////////////////////////////////////////
+// Hardware specifics, for easier porting to other microcontrollers
+
+//
+// Set PA8 pin as PWM, at 256 timer ticks overflow (8bit resolution)
+
+#include <math.h>
+#include <stm32wbxx_ll_tim.h>
+
+#define FURI_HAL_SPEAKER_TIMER TIM16
+#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1
+
+void STM32SAM::begin(void) {
+#ifdef USE_ROGER_CORE
+
+    pinMode(PA8, PWM); //   audio output pin
+
+    Timer1.setPeriod(
+        4); // Can't set at 256 ticks, only in uS. First nearest uS is 4 (Roger core is only for bluepill, that means 72*4=288 ticks, or 128*4=512 ticks when overclocked. It's ok, just overall volume will be lower, because maximum volume will be 256/288 or 256/512)
+
+#endif
+
+#ifdef USE_STM32duino_CORE
+    pinMode(PA8, OUTPUT);
+
+    PWM->pause();
+    PWM->setMode(1, TIMER_OUTPUT_COMPARE_PWM1, PA8); // TIM1 CH1 (PA8)
+    PWM->setPrescaleFactor(1);
+    PWM->setOverflow(256, TICK_FORMAT); // 256 ticks overflow, no matter the CPU (timer) speed
+    PWM->resume();
+
+#endif
+
+    LL_TIM_InitTypeDef TIM_InitStruct;
+    memset(&TIM_InitStruct, 0, sizeof(LL_TIM_InitTypeDef));
+    TIM_InitStruct.Prescaler = 4;
+    TIM_InitStruct.Autoreload = 255;
+    LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct);
+
+    LL_TIM_OC_InitTypeDef TIM_OC_InitStruct;
+    memset(&TIM_OC_InitStruct, 0, sizeof(LL_TIM_OC_InitTypeDef));
+    TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
+    TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
+    TIM_OC_InitStruct.CompareValue = 127;
+    LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct);
+
+    LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER);
+    LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER);
+} // begin
+
+inline void STM32SAM::SetAUDIO(unsigned char main_volume) {
+#ifdef USE_ROGER_CORE
+    Timer1.setCompare(TIMER_CH1, main_volume);
+#endif
+
+#ifdef USE_STM32duino_CORE
+    PWM->setCaptureCompare(1, main_volume, TICK_COMPARE_FORMAT);
+#endif
+
+    // if(main_volume > 64) {
+    //     LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, 127);
+    // } else {
+    //     LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, main_volume);
+    // }
+
+    float data = main_volume;
+    data /= 255.0f;
+    data -= 0.5f;
+    data *= 4.0f;
+    data = tanhf(data);
+
+    data += 0.5f;
+    data *= 255.0f;
+
+    if(data < 0) {
+        data = 0;
+    } else if(data > 255) {
+        data = 255;
+    }
+
+    LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, data);
+}

+ 96 - 0
chess/sam/stm32_sam.h

@@ -0,0 +1,96 @@
+#include <furi.h>
+
+#ifndef __STM32SAM__
+#define __STM32SAM__
+
+// SAM Text-To-Speech (TTS), ported from https://github.com/s-macke/SAM
+
+class STM32SAM {
+public:
+    STM32SAM(uint32_t STM32SAM_SPEED);
+    STM32SAM();
+
+    void begin(void);
+
+    void
+        sam(const char* argv,
+            unsigned char phonetic,
+            unsigned char singmode,
+            unsigned char pitch,
+            unsigned char speed,
+            unsigned char mouth,
+            unsigned char throat);
+    void
+        sam(char* argv,
+            unsigned char phonetic,
+            unsigned char singmode,
+            unsigned char pitch,
+            unsigned char speed,
+            unsigned char mouth,
+            unsigned char throat);
+
+    void say(const char* argv);
+    void say(char* argv);
+    void sing(const char* argv);
+    void sing(char* argv);
+    void sayPhonetic(const char* argv);
+    void sayPhonetic(char* argv);
+    void singPhonetic(const char* argv);
+    void singPhonetic(char* argv);
+    void setVoice(
+        unsigned char _pitch = 64,
+        unsigned char _speed = 72,
+        unsigned char _mouth = 128,
+        unsigned char _throat = 128);
+    void setPitch(unsigned char _pitch = 64);
+    void setSpeed(unsigned char _speed = 72);
+    void setMouth(unsigned char _mouth = 128);
+    void setThroat(unsigned char _throat = 128);
+
+private:
+    void SetAUDIO(unsigned char main_volume);
+
+    void Output8BitAry(int index, unsigned char ary[5]);
+    void Output8Bit(int index, unsigned char A);
+    unsigned char Read(unsigned char p, unsigned char Y);
+    void Write(unsigned char p, unsigned char Y, unsigned char value);
+    void RenderSample(unsigned char* mem66);
+    void Render();
+    void AddInflection(unsigned char mem48, unsigned char phase1);
+    void SetMouthThroat();
+    unsigned char trans(unsigned char mem39212, unsigned char mem39213);
+    void SetInput(char* _input);
+    void Init();
+    int SAMMain();
+    void PrepareOutput();
+    void Insert(
+        unsigned char position /*var57*/,
+        unsigned char mem60,
+        unsigned char mem59,
+        unsigned char mem58);
+    void InsertBreath();
+    void CopyStress();
+    int Parser1();
+    void SetPhonemeLength();
+    void Code41240();
+    void Parser2();
+    void AdjustLengths();
+    void Code47503(unsigned char mem52);
+    void Code37055(unsigned char mem59);
+    void Code37066(unsigned char mem58);
+    unsigned char GetRuleByte(unsigned short mem62, unsigned char Y);
+    int TextToPhonemes(unsigned char* input); // Code36484
+
+    uint32_t _STM32SAM_SPEED;
+
+    unsigned char speed;
+    unsigned char pitch;
+    unsigned char mouth;
+    unsigned char throat;
+
+    unsigned char phonetic;
+    unsigned char singmode;
+
+}; // STM32SAM class
+
+#endif

+ 30 - 0
chess/scenes/flipchess_scene.c

@@ -0,0 +1,30 @@
+#include "flipchess_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const flipchess_on_enter_handlers[])(void*) = {
+#include "flipchess_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const flipchess_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "flipchess_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const flipchess_on_exit_handlers[])(void* context) = {
+#include "flipchess_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers flipchess_scene_handlers = {
+    .on_enter_handlers = flipchess_on_enter_handlers,
+    .on_event_handlers = flipchess_on_event_handlers,
+    .on_exit_handlers = flipchess_on_exit_handlers,
+    .scene_num = FlipChessSceneNum,
+};

+ 29 - 0
chess/scenes/flipchess_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) FlipChessScene##id,
+typedef enum {
+#include "flipchess_scene_config.h"
+    FlipChessSceneNum,
+} FlipChessScene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers flipchess_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "flipchess_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_event handlers declaration
+#define ADD_SCENE(prefix, name, id) \
+    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
+#include "flipchess_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
+#include "flipchess_scene_config.h"
+#undef ADD_SCENE

+ 4 - 0
chess/scenes/flipchess_scene_config.h

@@ -0,0 +1,4 @@
+ADD_SCENE(flipchess, startscreen, Startscreen)
+ADD_SCENE(flipchess, menu, Menu)
+ADD_SCENE(flipchess, scene_1, Scene_1)
+ADD_SCENE(flipchess, settings, Settings)

+ 91 - 0
chess/scenes/flipchess_scene_menu.c

@@ -0,0 +1,91 @@
+#include "../flipchess.h"
+
+enum SubmenuIndex {
+    SubmenuIndexScene1New = 10,
+    SubmenuIndexScene1Resume,
+    SubmenuIndexScene1Import,
+    SubmenuIndexSettings,
+};
+
+void flipchess_scene_menu_submenu_callback(void* context, uint32_t index) {
+    FlipChess* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void flipchess_scene_menu_on_enter(void* context) {
+    FlipChess* app = context;
+
+    submenu_add_item(
+        app->submenu,
+        "New Game",
+        SubmenuIndexScene1New,
+        flipchess_scene_menu_submenu_callback,
+        app);
+
+    if(app->import_game == 1) {
+        submenu_add_item(
+            app->submenu,
+            "Resume Game",
+            SubmenuIndexScene1Resume,
+            flipchess_scene_menu_submenu_callback,
+            app);
+    }
+
+    // submenu_add_item(
+    //     app->submenu,
+    //     "Import Game",
+    //     SubmenuIndexScene1Import,
+    //     flipchess_scene_menu_submenu_callback,
+    //     app);
+
+    submenu_add_item(
+        app->submenu, "Settings", SubmenuIndexSettings, flipchess_scene_menu_submenu_callback, app);
+
+    submenu_set_selected_item(
+        app->submenu, scene_manager_get_scene_state(app->scene_manager, FlipChessSceneMenu));
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdMenu);
+}
+
+bool flipchess_scene_menu_on_event(void* context, SceneManagerEvent event) {
+    FlipChess* app = context;
+    //UNUSED(app);
+    if(event.type == SceneManagerEventTypeBack) {
+        //exit app
+        scene_manager_stop(app->scene_manager);
+        view_dispatcher_stop(app->view_dispatcher);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexScene1New) {
+            app->import_game = 0;
+            scene_manager_set_scene_state(
+                app->scene_manager, FlipChessSceneMenu, SubmenuIndexScene1New);
+            scene_manager_next_scene(app->scene_manager, FlipChessSceneScene_1);
+            return true;
+        }
+        if(event.event == SubmenuIndexScene1Resume) {
+            app->import_game = 1;
+            scene_manager_set_scene_state(
+                app->scene_manager, FlipChessSceneMenu, SubmenuIndexScene1Resume);
+            scene_manager_next_scene(app->scene_manager, FlipChessSceneScene_1);
+            return true;
+        } else if(event.event == SubmenuIndexScene1Import) {
+            app->import_game = 1;
+            app->input_state = FlipChessTextInputGame;
+            text_input_set_header_text(app->text_input, "Enter board FEN");
+            view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdTextInput);
+            return true;
+        } else if(event.event == SubmenuIndexSettings) {
+            scene_manager_set_scene_state(
+                app->scene_manager, FlipChessSceneMenu, SubmenuIndexSettings);
+            scene_manager_next_scene(app->scene_manager, FlipChessSceneSettings);
+            return true;
+        }
+    }
+    return false;
+}
+
+void flipchess_scene_menu_on_exit(void* context) {
+    FlipChess* app = context;
+    submenu_reset(app->submenu);
+}

+ 55 - 0
chess/scenes/flipchess_scene_scene_1.c

@@ -0,0 +1,55 @@
+#include "../flipchess.h"
+#include "../helpers/flipchess_file.h"
+#include "../helpers/flipchess_custom_event.h"
+#include "../views/flipchess_scene_1.h"
+
+void flipchess_scene_1_callback(FlipChessCustomEvent event, void* context) {
+    furi_assert(context);
+    FlipChess* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void flipchess_scene_scene_1_on_enter(void* context) {
+    furi_assert(context);
+    FlipChess* app = context;
+
+    flipchess_scene_1_set_callback(app->flipchess_scene_1, flipchess_scene_1_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdScene1);
+}
+
+bool flipchess_scene_scene_1_on_event(void* context, SceneManagerEvent event) {
+    FlipChess* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+        case FlipChessCustomEventScene1Left:
+        case FlipChessCustomEventScene1Right:
+            break;
+        case FlipChessCustomEventScene1Up:
+        case FlipChessCustomEventScene1Down:
+            break;
+        case FlipChessCustomEventScene1Back:
+            notification_message(app->notification, &sequence_reset_red);
+            notification_message(app->notification, &sequence_reset_green);
+            notification_message(app->notification, &sequence_reset_blue);
+            if(!scene_manager_search_and_switch_to_previous_scene(
+                   app->scene_manager, FlipChessSceneMenu)) {
+                scene_manager_stop(app->scene_manager);
+                view_dispatcher_stop(app->view_dispatcher);
+            }
+            consumed = true;
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void flipchess_scene_scene_1_on_exit(void* context) {
+    FlipChess* app = context;
+
+    if(app->import_game == 1 && strlen(app->import_game_text) > 0) {
+        flipchess_save_file(app->import_game_text, FlipChessFileBoard, NULL, false, true);
+    }
+}

+ 102 - 0
chess/scenes/flipchess_scene_settings.c

@@ -0,0 +1,102 @@
+#include "../flipchess.h"
+#include "../helpers/flipchess_voice.h"
+#include <lib/toolbox/value_index.h>
+
+#define TEXT_LABEL_ON "ON"
+#define TEXT_LABEL_OFF "OFF"
+
+const char* const haptic_text[2] = {
+    TEXT_LABEL_OFF,
+    TEXT_LABEL_ON,
+};
+const uint32_t haptic_value[2] = {
+    FlipChessHapticOff,
+    FlipChessHapticOn,
+};
+
+const char* const player_mode_text[4] = {
+    "Human",
+    "CPU 1",
+    "CPU 2",
+    "CPU 3",
+};
+const uint32_t player_mode_value[4] = {
+    FlipChessPlayerHuman,
+    FlipChessPlayerAI1,
+    FlipChessPlayerAI2,
+    FlipChessPlayerAI3,
+};
+
+static void flipchess_scene_settings_set_haptic(VariableItem* item) {
+    FlipChess* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, haptic_text[index]);
+    app->haptic = haptic_value[index];
+}
+
+static void flipchess_scene_settings_set_white_mode(VariableItem* item) {
+    FlipChess* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, player_mode_text[index]);
+    app->white_mode = player_mode_value[index];
+}
+
+static void flipchess_scene_settings_set_black_mode(VariableItem* item) {
+    FlipChess* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, player_mode_text[index]);
+    app->black_mode = player_mode_value[index];
+}
+
+void flipchess_scene_settings_submenu_callback(void* context, uint32_t index) {
+    FlipChess* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void flipchess_scene_settings_on_enter(void* context) {
+    FlipChess* app = context;
+    VariableItem* item;
+    uint8_t value_index;
+
+    if(app->sound == 1) {
+        flipchess_voice_which_side();
+    }
+
+    // White mode
+    item = variable_item_list_add(
+        app->variable_item_list, "White:", 4, flipchess_scene_settings_set_white_mode, app);
+    value_index = value_index_uint32(app->white_mode, player_mode_value, 4);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, player_mode_text[value_index]);
+
+    // Black mode
+    item = variable_item_list_add(
+        app->variable_item_list, "Black:", 4, flipchess_scene_settings_set_black_mode, app);
+    value_index = value_index_uint32(app->black_mode, player_mode_value, 4);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, player_mode_text[value_index]);
+
+    // Vibro on/off
+    item = variable_item_list_add(
+        app->variable_item_list, "Vibro/Haptic:", 2, flipchess_scene_settings_set_haptic, app);
+    value_index = value_index_uint32(app->haptic, haptic_value, 2);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, haptic_text[value_index]);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdSettings);
+}
+
+bool flipchess_scene_settings_on_event(void* context, SceneManagerEvent event) {
+    FlipChess* app = context;
+    UNUSED(app);
+    bool consumed = false;
+    if(event.type == SceneManagerEventTypeCustom) {
+    }
+    return consumed;
+}
+
+void flipchess_scene_settings_on_exit(void* context) {
+    FlipChess* app = context;
+    variable_item_list_set_selected_item(app->variable_item_list, 0);
+    variable_item_list_reset(app->variable_item_list);
+}

+ 67 - 0
chess/scenes/flipchess_scene_startscreen.c

@@ -0,0 +1,67 @@
+#include "../flipchess.h"
+#include "../helpers/flipchess_voice.h"
+#include "../helpers/flipchess_file.h"
+#include "../helpers/flipchess_custom_event.h"
+#include "../views/flipchess_startscreen.h"
+
+void flipchess_scene_startscreen_callback(FlipChessCustomEvent event, void* context) {
+    furi_assert(context);
+    FlipChess* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void flipchess_scene_startscreen_on_enter(void* context) {
+    furi_assert(context);
+    FlipChess* app = context;
+
+    if(flipchess_has_file(FlipChessFileBoard, NULL, false)) {
+        if(flipchess_load_file(app->import_game_text, FlipChessFileBoard, NULL)) {
+            app->import_game = 1;
+        }
+    }
+
+    flipchess_startscreen_set_callback(
+        app->flipchess_startscreen, flipchess_scene_startscreen_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdStartscreen);
+}
+
+bool flipchess_scene_startscreen_on_event(void* context, SceneManagerEvent event) {
+    FlipChess* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+        case FlipChessCustomEventStartscreenLeft:
+        case FlipChessCustomEventStartscreenRight:
+            break;
+        case FlipChessCustomEventStartscreenUp:
+        case FlipChessCustomEventStartscreenDown:
+            break;
+        case FlipChessCustomEventStartscreenOk:
+            scene_manager_next_scene(app->scene_manager, FlipChessSceneMenu);
+            consumed = true;
+            break;
+        case FlipChessCustomEventStartscreenBack:
+            notification_message(app->notification, &sequence_reset_red);
+            notification_message(app->notification, &sequence_reset_green);
+            notification_message(app->notification, &sequence_reset_blue);
+            if(!scene_manager_search_and_switch_to_previous_scene(
+                   app->scene_manager, FlipChessSceneStartscreen)) {
+                scene_manager_stop(app->scene_manager);
+                view_dispatcher_stop(app->view_dispatcher);
+            }
+            consumed = true;
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void flipchess_scene_startscreen_on_exit(void* context) {
+    FlipChess* app = context;
+
+    if(app->sound == 1) {
+        flipchess_voice_shall_we_play();
+    }
+}

+ 727 - 0
chess/views/flipchess_scene_1.c

@@ -0,0 +1,727 @@
+#include "../flipchess.h"
+#include <furi.h>
+// #include <furi_hal.h>
+// #include <furi_hal_random.h>
+#include <input/input.h>
+#include <gui/elements.h>
+//#include <dolphin/dolphin.h>
+#include <string.h>
+//#include "flipchess_icons.h"
+#include "../helpers/flipchess_voice.h"
+#include "../helpers/flipchess_haptic.h"
+
+#define SCL_960_CASTLING 0 // setting to 1 compiles a 960 version of smolchess
+#define XBOARD_DEBUG 0 // will create files with xboard communication
+#define SCL_EVALUATION_FUNCTION SCL_boardEvaluateStatic
+#define SCL_DEBUG_AI 0
+
+#include "../chess/smallchesslib.h"
+
+#define ENABLE_960 0 // setting to 1 enables 960 chess
+#define MAX_TEXT_LEN 15 // 15 = max length of text
+#define MAX_TEXT_BUF (MAX_TEXT_LEN + 1) // max length of text + null terminator
+#define THREAD_WAIT_TIME 20 // time to wait for draw thread to finish
+
+struct FlipChessScene1 {
+    View* view;
+    FlipChessScene1Callback callback;
+    void* context;
+};
+typedef struct {
+    uint8_t paramPlayerW;
+    uint8_t paramPlayerB;
+
+    uint8_t paramAnalyze; // depth of analysis
+    uint8_t paramMoves;
+    uint8_t paramInfo;
+    uint8_t paramFlipBoard;
+    uint8_t paramExit;
+    uint16_t paramStep;
+    char* paramFEN;
+    char* paramPGN;
+
+    int clockSeconds;
+    SCL_Game game;
+    SCL_Board startState;
+
+#if ENABLE_960
+    int16_t random960PosNumber;
+#endif
+
+    //uint8_t picture[SCL_BOARD_PICTURE_WIDTH * SCL_BOARD_PICTURE_WIDTH];
+    uint8_t squareSelected;
+    uint8_t squareSelectedLast;
+
+    char* msg;
+    char* msg2;
+    char* msg3;
+    char moveString[MAX_TEXT_BUF];
+    char moveString2[MAX_TEXT_BUF];
+    char moveString3[MAX_TEXT_BUF];
+    uint8_t thinking;
+
+    SCL_SquareSet moveHighlight;
+    uint8_t squareFrom;
+    uint8_t squareTo;
+    uint8_t turnState;
+
+} FlipChessScene1Model;
+
+static uint8_t picture[SCL_BOARD_PICTURE_WIDTH * SCL_BOARD_PICTURE_WIDTH];
+
+void flipchess_putImagePixel(uint8_t pixel, uint16_t index) {
+    picture[index] = pixel;
+}
+
+uint8_t flipchess_stringsEqual(const char* s1, const char* s2, int max) {
+    for(int i = 0; i < max; ++i) {
+        if(*s1 != *s2) return 0;
+
+        if(*s1 == 0) return 1;
+
+        s1++;
+        s2++;
+    }
+
+    return 1;
+}
+
+int16_t flipchess_makeAIMove(
+    SCL_Board board,
+    uint8_t* s0,
+    uint8_t* s1,
+    char* prom,
+    FlipChessScene1Model* model) {
+    uint8_t level = SCL_boardWhitesTurn(board) ? model->paramPlayerW : model->paramPlayerB;
+    uint8_t depth = (level > 0) ? level : 1;
+    uint8_t extraDepth = 3;
+    uint8_t endgameDepth = 1;
+    uint8_t randomness =
+        model->game.ply < 2 ? 1 : 0; /* in first moves increase randomness for different 
+                             openings */
+    uint8_t rs0, rs1;
+
+    SCL_gameGetRepetiotionMove(&(model->game), &rs0, &rs1);
+
+    if(model->clockSeconds >= 0) // when using clock, choose AI params accordingly
+    {
+        if(model->clockSeconds <= 5) {
+            depth = 1;
+            extraDepth = 2;
+            endgameDepth = 0;
+        } else if(model->clockSeconds < 15) {
+            depth = 2;
+            extraDepth = 2;
+        } else if(model->clockSeconds < 100) {
+            depth = 2;
+        } else if(model->clockSeconds < 5 * 60) {
+            depth = 3;
+        } else {
+            depth = 3;
+            extraDepth = 4;
+        }
+    }
+
+    return SCL_getAIMove(
+        board,
+        depth,
+        extraDepth,
+        endgameDepth,
+        SCL_boardEvaluateStatic,
+        SCL_randomBetter,
+        randomness,
+        rs0,
+        rs1,
+        s0,
+        s1,
+        prom);
+}
+
+bool flipchess_isPlayerTurn(FlipChessScene1Model* model) {
+    return (SCL_boardWhitesTurn(model->game.board) && model->paramPlayerW == 0) ||
+           (!SCL_boardWhitesTurn(model->game.board) && model->paramPlayerB == 0);
+}
+
+void flipchess_shiftMessages(FlipChessScene1Model* model) {
+    // shift messages
+    model->msg3 = model->msg2;
+    model->msg2 = model->msg;
+    strncpy(model->moveString3, model->moveString2, MAX_TEXT_LEN);
+    strncpy(model->moveString2, model->moveString, MAX_TEXT_LEN);
+}
+
+void flipchess_drawBoard(FlipChessScene1Model* model) {
+    // draw chess board
+    SCL_drawBoard(
+        model->game.board,
+        flipchess_putImagePixel,
+        model->squareSelected,
+        model->moveHighlight,
+        model->paramFlipBoard);
+}
+
+uint8_t flipchess_saveState(FlipChess* app, FlipChessScene1Model* model) {
+    for(uint8_t i = 0; i < SCL_FEN_MAX_LENGTH; i++) {
+        app->import_game_text[i] = '\0';
+    }
+    const uint8_t res = SCL_boardToFEN(model->game.board, app->import_game_text);
+    if(res > 0) {
+        app->import_game = 1;
+    }
+    return res;
+}
+
+uint8_t flipchess_turn(FlipChessScene1Model* model) {
+    // 0: none, 1: player, 2: AI, 3: undo
+    uint8_t moveType = FlipChessStatusNone;
+
+    // if(model->paramInfo) {
+
+    //     if(model->random960PosNumber >= 0)
+    //         printf("960 random position number: %d\n", model->random960PosNumber);
+
+    //     printf("ply number: %d\n", model->game.ply);
+
+    //     int16_t eval = SCL_boardEvaluateStatic(model->game.board);
+    //     printf(
+    //         "board static evaluation: %lf (%d)\n",
+    //         ((double)eval) / ((double)SCL_VALUE_PAWN),
+    //         eval);
+    //     printf("board hash: %u\n", SCL_boardHash32(model->game.board));
+    //     printf("phase: ");
+
+    //     switch(SCL_boardEstimatePhase(model->game.board)) {
+    //     case SCL_PHASE_OPENING:
+    //         puts("opening");
+    //         break;
+    //     case SCL_PHASE_ENDGAME:
+    //         puts("endgame");
+    //         break;
+    //     default:
+    //         puts("midgame");
+    //         break;
+    //     }
+
+    //     printf(
+    //         "en passant: %d\n",
+    //         ((model->game.board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f) + 1) % 16);
+    //     printf(
+    //         "50 move rule count: %d\n", model->game.board[SCL_BOARD_MOVE_COUNT_BYTE]);
+
+    //     if(model->paramFEN == NULL && model->paramPGN == NULL) {
+    //         printf("PGN: ");
+    //         SCL_printPGN(model->game.record, putCharacter, startState);
+    //         putchar('\n');
+    //     }
+    // }
+
+    if(model->game.state != SCL_GAME_STATE_PLAYING) {
+        model->paramExit = FlipChessStatusNone;
+
+    } else {
+        char movePromote = 'q';
+
+        if(flipchess_isPlayerTurn(model)) {
+            // if(stringsEqual(string, "undo", 5))
+            //     moveType = FlipChessStatusMoveUndo;
+            // else if(stringsEqual(string, "quit", 5))
+            //     break;
+
+            if(model->turnState == 0 && model->squareSelected != 255) {
+                model->squareFrom = model->squareSelected;
+                model->turnState = 1;
+            } else if(model->turnState == 1 && model->squareSelected != 255) {
+                model->squareTo = model->squareSelected;
+                model->turnState = 2;
+                model->squareSelectedLast = model->squareSelected;
+                //model->squareSelected = 255;
+            }
+
+            if(model->turnState == 1 && model->squareFrom != 255) {
+                if((model->game.board[model->squareFrom] != '.') &&
+                   (SCL_pieceIsWhite(model->game.board[model->squareFrom]) ==
+                    SCL_boardWhitesTurn(model->game.board))) {
+                    SCL_boardGetMoves(model->game.board, model->squareFrom, model->moveHighlight);
+                }
+            } else if(model->turnState == 2) {
+                if(SCL_squareSetContains(model->moveHighlight, model->squareTo)) {
+                    moveType = FlipChessStatusMovePlayer;
+                }
+                model->turnState = 0;
+                SCL_squareSetClear(model->moveHighlight);
+            }
+
+        } else {
+            model->squareSelected = 255;
+            flipchess_makeAIMove(
+                model->game.board, &(model->squareFrom), &(model->squareTo), &movePromote, model);
+            moveType = FlipChessStatusMoveAI;
+            model->turnState = 0;
+        }
+
+        if(moveType == FlipChessStatusMovePlayer || moveType == FlipChessStatusMoveAI) {
+            flipchess_shiftMessages(model);
+
+            SCL_moveToString(
+                model->game.board,
+                model->squareFrom,
+                model->squareTo,
+                movePromote,
+                model->moveString);
+
+            SCL_gameMakeMove(&(model->game), model->squareFrom, model->squareTo, movePromote);
+
+            SCL_squareSetClear(model->moveHighlight);
+            SCL_squareSetAdd(model->moveHighlight, model->squareFrom);
+            SCL_squareSetAdd(model->moveHighlight, model->squareTo);
+        } else if(moveType == FlipChessStatusMoveUndo) {
+            flipchess_shiftMessages(model);
+
+            if(model->paramPlayerW != 0 || model->paramPlayerB != 0)
+                SCL_gameUndoMove(&(model->game));
+
+            SCL_gameUndoMove(&(model->game));
+            SCL_squareSetClear(model->moveHighlight);
+        }
+
+        switch(model->game.state) {
+        case SCL_GAME_STATE_WHITE_WIN:
+            model->msg = "white wins";
+            model->paramExit = FlipChessStatusReturn;
+            break;
+
+        case SCL_GAME_STATE_BLACK_WIN:
+            model->msg = "black wins";
+            model->paramExit = FlipChessStatusReturn;
+            break;
+
+        case SCL_GAME_STATE_DRAW_STALEMATE:
+            model->msg = "stalemate";
+            model->paramExit = FlipChessStatusReturn;
+            break;
+
+        case SCL_GAME_STATE_DRAW_REPETITION:
+            model->msg = "draw-repetition";
+            model->paramExit = FlipChessStatusReturn;
+            break;
+
+        case SCL_GAME_STATE_DRAW_DEAD:
+            model->msg = "draw-dead pos.";
+            model->paramExit = FlipChessStatusReturn;
+            break;
+
+        case SCL_GAME_STATE_DRAW:
+            model->msg = "draw";
+            model->paramExit = FlipChessStatusReturn;
+            break;
+
+        case SCL_GAME_STATE_DRAW_50:
+            model->msg = "draw-50 moves";
+            model->paramExit = FlipChessStatusReturn;
+            break;
+
+        default:
+            if(model->game.ply > 0) {
+                const uint8_t whitesTurn = SCL_boardWhitesTurn(model->game.board);
+
+                if(SCL_boardCheck(model->game.board, whitesTurn)) {
+                    model->msg = (whitesTurn ? "black: check!" : "white: check!");
+                } else {
+                    model->msg = (whitesTurn ? "black played" : "white played");
+                }
+
+                uint8_t s0, s1;
+                char p;
+
+                SCL_recordGetMove(model->game.record, model->game.ply - 1, &s0, &s1, &p);
+                SCL_moveToString(model->game.board, s0, s1, p, model->moveString);
+            }
+            break;
+            model->paramExit = moveType;
+        }
+    }
+
+    model->thinking = 0;
+    return model->paramExit;
+}
+
+void flipchess_scene_1_set_callback(
+    FlipChessScene1* instance,
+    FlipChessScene1Callback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(callback);
+    instance->callback = callback;
+    instance->context = context;
+}
+
+void flipchess_scene_1_draw(Canvas* canvas, FlipChessScene1Model* model) {
+    //UNUSED(model);
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    //canvas_draw_icon(canvas, 0, 0, &I_FLIPR_128x64);
+
+    // Frame
+    canvas_draw_frame(canvas, 0, 0, 66, 64);
+
+    // Message
+    canvas_set_font(canvas, FontSecondary);
+    if(model->thinking) {
+        canvas_draw_str(canvas, 68, 10, "thinking...");
+    } else {
+        canvas_draw_str(canvas, 68, 10, model->msg);
+    }
+    canvas_draw_str(canvas, 68, 19, model->moveString);
+    canvas_draw_str(canvas, 68, 31, model->msg2);
+    canvas_draw_str(canvas, 68, 40, model->moveString2);
+    canvas_draw_str(canvas, 68, 52, model->msg3);
+    canvas_draw_str(canvas, 68, 61, model->moveString3);
+
+    // Board
+    for(uint16_t y = 0; y < SCL_BOARD_PICTURE_WIDTH; y++) {
+        for(uint16_t x = 0; x < SCL_BOARD_PICTURE_WIDTH; x++) {
+            if(!picture[x + (y * SCL_BOARD_PICTURE_WIDTH)]) {
+                canvas_draw_dot(canvas, x + 1, y);
+            }
+        }
+    }
+}
+
+static int flipchess_scene_1_model_init(
+    FlipChessScene1Model* const model,
+    const int white_mode,
+    const int black_mode,
+    char* import_game_text) {
+    model->paramPlayerW = white_mode;
+    model->paramPlayerB = black_mode;
+
+    model->paramAnalyze = 255; // depth of analysis
+    model->paramMoves = 0;
+    model->paramInfo = 1;
+    model->paramFlipBoard = 0;
+    model->paramExit = FlipChessStatusNone;
+    model->paramStep = 0;
+    model->paramFEN = import_game_text;
+    model->paramPGN = NULL;
+    model->clockSeconds = -1;
+
+    SCL_Board emptyStartState = SCL_BOARD_START_STATE;
+    memcpy(model->startState, &emptyStartState, sizeof(SCL_Board));
+
+#if ENABLE_960
+    model->random960PosNumber = -1;
+#endif
+
+    model->squareSelected = 255;
+    model->squareSelectedLast = 28; // start selector near middle
+
+    model->msg = "init";
+    model->moveString[0] = '\0';
+    model->msg2 = "";
+    model->moveString2[0] = '\0';
+    model->msg3 = "";
+    model->moveString3[0] = '\0';
+    model->thinking = 0;
+
+    SCL_SquareSet emptySquareSet = SCL_SQUARE_SET_EMPTY;
+    memcpy(model->moveHighlight, &emptySquareSet, sizeof(SCL_SquareSet));
+    model->squareFrom = 255;
+    model->squareTo = 255;
+    model->turnState = 0;
+
+    SCL_randomBetterSeed(furi_hal_random_get());
+
+#if ENABLE_960
+#if SCL_960_CASTLING
+    if(model->random960PosNumber < 0) model->random960PosNumber = SCL_randomBetter();
+#endif
+    if(model->random960PosNumber >= 0) model->random960PosNumber %= 960;
+#endif
+
+    if(model->paramFEN != NULL)
+        SCL_boardFromFEN(model->startState, model->paramFEN);
+    else if(model->paramPGN != NULL) {
+        SCL_Record record;
+        SCL_recordFromPGN(record, model->paramPGN);
+        SCL_boardInit(model->startState);
+        SCL_recordApply(record, model->startState, model->paramStep);
+    }
+
+#if ENABLE_960
+#if SCL_960_CASTLING
+    else
+        SCL_boardInit960(model->startState, model->random960PosNumber);
+#endif
+#endif
+
+    SCL_gameInit(&(model->game), model->startState);
+
+    if(model->paramAnalyze != 255) {
+        char p;
+        uint8_t move[] = {0, 0};
+
+        model->paramPlayerW = model->paramAnalyze;
+        model->paramPlayerB = model->paramAnalyze;
+
+        int16_t evaluation =
+            flipchess_makeAIMove(model->game.board, &(move[0]), &(move[1]), &p, model);
+
+        if(model->paramAnalyze == 0) evaluation = SCL_boardEvaluateStatic(model->game.board);
+
+        char moveStr[5];
+        moveStr[4] = 0;
+
+        SCL_squareToString(move[0], moveStr);
+        SCL_squareToString(move[1], moveStr + 2);
+
+        //printf("%lf (%d)\n", ((double)evaluation) / ((double)SCL_VALUE_PAWN), evaluation);
+        //puts(moveStr);
+
+        return evaluation;
+    }
+
+    if(model->paramMoves) {
+        char string[256];
+
+        for(int i = 0; i < 64; ++i)
+            if(model->game.board[i] != '.' &&
+               SCL_pieceIsWhite(model->game.board[i]) == SCL_boardWhitesTurn(model->game.board)) {
+                SCL_SquareSet possibleMoves = SCL_SQUARE_SET_EMPTY;
+
+                SCL_boardGetMoves(model->game.board, i, possibleMoves);
+
+                SCL_SQUARE_SET_ITERATE_BEGIN(possibleMoves)
+                SCL_moveToString(model->game.board, i, iteratedSquare, 'q', string);
+                //printf("%s ", string);
+                SCL_SQUARE_SET_ITERATE_END
+            }
+
+        return FlipChessStatusReturn;
+    }
+
+    model->msg = (SCL_boardWhitesTurn(model->game.board) ? "white to move" : "black to move");
+
+    // 0 = success
+    return FlipChessStatusNone;
+}
+
+bool flipchess_scene_1_input(InputEvent* event, void* context) {
+    furi_assert(context);
+    FlipChessScene1* instance = context;
+    FlipChess* app = instance->context;
+
+    if(event->type == InputTypeRelease) {
+        switch(event->key) {
+        case InputKeyBack:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    if(model->turnState == 1) {
+                        model->turnState = 0;
+                        SCL_squareSetClear(model->moveHighlight);
+                        flipchess_drawBoard(model);
+                    } else {
+                        instance->callback(FlipChessCustomEventScene1Back, instance->context);
+                    }
+                },
+                true);
+            break;
+        case InputKeyRight:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    if(model->squareSelectedLast != 255 && model->squareSelected == 255) {
+                        model->squareSelected = model->squareSelectedLast;
+                    } else {
+                        model->squareSelected = (model->squareSelected + 1) % 64;
+                    }
+                    flipchess_drawBoard(model);
+                },
+                true);
+            break;
+        case InputKeyDown:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    if(model->squareSelectedLast != 255 && model->squareSelected == 255) {
+                        model->squareSelected = model->squareSelectedLast;
+                    } else {
+                        model->squareSelected = (model->squareSelected + 56) % 64;
+                    }
+                    flipchess_drawBoard(model);
+                },
+                true);
+            break;
+        case InputKeyLeft:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    if(model->squareSelectedLast != 255 && model->squareSelected == 255) {
+                        model->squareSelected = model->squareSelectedLast;
+                    } else {
+                        model->squareSelected = (model->squareSelected + 63) % 64;
+                    }
+                    flipchess_drawBoard(model);
+                },
+                true);
+            break;
+        case InputKeyUp:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    if(model->squareSelectedLast != 255 && model->squareSelected == 255) {
+                        model->squareSelected = model->squareSelectedLast;
+                    } else {
+                        model->squareSelected = (model->squareSelected + 8) % 64;
+                    }
+                    flipchess_drawBoard(model);
+                },
+                true);
+            break;
+        case InputKeyOk:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    // if(model->paramExit == FlipChessStatusReturn) {
+                    //     instance->callback(FlipChessCustomEventScene1Back, instance->context);
+                    //     break;
+                    // }
+                    if(!flipchess_isPlayerTurn(model)) {
+                        model->thinking = 1;
+                    }
+                },
+                true);
+            furi_thread_flags_wait(0, FuriFlagWaitAny, THREAD_WAIT_TIME);
+
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    // first turn of round, probably player but could be AI
+                    if(flipchess_turn(model) == FlipChessStatusReturn) {
+                        if(app->sound == 1) flipchess_voice_a_strange_game();
+                        flipchess_play_long_bump(app);
+                    }
+                    flipchess_saveState(app, model);
+                    flipchess_drawBoard(model);
+                },
+                true);
+
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    if(!flipchess_isPlayerTurn(model)) {
+                        model->thinking = 1;
+                    }
+                },
+                true);
+            furi_thread_flags_wait(0, FuriFlagWaitAny, THREAD_WAIT_TIME);
+
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    // if player played, let AI play
+                    if(!flipchess_isPlayerTurn(model)) {
+                        if(flipchess_turn(model) == FlipChessStatusReturn) {
+                            if(app->sound == 1) flipchess_voice_a_strange_game();
+                            flipchess_play_long_bump(app);
+                        }
+                        flipchess_saveState(app, model);
+                        flipchess_drawBoard(model);
+                    }
+                },
+                true);
+            break;
+        case InputKeyMAX:
+            break;
+        }
+    }
+    return true;
+}
+
+void flipchess_scene_1_exit(void* context) {
+    furi_assert(context);
+    FlipChessScene1* instance = (FlipChessScene1*)context;
+
+    with_view_model(
+        instance->view, FlipChessScene1Model * model, { model->paramExit = 0; }, true);
+}
+
+void flipchess_scene_1_enter(void* context) {
+    furi_assert(context);
+    FlipChessScene1* instance = (FlipChessScene1*)context;
+    FlipChess* app = instance->context;
+
+    flipchess_play_happy_bump(app);
+
+    with_view_model(
+        instance->view,
+        FlipChessScene1Model * model,
+        {
+            // load imported game if applicable
+            char* import_game_text = NULL;
+            if(app->import_game == 1 && strlen(app->import_game_text) > 0) {
+                import_game_text = app->import_game_text;
+            } else {
+                if(app->sound == 1) flipchess_voice_how_about_chess();
+            }
+
+            int init = flipchess_scene_1_model_init(
+                model, app->white_mode, app->black_mode, import_game_text);
+
+            if(init == FlipChessStatusNone) {
+                // perform initial turn, sets up and lets white
+                // AI play if applicable
+                const uint8_t turn = flipchess_turn(model);
+                if(turn == FlipChessStatusReturn) {
+                    init = turn;
+                } else {
+                    flipchess_saveState(app, model);
+                    flipchess_drawBoard(model);
+                }
+            }
+
+            // if return status, return from scene immediately
+            // if(init == FlipChessStatusReturn) {
+            //     instance->callback(FlipChessCustomEventScene1Back, instance->context);
+            // }
+        },
+        true);
+}
+
+FlipChessScene1* flipchess_scene_1_alloc() {
+    FlipChessScene1* instance = malloc(sizeof(FlipChessScene1));
+    instance->view = view_alloc();
+    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipChessScene1Model));
+    view_set_context(instance->view, instance); // furi_assert crashes in events without this
+    view_set_draw_callback(instance->view, (ViewDrawCallback)flipchess_scene_1_draw);
+    view_set_input_callback(instance->view, flipchess_scene_1_input);
+    view_set_enter_callback(instance->view, flipchess_scene_1_enter);
+    view_set_exit_callback(instance->view, flipchess_scene_1_exit);
+
+    return instance;
+}
+
+void flipchess_scene_1_free(FlipChessScene1* instance) {
+    furi_assert(instance);
+
+    with_view_model(
+        instance->view, FlipChessScene1Model * model, { UNUSED(model); }, true);
+
+    view_free(instance->view);
+    free(instance);
+}
+
+View* flipchess_scene_1_get_view(FlipChessScene1* instance) {
+    furi_assert(instance);
+    return instance->view;
+}

+ 19 - 0
chess/views/flipchess_scene_1.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../helpers/flipchess_custom_event.h"
+
+typedef struct FlipChessScene1 FlipChessScene1;
+
+typedef void (*FlipChessScene1Callback)(FlipChessCustomEvent event, void* context);
+
+void flipchess_scene_1_set_callback(
+    FlipChessScene1* flipchess_scene_1,
+    FlipChessScene1Callback callback,
+    void* context);
+
+View* flipchess_scene_1_get_view(FlipChessScene1* flipchess_static);
+
+FlipChessScene1* flipchess_scene_1_alloc();
+
+void flipchess_scene_1_free(FlipChessScene1* flipchess_static);

+ 163 - 0
chess/views/flipchess_startscreen.c

@@ -0,0 +1,163 @@
+#include "../flipchess.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include <input/input.h>
+#include <gui/elements.h>
+#include "flipchess_icons.h"
+
+struct FlipChessStartscreen {
+    View* view;
+    FlipChessStartscreenCallback callback;
+    void* context;
+};
+
+typedef struct {
+    int some_value;
+} FlipChessStartscreenModel;
+
+void flipchess_startscreen_set_callback(
+    FlipChessStartscreen* instance,
+    FlipChessStartscreenCallback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(callback);
+    instance->callback = callback;
+    instance->context = context;
+}
+
+void flipchess_startscreen_draw(Canvas* canvas, FlipChessStartscreenModel* model) {
+    UNUSED(model);
+    canvas_clear(canvas);
+    canvas_set_color(canvas, ColorBlack);
+
+    canvas_draw_icon(canvas, 0, 0, &I_FLIPR_128x64);
+
+#ifdef CANVAS_HAS_FONT_SCUMM_ROMAN_OUTLINE
+    const uint8_t text_x_pos = 2;
+    const uint8_t text_y_pos = 12;
+    canvas_set_font(canvas, FontScummRomanOutline);
+#else
+    const uint8_t text_x_pos = 4;
+    const uint8_t text_y_pos = 11;
+    canvas_set_font(canvas, FontPrimary);
+#endif
+    canvas_draw_str(canvas, text_x_pos, text_y_pos, "Chess");
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 62, text_y_pos, FLIPCHESS_VERSION);
+
+    //canvas_set_font(canvas, FontSecondary);
+    //canvas_draw_str(canvas, 10, 11, "How about a nice game of...");
+    //canvas_draw_str(canvas, 99, 40, FLIPCHESS_VERSION);
+
+    //canvas_set_font(canvas, FontPrimary);
+    //canvas_draw_str(canvas, 10, 23, "Chess");
+    //canvas_draw_icon(canvas, 0, 40, &I_Background_128x11);
+    //canvas_draw_str(canvas, 10, 61, "FLIPR");
+
+    elements_button_left(canvas, "Sound");
+    elements_button_right(canvas, "Silent");
+}
+
+static void flipchess_startscreen_model_init(FlipChessStartscreenModel* const model) {
+    model->some_value = 1;
+}
+
+bool flipchess_startscreen_input(InputEvent* event, void* context) {
+    furi_assert(context);
+    FlipChessStartscreen* instance = context;
+    FlipChess* app = instance->context;
+
+    if(event->type == InputTypeRelease) {
+        switch(event->key) {
+        case InputKeyBack:
+            with_view_model(
+                instance->view,
+                FlipChessStartscreenModel * model,
+                {
+                    UNUSED(model);
+                    instance->callback(FlipChessCustomEventStartscreenBack, instance->context);
+                },
+                true);
+            break;
+        case InputKeyLeft:
+            // sound on, haptic off
+            app->sound = 1;
+            app->haptic = FlipChessHapticOff;
+            with_view_model(
+                instance->view,
+                FlipChessStartscreenModel * model,
+                {
+                    UNUSED(model);
+                    instance->callback(FlipChessCustomEventStartscreenOk, instance->context);
+                },
+                true);
+            break;
+        case InputKeyRight:
+            // sound off, haptic on
+            app->sound = 0;
+            app->haptic = FlipChessHapticOn;
+            with_view_model(
+                instance->view,
+                FlipChessStartscreenModel * model,
+                {
+                    UNUSED(model);
+                    instance->callback(FlipChessCustomEventStartscreenOk, instance->context);
+                },
+                true);
+            break;
+        case InputKeyUp:
+        case InputKeyDown:
+        case InputKeyOk:
+        case InputKeyMAX:
+            break;
+        }
+    }
+    return true;
+}
+
+void flipchess_startscreen_exit(void* context) {
+    furi_assert(context);
+}
+
+void flipchess_startscreen_enter(void* context) {
+    furi_assert(context);
+    FlipChessStartscreen* instance = (FlipChessStartscreen*)context;
+    with_view_model(
+        instance->view,
+        FlipChessStartscreenModel * model,
+        { flipchess_startscreen_model_init(model); },
+        true);
+}
+
+FlipChessStartscreen* flipchess_startscreen_alloc() {
+    FlipChessStartscreen* instance = malloc(sizeof(FlipChessStartscreen));
+    instance->view = view_alloc();
+    view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipChessStartscreenModel));
+    view_set_context(instance->view, instance); // furi_assert crashes in events without this
+    view_set_draw_callback(instance->view, (ViewDrawCallback)flipchess_startscreen_draw);
+    view_set_input_callback(instance->view, flipchess_startscreen_input);
+    //view_set_enter_callback(instance->view, flipchess_startscreen_enter);
+    //view_set_exit_callback(instance->view, flipchess_startscreen_exit);
+
+    with_view_model(
+        instance->view,
+        FlipChessStartscreenModel * model,
+        { flipchess_startscreen_model_init(model); },
+        true);
+
+    return instance;
+}
+
+void flipchess_startscreen_free(FlipChessStartscreen* instance) {
+    furi_assert(instance);
+
+    with_view_model(
+        instance->view, FlipChessStartscreenModel * model, { UNUSED(model); }, true);
+    view_free(instance->view);
+    free(instance);
+}
+
+View* flipchess_startscreen_get_view(FlipChessStartscreen* instance) {
+    furi_assert(instance);
+    return instance->view;
+}

+ 19 - 0
chess/views/flipchess_startscreen.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <gui/view.h>
+#include "../helpers/flipchess_custom_event.h"
+
+typedef struct FlipChessStartscreen FlipChessStartscreen;
+
+typedef void (*FlipChessStartscreenCallback)(FlipChessCustomEvent event, void* context);
+
+void flipchess_startscreen_set_callback(
+    FlipChessStartscreen* flipchess_startscreen,
+    FlipChessStartscreenCallback callback,
+    void* context);
+
+View* flipchess_startscreen_get_view(FlipChessStartscreen* flipchess_static);
+
+FlipChessStartscreen* flipchess_startscreen_alloc();
+
+void flipchess_startscreen_free(FlipChessStartscreen* flipchess_static);