HyperChiicken
Published © GPL3+

Prototyping the ESP8266 Little Game Machine

This is a walkthrough of how I implemented mali1741's fork of corax89's ESP8266 Little Game Machine using a NodeMCU and a Wii nunchuck.

IntermediateFull instructions provided2 hours323
Prototyping the ESP8266 Little Game Machine

Things used in this project

Hardware components

NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
×1
ILI9341 SPI TFT LCD Screen (240x320)
×1
Wii Nunchuk
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

ESPLGE_Schematic

Code

ESP_ILI9341_game_engine.ino

Arduino
#include <Arduino.h>
#include <Ticker.h>
#include <SPI.h>
#include <Wire.h>
#include <TFT_eSPI.h>
#include <coos.h>
#include <FS.h>
#include <NintendoExtensionCtrl.h>

ADC_MODE(ADC_VCC);

// Use nunchuck
Nunchuk nchuk;
// Use hardware SPI
TFT_eSPI tft = TFT_eSPI();
#define RAM_SIZE 20 * 1024

// ------------------begin ESP8266'centric----------------------------------
#define FREQUENCY    160                  // valid 80, 160
//
#include "ESP8266WiFi.h"
extern "C" {
  #include "user_interface.h"
}
// ------------------end ESP8266'centric------------------------------------
int voltaje=0;
byte i2c_adress;
byte thiskey;
char c;
Ticker timer;
uint16_t cadr_count;
unsigned long timeF,timeR;
int timeCpu = 0,timeGpu = 0,timeSpr = 0,cpuOPS = 0;
byte fps;
volatile uint16_t timers[8];
uint8_t mem[RAM_SIZE] = {0x12,0x00,0x06,0x20,0xA1,0x01,0x90,0x00,0x24,0x01,0x11,0x08,0xD4,0x21,0xD0,0x00,0x12,0x01,0x06,0x20,0xA1,0x01,0x03,0x01,0xA1,0x01,0x12,0x0C,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0x50,0x00,0x01,0x02,0x46,0x01,0x82,0x02,0x12,0x38,0x82,0x02,0x03,0x02,0xA1,0x01,0x82,0x02,0x12,0x10,0x82,0x02,0x12,0x10,0x82,0x02,0x99,0x00,0x3C,0x01,0xA8,0xA0,0x99,0x00,0x32,0x01,0xA8,0x10,0xA1,0x01,0x90,0x00,0x16,0x00,0x11,0x02,0xD4,0x51,0x12,0x0C,0x06,0x20,0xA1,0x01,0x03,0x01,0xA1,0x01,0x12,0x14,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0x94,0x00,0x01,0x02,0x46,0x01,0x82,0x02,0x12,0x30,0x82,0x02,0x03,0x02,0xA1,0x01,0x82,0x02,0x12,0x10,0x82,0x02,0x12,0x10,0x82,0x02,0x99,0x00,0x3C,0x01,0xA8,0xA0,0x99,0x00,0x32,0x01,0xA8,0x10,0xA1,0x01,0x90,0x00,0x5A,0x00,0x11,0x03,0xD4,0x51,0x12,0x14,0x06,0x20,0xA1,0x01,0x03,0x01,0xA1,0x01,0x12,0x1C,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0xD8,0x00,0x01,0x02,0x46,0x01,0x82,0x02,0x12,0x28,0x82,0x02,0x03,0x02,0xA1,0x01,0x82,0x02,0x12,0x10,0x82,0x02,0x12,0x10,0x82,0x02,0x99,0x00,0x3C,0x01,0xA8,0xA0,0x99,0x00,0x32,0x01,0xA8,0x10,0xA1,0x01,0x90,0x00,0x9E,0x00,0x11,0x04,0xD4,0x51,0x01,0x02,0x46,0x01,0x82,0x02,0x12,0x20,0x82,0x02,0x03,0x02,0xA1,0x01,0x82,0x02,0x12,0x10,0x82,0x02,0x12,0x10,0x82,0x02,0x99,0x00,0x3C,0x01,0xA8,0xA0,0x12,0x14,0x06,0x20,0xA1,0x01,0x03,0x01,0xA1,0x01,0x12,0x28,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0x1C,0x01,0x99,0x00,0x32,0x01,0xA8,0x10,0xA1,0x01,0x90,0x00,0x00,0x01,0x11,0x00,0xD4,0x21,0xD0,0x00,0x9A,0x00,0x01,0x0F,0x00,0x00,0x06,0xF0,0xA3,0x01,0x99,0x00,0x0A,0x00,0x50,0x00,0xC2,0x16,0xB1,0x00,0x92,0x00,0x32,0x01,0x9A,0x00,0x07,0x10,0x12,0x02,0xA0,0x12,0xD4,0x71,0x9A,0x00,0x08,0x88,0x87,0x86,0x66,0x88,0x66,0x86,0x68,0x02,0x88,0x85,0x86,0x68,0x86,0x88,0x02,0x86,0x02,0x88,0x82,0x86,0x02,0x88,0x02,0x86,0x82,0x68,0x02,0x88,0x86,0x86,0x66,0x86,0x68,0x86,0x0D,0x88,0x82,0x86,0x02,0x88,0x84,0x66,0x86,0x66,0x02,0x88,0x87,0x86,0x88,0x86,0x88,0x86,0x68,0x02,0x88,0x83,0x86,0x88,0x03,0x86,0x03,0x88,0x87,0x86,0x66,0x88,0x66,0x86,0x66,0x0A,0x88,0x82,0x68,0x0E,0x88,0x88,0x68,0x88,0x68,0x88,0x86,0x88,0x68,0x0A,0x88,0x82,0x68,0x05,0x88};
uint16_t palette[16] = {
    0x0020, 0xE718, 0xB9A8, 0x7DB6, 0x41E9, 0x6D2D, 0x21EC, 0xD5CA,
    0xAC4D, 0x42CB, 0xBB09, 0x3186, 0x73AE, 0x8D4B, 0x3DF9, 0xbdd7
};

uint16_t bgr_to_rgb(uint16_t c){
  return ((c & 0x001f) << 11) + ((c & 0xf800) >> 11) + (c & 0x07e0);
}

unsigned char hexToByte(char h){
  if(h < 48)
    return 48;
  if (h >= 48 && h <= 57) 
    h = map(h, 48, 57, 0, 9);
  else if (h >= 65 && h <= 70) 
    h = map(h, 65, 70, 10, 15);
  else if (h >= 97 && h <= 102) 
    h = map(h, 97, 102, 10, 15);
  return h;
}

void loadFromSerial(){
  char c;
  unsigned char n;
  int16_t j = 0;
  for(int16_t i = 0; i < RAM_SIZE; i++)
    mem[i] = 0;
  while(c != '.'){
    if(Serial.available()){
      c = Serial.read();
      Serial.print(c);
      if(c == '$'){
        n = 48;
        while(n > 15){
          c = Serial.read();
          n = hexToByte(c);
        }
        Serial.print(c);
        mem[j] = n << 4;
        n = 48;
        while(n > 15){
          c = Serial.read();
          n = hexToByte(c);
        }
        Serial.print(c);
        mem[j] += n;
        j++;
      }
    }
  }
  Serial.print(F("load "));
  Serial.print(j);
  Serial.println(F(" byte"));
  Serial.print(F("free heap "));
  Serial.println(system_get_free_heap_size());
  cpuInit();
}

void changeSettings(){
  if(Serial.available()){
    c = Serial.read();
    Serial.print(c);
    if(c == 'm'){
      loadFromSerial();
      cpuInit();
      return;
    }
    else if(c == 'r'){
      ESP.reset();
      return;
    }
    else if(c == 'd'){
      debug();
      return;
    }
    else if(c == 'v'){
      Serial.println();
      Serial.println(F("input new resolution"));
      int w = 0;
      int h = 0;
      while(Serial.available() == 0){}
      c = Serial.read();
      if(c <= 47 || c > 57){
        while(Serial.available() == 0){}
        c = Serial.read();
      }
      while(c > 47 && c <= 57){
        w = w * 10 + (c - 48);
        while(Serial.available() == 0){}
        c = Serial.read();
      }
      Serial.print(w);
      Serial.print(' ');
      while(Serial.available() == 0){}
      c = Serial.read();
      while(c > 47 && c <= 57){
        h = h * 10 + (c - 48);
        while(Serial.available() == 0){}
        c = Serial.read();
      }
      Serial.println(h);
      setScreenResolution(w, h);
      return;
    }
  }
}

void coos_cpu(void){   
  while(1){
    COOS_DELAY(1);        // 1 ms
    timeR = millis();
    cpuOPS += 1;
    cpuRun(1100);  
    timeCpu += millis() - timeR;
  }
}

void coos_screen(void){
   while(1){
    yield();
    COOS_DELAY(50);        // 50 ms
    timeR = millis();
    clearSpriteScr();
    redrawSprites();
    testSpriteCollision();
    redrawParticles();
    timeSpr += millis() - timeR;
    timeR = millis();
    redrawScreen();
    setRedraw(); 
    timeGpu += millis() - timeR;
    if(millis() - timeF > 1000){
      timeF = millis();
      fps = cadr_count;
      cadr_count = cadr_count % 2;
    }  
  } 
}

void timer_tick(void){
  for(int8_t i = 0; i < 8; i++){
    if(timers[i] >= 1)
      timers[i] --;
  }
}

void coos_key(void){   
  while(1){
    COOS_DELAY(100);        // 100 ms
    getKey(); 
  }
}

void coos_info(void){   
  while(1){
    COOS_DELAY(1000);        // 1000 ms
    voltaje = ESP.getVcc();
    if(getDisplayXOffset() > 30){
      tft.fillRect(0, 0, 30, 92, 0x0000);
      tft.setCursor(1, 0);
      tft.println("fps");
      tft.println(fps);
      tft.println("cpu");
      tft.println(timeCpu, DEC);
      tft.println("gpu");
      tft.println(timeGpu, DEC);
      tft.println("spr");
      tft.println(timeSpr, DEC);
      tft.println("kIPS");
      tft.println(cpuOPS, DEC);
    }
    timeCpu = 0;
    timeGpu = 0;
    timeSpr = 0;
    cpuOPS = 0;
  }
}

void setup() {
  byte menuSelected = 3;
  // ------------------begin ESP8266'centric----------------------------------
  WiFi.forceSleepBegin();                  // turn off ESP8266 RF
  delay(1);                                // give RF section time to shutdown
  system_update_cpu_freq(FREQUENCY);
  // ------------------end ESP8266'centric------------------------------------
  tft.init();            // initialize LCD
  tft.setRotation(1);
  tft.fillScreen(0x0000);
  tft.setTextSize(1);
  tft.setTextColor(0xffff);
  Serial.begin (115200);
  nchuk.begin();
  while (!nchuk.connect()) {
    Serial.println("Nunchuk not detected!");
    delay(1000);
  }
  Serial.println();
  cpuInit();
  //Initialize File System
  if(SPIFFS.begin()){
    Serial.println(F("SPIFFS Initialize....ok"));
    //fileList("/");
  }
  else{
    Serial.println(F("SPIFFS Initialization...failed"));
  }
  Serial.print(F("FreeHeap:"));
  Serial.println(ESP.getFreeHeap());
  Serial.println(F("print \"vW H\" for change viewport, \"d name\" for delite file,"));
  Serial.println(F("\"s name\" for save file and \"m\" for load to memory"));
  voltaje = ESP.getVcc();
  randomSeed(ESP.getVcc());
  clearScr(0);
  setColor(1);
  setScreenResolution(239, 239);
  randomSeed(analogRead(0));
  timer.attach(0.001, timer_tick);
  coos.register_task(coos_cpu); 
  coos.register_task(coos_screen);   
  coos.register_task(coos_key); 
  coos.register_task(coos_info);
  coos.start();                     // init registered tasks
}

void loop() {
  coos.run();  // Coos scheduler
  changeSettings();
}

cpu.ino

Arduino
Code accompanies ESP_ILI9341_game_engine.ino. And yes, some comments are in Russian (I think).
#define FIFO_MAX_SIZE 32

int16_t reg[16];
int16_t shadow_reg[16];
uint16_t pc = 0;
uint16_t interrupt = 0;
byte carry = 0;
byte zero = 0;
byte negative = 0;
byte redraw = 0;
int8_t color = 1;
int8_t bgcolor = 0;
String s_buffer;

struct Fifo_t {
  uint16_t el[FIFO_MAX_SIZE];
  uint8_t position_read;
  uint8_t position_write;
  uint8_t size;
};

struct Fifo_t interruptFifo;

void fifoClear(){
  interruptFifo.position_read = 0;
  interruptFifo.position_write = 0;
  interruptFifo.size = 0;
}

void pushInFifo(int16_t n){
  if(interruptFifo.size < FIFO_MAX_SIZE){
    interruptFifo.el[interruptFifo.position_write] = n;
    interruptFifo.position_write++;
    if(interruptFifo.position_write >= FIFO_MAX_SIZE)
      interruptFifo.position_write = 0;
    interruptFifo.size++;
  }
}

uint16_t popOutFifo(){
  uint16_t out = 0;
  if(interruptFifo.size > 0){
    interruptFifo.size--;
    out = interruptFifo.el[interruptFifo.position_read];
    interruptFifo.position_read++;
    if(interruptFifo.position_read >= FIFO_MAX_SIZE)
      interruptFifo.position_read = 0;
  }
  return out;
}

int16_t flagsToByte(){
  return (carry & 0x1) + ((zero & 0x1) << 1)  + ((negative & 0x1) << 2);
}
  
void byteToFlags(int16_t b){
  carry = b & 0x1;
  zero = (b & 0x2) >> 1;
  negative = (b & 0x4) >> 2;
}

void setinterrupt(uint16_t adr, int16_t param){
  if(interrupt == 0 && adr != 0){
    shadow_reg[0] = flagsToByte();
    for(int8_t j = 1; j <= 15; j++){
      shadow_reg[j] = reg[j];
    }
    reg[0] -= 2;
    writeInt(reg[0], param);
    reg[0] -= 2;
    writeInt(reg[0], pc);
    interrupt = pc;
    pc = adr;
  }
  else{
    pushInFifo(adr);
    pushInFifo(param);
  }
}

void cpuInit(){
  for(byte i = 1; i < 16; i++){
    reg[i] = 0;
  }
  interrupt = 0;
  fifoClear();
  display_init();
  reg[0] = RAM_SIZE - 1;//stack pointer
  clearScr(0);
  color = 1;
  bgcolor = 0;
  setCharX(0);
  setCharY(0);
  pc = 0;
  carry = 0;
  zero = 0;
  negative = 0;
  tft.setTextColor(palette[color]);
}

void debug(){
  for(byte i = 0; i < 16; i++){
    Serial.print(" R");
    Serial.print(i);
    Serial.print(':');
    Serial.print(reg[i]);
  }
  Serial.print(F(" OP:"));
  Serial.print(readMem(pc),HEX);
  Serial.print(F(" PC:"));
  Serial.println(pc);
  Serial.print(F("carry: "));
  Serial.print(carry);
  Serial.print(F(" zero: "));
  Serial.print(zero);
  Serial.print(F(" negative: "));
  Serial.print(negative);
  Serial.print(F(" interrupt: "));
  Serial.print(interrupt);
  Serial.print('/');
  Serial.println(interruptFifo.size);
}

inline void writeInt(uint16_t adr, int16_t n){
  int8_t *nPtr;
  nPtr = (int8_t*)&n;
  writeMem(adr, *nPtr);
  nPtr++;
  adr++;
  writeMem(adr, *nPtr);
  //writeMem(adr + 1, (n & 0xff00) >> 8);
  //writeMem(adr, n & 0xff);
}

inline int16_t readInt(uint16_t adr){
  int16_t n;
  int8_t *nPtr;
  nPtr = (int8_t*)&n;
  *nPtr = readMem(adr);
  nPtr++;
  adr++;
  *nPtr = readMem(adr);
  return n;
  //return (readMem(adr + 1) << 8) + readMem(adr);
}

inline void writeMem(uint16_t adr, int16_t n){
  if(adr < RAM_SIZE)
    mem[adr] = n;
}

inline byte readMem(uint16_t adr){
  return (adr < RAM_SIZE) ? mem[adr] : 0;
}

void setRedraw(){
  redraw = 1;
}

inline int16_t setFlags(int32_t n){
  carry = (n > 0xffff) ? 1 : 0;
  zero = (n == 0) ? 1 : 0;
  negative = (n < 0) ? 1 : 0;
  return (int16_t)n;
}

inline int16_t setFlagsC(int16_t n){
  carry = (n > 0xff) ? 1 : 0;
  zero = (n == 0) ? 1 : 0;
  negative = (n < 0) ? 1 : 0;
  return (uint16_t)n;
}

int16_t isqrt(int16_t n) {
  int g = 0x8000;
  int c = 0x8000;
  for (;;) {
    if (g*g > n) {
      g ^= c;
    }
    c >>= 1;
    if (c == 0) {
      return g;
    }
    g |= c;
  }
}

int16_t distancepp(int16_t x1, int16_t y1, int16_t x2, int16_t y2){
  return isqrt((x2 - x1)*(x2 - x1) + (y2-y1)*(y2-y1));
}

void cpuRun(uint16_t n){
  for(uint16_t i=0; i < n; i++)
    cpuStep();
}

void cpuStep(){
  byte op1 = readMem(pc++);
  byte op2 = readMem(pc++);
  byte reg1 = 0;
  byte reg2 = 0;
  byte reg3 = 0;
  uint16_t adr;
  uint16_t j;
  uint32_t n = 0;
  //if(isDebug)
  //  debug();
  switch(op1 >> 4){
    case 0x0:
      switch(op1){ 
        case 0x01: 
          //LDI R,int   01 0R XXXX
          reg1 = (op2 & 0xf);
          reg[reg1] = readInt(pc);
          setFlags(reg[reg1]);
          pc += 2;
          break;
        case 0x02: 
          //LDI R,(R)   02 RR
          reg1 = ((op2 & 0xf0) >> 4);
          reg2 = (op2 & 0xf);
          reg[reg1] = readInt(reg[reg2]);
          setFlags(reg[reg1]);
          break;
        case 0x03: 
          //LDI R,(adr) 03 0R XXXX
          reg1 = (op2 & 0xf);
          reg[reg1] = readInt(readInt(pc));
          setFlags(reg[reg1]);
          pc += 2;
          break;
        case 0x04: 
          //LDI R,(int+R) 04 RR XXXX
          reg1 = ((op2 & 0xf0) >> 4);
          reg2 = (op2 & 0xf);
          reg[reg1] = readInt(reg[reg2] + readInt(pc));
          setFlags(reg[reg1]);
          pc += 2;
          break;
        case 0x05: 
          //STI (R),R   05 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          writeInt(reg[reg1],reg[reg2]);
          break;
        case 0x06:
          if((op2 & 0x0f) == 0){
            //STI (adr),R 06 R0 XXXX
            reg1 = (op2 & 0xf0) >> 4;
            writeInt(readInt(pc),reg[reg1]);
            pc += 2;
          }
          else{
            //STI (adr+R),R 06 RR XXXX
            reg1 = (op2 & 0xf0) >> 4;
            reg2 = op2 & 0xf;
            writeInt(readInt(pc) + reg[reg1],reg[reg2]);
            pc += 2;
          }
          break;
        case 0x07:
          //MOV R,R   07 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          reg[reg1] = reg[reg2];
          break;
        case 0x08:
          //LDIAL R,(int+R*2) 08 RR XXXX
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          reg[reg1] = readInt(reg[reg2] * 2 + readInt(pc));
          setFlags(reg[reg1]);
          pc += 2;
          break;
        case 0x09:
          //STIAL (adr+R*2),R   09 RR XXXX
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          writeInt(readInt(pc) + reg[reg1] * 2,reg[reg2]);
          pc += 2;
          break;
        default:
          pc++;
      }
      break;
    case 0x1:
      // LDC R,char 1R XX
      reg1 = (op1 & 0xf);
      reg[reg1] = op2;
      setFlagsC(reg[reg1]);
      break;
    case 0x2:
      if(op1 == 0x20){
        // LDC R,(R)  20 RR
        reg1 = ((op2 & 0xf0) >> 4);
        reg2 = (op2 & 0xf);
        reg[reg1] = readMem(reg[reg2]);
        setFlagsC(reg[reg1]);
      }
      else{
        // LDC R,(R+R)  2R RR
        reg1 = (op1 & 0xf);
        reg2 = ((op2 & 0xf0) >> 4);
        reg3 = (op2 & 0xf);
        reg[reg1] = readMem(reg[reg2] + reg[reg3]);
        setFlagsC(reg[reg1]);
      }
      break;
    case 0x3: 
      switch(op1){
        case 0x30:
          // LDC R,(int+R)30 RR XXXX
          reg1 = ((op2 & 0xf0) >> 4);
          reg2 = (op2 & 0xf);
          reg[reg1] = readMem(reg[reg2] + readInt(pc));
          setFlagsC(reg[reg1]);
          pc += 2;
          break;
        case 0x31:
          // LDC R,(adr)  31 0R XXXX
          reg1 = (op2 & 0xf);
          reg[reg1] = readMem(readInt(pc));
          setFlagsC(reg[reg1]);
          pc += 2;
          break;
        case 0x32:
          // STC (adr),R  32 0R XXXX
          reg1 = (op2 & 0xf0) >> 4;
          writeMem(readInt(pc),reg[reg1]);
          pc += 2;
          break;
        case 0x33:
          // STC (int+R),R33 RR XXXX
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          writeMem(readInt(pc) + reg[reg1],reg[reg2]);
          pc += 2;
          break;
      }
      break;
    case 0x4:
      if(op1 == 0x40){
          // STC (R),R  40 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          writeMem(reg[reg1], reg[reg2]);
        }
        else{
          // STC (R+R),R  4R RR 
          reg1 = (op1 & 0xf);
          reg2 = ((op2 & 0xf0) >> 4);
          reg3 = (op2 & 0xf);
          writeMem(reg[reg1] + reg[reg2], reg[reg3]);
        }
      break;
    case 0x5:
      switch(op1){ 
        case 0x50:
          //HLT       5000
          //pc -= 2;
          fileList("/");
          break;
        case 0x51:
          // STIMER R,R   51RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          timers[reg[reg1] & 0x7] = reg[reg2];
          break;
        case 0x52:
          // GTIMER R   520R
          reg1 = op2 & 0xf;
          reg[reg1] = timers[reg[reg1] & 0x7];
          setFlags(reg[reg1]);
          break;
      }
      break;
    case 0x6:
      // LDI R,(R+R)  6R RR
      reg1 = (op1 & 0xf);
      reg2 = ((op2 & 0xf0) >> 4);
      reg3 = (op2 & 0xf);
      reg[reg1] = readInt(reg[reg2] + reg[reg3]);
      setFlags(reg[reg1]);
      break;
    case 0x7:
      // STI (R+R),R  7R RR
      reg1 = (op1 & 0xf);
      reg2 = ((op2 & 0xf0) >> 4);
      reg3 = (op2 & 0xf);
      writeInt(reg[reg1] + reg[reg2], reg[reg3]);
      break;  
    case 0x8:
      switch(op1){
        case 0x80:
          // POP R    80 0R
          reg1 = (op2 & 0xf);
          reg[reg1] = readInt(reg[0]);
          reg[0] += 2;
          break;
        case 0x81:
          // POPN R   81 0R
          reg1 = (op2 & 0xf);
          for(j = reg1; j >= 1; j--){
            reg[j] = readInt(reg[0]);
            reg[0] += 2;
          }
          break;
        case 0x82:
          // PUSH R   82 0R
          reg1 = (op2 & 0xf);
          reg[0] -= 2;
          writeInt(reg[0], reg[reg1]);
          break;
        case 0x83:
          // PUSHN R    83 0R
          reg1 = (op2 & 0xf);
          for(j = 1; j <= reg1; j++){
            reg[0] -= 2;
            writeInt(reg[0], reg[j]);
          }
          break;
      }
      break;
    case 0x9:
      switch(op1){
        case 0x90:
          // JMP adr    90 00 XXXX
          pc = readInt(pc);
          break;
        case 0x91:
          // JNZ adr    91 00 XXXX
          if(zero == 0)
            pc = readInt(pc);
          else 
            pc += 2;
          break;
        case 0x92:
          // JZ adr   92 00 XXXX
          if(zero != 0)
            pc = readInt(pc);
          else 
            pc += 2;
          break;
        case 0x93:
          // JNP adr    93 00 XXXX
          if(negative == 1)
            pc = readInt(pc);
          else 
            pc += 2;
          break;
        case 0x94:
          // JP adr   94 00 XXXX
          if(negative != 1)
            pc = readInt(pc);
          else 
            pc += 2;
          break;
        case 0x95:
          // JNC adr    95 00 XXXX
          if(carry != 1)
            pc = readInt(pc);
          else 
            pc += 2;
          break;
        case 0x96:
          // JC adr   96 00 XXXX
          if(carry == 1)
            pc = readInt(pc);
          else 
            pc += 2;
          break;
        case 0x97:
          // JZR R,adr  97 0R XXXX
          reg1 = op2 & 0xf;
          if(reg[reg1] == 0)
            pc = readInt(pc);
          else 
            pc += 2;
          break;
        case 0x98:
          // JNZR R,adr 98 0R XXXX
          reg1 = op2 & 0xf;
          if(reg[reg1] != 0)
            pc = readInt(pc);
          else 
            pc += 2;
          break;
        case 0x99:
          // CALL adr   99 00 XXXX
          reg[0] -= 2;
          if(reg[0] < 0)
            reg[0] += 0xffff;
          writeInt(reg[0], pc + 2);
          pc = readInt(pc);
          break;
        case 0x9A:
          // RET      9A 00
          if(interrupt == 0){
            pc = readInt(reg[0]);
            reg[0] += 2;
          }
          else{
            pc = readInt(reg[0]);
            if(pc == interrupt){
              reg[0] += 4;
              for(int8_t j = 15; j >= 1; j--){
                reg[j] = shadow_reg[j];
              }
              byteToFlags(shadow_reg[0]);
              interrupt = 0;
              if(interruptFifo.size > 0)
                setinterrupt(popOutFifo(), popOutFifo());
            }
            else
              reg[0] += 2;
          }
          break;
      }
      break;
    case 0xA:
      switch(op1){
        case 0xA0:
          // ADD R,R    A0 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] + reg[reg2];
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xA1:
          // ADC R,R    A1 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] + reg[reg2] + carry;
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xA2:
          // SUB R,R    A2 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] - reg[reg2];
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xA3:
          // SBC R,R    A3 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] - reg[reg2] - carry;
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xA4:
          // MUL R,R    A4 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] * reg[reg2];
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xA5:
          // DIV R,R    A5 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          if(reg[reg2] != 0)
            n = reg[reg1] / reg[reg2];
          else
            n = 0;//error
          n = setFlags(n);
          reg[reg2] = reg[reg1] % reg[reg2];
          reg[reg1] = n;
          break;
        case 0xA6:
          // AND R,R    A6 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] & reg[reg2];
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xA7:
          // OR R,R   A7 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] | reg[reg2];
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xA8:
          if(op2 == 0x10){
            // INC adr    A8 10 XXXX
            reg1 = op2 & 0xf;
            n = readInt(readInt(pc)) + 1;
            n = setFlags(n);
            writeInt(readInt(pc), n);
            pc += 2;
          }
          else if(op2 > 0x10){
            // INC R,n    A8 nR
            reg1 = op2 & 0xf;
            n = reg[reg1] + (op2 >> 4);
            n = setFlags(n);
            reg[reg1] = n;
          }
          else{
            // INC R    A8 0R       
            reg1 = op2 & 0xf;
            n = reg[reg1] + 1;
            n = setFlags(n);
            reg[reg1] = n;
          }
          break;
        case 0xA9:
          if(op2 == 0x10){
            // DEC adr    A9 10 XXXX
            reg1 = op2 & 0xf;
            n = readInt(readInt(pc)) - 1;
            n = setFlags(n);
            writeInt(readInt(pc), n);
            pc += 2;
          }
          else if(op2 > 0x10){
            // DEC R,n    A9 nR
            reg1 = op2 & 0xf;
            n = reg[reg1] - (op2 >> 4);
            n = setFlags(n);
            reg[reg1] = n;
          }
          else{
            // DEC R    A9 0R
            reg1 = op2 & 0xf;
            n = reg[reg1] - 1;
            n = setFlags(n);
            reg[reg1] = n;
          }
          break;
        case 0xAA:
          // XOR R,R    AA RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] ^ reg[reg2];
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xAB:
          // SHL R,R    AB RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] << reg[reg2];
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xAC:
          // SHR R,R    AC RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] >> reg[reg2];
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xAD:
          reg1 = op2 & 0xf;
          reg2 = op2 & 0xf0;
          // RAND R,R   AD 0R
          if(reg2 == 0x00){
            n = random(0, reg[reg1] + 1);
            n = setFlags(n);
            reg[reg1] = n;
          }
          // SQRT R    AD 1R
          else if(reg2 == 0x10){
            n = isqrt(reg[reg1]);
            n = setFlags(n);
            reg[reg1] = n;
          }
          break;
        case 0xAE:
          // ANDL R,R   AE RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = (reg[reg1] != 0 && reg[reg2] != 0) ? 1 : 0;
          n = setFlags(n);
          reg[reg1] = n;
          break;
        case 0xAF:
          // ORL R,R    AF RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = (reg[reg1] != 0 || reg[reg2] != 0) ? 1 : 0;
          n = setFlags(n);
          reg[reg1] = n;
          break;
      }
      break;
    case 0xB:
      //CMP R,CHR   BR XX
      reg1 = (op1 & 0x0f);
      n = reg[reg1] - op2;
      n = setFlags(n);
      break;
    case 0xC:
      switch(op1){
        case 0xC0:
          //CMP R,INT   C0 R0 XXXX
          reg1 = (op2 & 0xf0) >> 4;
          n = reg[reg1] - readInt(pc);
          n = setFlags(n);
          pc += 2;
          break;
        case 0xC1:
          //CMP R,R   C1 RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          n = reg[reg1] - reg[reg2];
          n = setFlags(n);
          break;
        case 0xC2:
          //LDF R,F   C2 RF
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          if(reg2 == 0)
            reg[reg1] = carry;
          else if(reg2 == 1)
            reg[reg1] = zero;
          else if(reg2 == 2)
            reg[reg1] = negative;
          else if(reg2 == 3){ //pozitive
            if(negative == 0 && zero == 0)
              reg[reg1] = 1;
            else
              reg[reg1] = 0;
          }
          else if(reg2 == 4){ //not pozitive
            if(negative == 0 && zero == 0)
              reg[reg1] = 0;
            else
              reg[reg1] = 1;
          }
          else if(reg2 == 5)
            reg[reg1] = 1 - zero;
          else if(reg2 == 6){
              reg[reg1] = redraw;
              redraw = 0;
            }
          else
            reg[reg1] = 0;
          break;
      }
      break;
    case 0xD:
      switch(op1){ 
        case 0xD0:
          //CLS   D000
          clearScr(bgcolor);
          break;
        case 0xD1:
          switch(op2 & 0xf0){
            case 0x00:
              //PUTC R  D10R
              reg1 = (op2 & 0xf);
              printc((char)reg[reg1], color, bgcolor);
              break;
            case 0x10:
              //PUTS R  D11R
              reg1 = (op2 & 0xf);
              j = 0;
              while(!(readMem(reg[reg1] + j) == 0 || j > 1000)){
                printc((char)(readMem(reg[reg1] + j)), color, bgcolor);
                j++;
              }
              break;
            case 0x20:
              //PUTN R D12R
              reg1 = (op2 & 0xf);
              if(reg[reg1] < 32768)
                s_buffer = String(reg[reg1]);
              else
                s_buffer = String(reg[reg1] - 0x10000);
              for(j = 0; j < s_buffer.length(); j++){
                printc(s_buffer[j], color, bgcolor);
              }
              break;
            case 0x30:
              //SETX R      D13R
              reg1 = (op2 & 0xf);
              setCharX(reg[reg1] & 0xff);
              break;
            case 0x40:
              //SETY R      D14R
              reg1 = (op2 & 0xf);
              setCharY(reg[reg1] & 0xff);
              break;
          }
          break;
        case 0xD2: 
          switch(op2 & 0xf0){
            case 0x00:
              // GETK R     D20R
              reg1 = (op2 & 0xf);
              if(Serial.available())
                reg[reg1] = Serial.read();
              else
                pc -= 2;
              break;
            case 0x10:
              // GETJ R     D21R
              reg1 = (op2 & 0xf);
              reg[reg1] = thiskey;
              break;
          }
          break;
        case 0xD3:
          // PPIX R,R   D3RR
          reg1 = (op2 & 0xf0) >> 4;
          reg2 = op2 & 0xf;
          setPix(reg[reg1], reg[reg2], color);
          break;
        case 0xD4:
          switch(op2 & 0xf0){
              case 0x00:
                // DRWIM R      D40R
                reg1 = op2 & 0xf;
                adr = reg[reg1];//регистр указывает на участок памяти, в котором расположены последовательно h, w, y, x, адрес
                drawImg(readInt(adr + 8), readInt(adr + 6), readInt(adr + 4), readInt(adr + 2), readInt(adr));
                break;
              case 0x10:
                // SFCLR R      D41R
                reg1 = op2 & 0xf;
                color = reg[reg1] & 0xf;
                break;
              case 0x20:
                // SBCLR R      D42R
                reg1 = op2 & 0xf;
                bgcolor = reg[reg1] & 0xf;
                break;
              case 0x30:
                // GFCLR R      D43R
                reg1 = op2 & 0xf;
                reg[reg1] = color;
                break;
              case 0x40:
                // GBCLR R      D44R
                reg1 = op2 & 0xf;
                reg[reg1] = bgcolor;
                break;
              case 0x50:
               // ISIZE      D45R
                reg1 = op2 & 0xf;
                setImageSize(reg[reg1] & 31);
                break;
              case 0x60:
                // DLINE      D46R
                reg1 = op2 & 0xf;
                adr = reg[reg1];//регистр указывает на участок памяти, в котором расположены последовательно y1, x1, y, x
                drwLine(readInt(adr + 6), readInt(adr + 4), readInt(adr + 2), readInt(adr));
                break;
              case 0x070:
                // // DRWRLE R   D47R
                reg1 = op2 & 0xf;
                adr = reg[reg1];//регистр указывает на участок памяти, в котором расположены последовательно h, w, y, x, адрес
                drawImgRLE(readInt(adr + 8), readInt(adr + 6), readInt(adr + 4), readInt(adr + 2), readInt(adr));
                break;
              case 0x80:
                // LDTILE R   D4 8R
                reg1 = op2 & 0xf;
                adr = reg[reg1];//регистр указывает на участок памяти, в котором расположены последовательно height, width, iheight, iwidth, adr
                loadTile(readInt(adr + 8), readInt(adr + 6), readInt(adr + 4), readInt(adr + 2), readInt(adr));
                break;
              case 0x90:
                // SPRSDS R*2 D4 9R
                reg1 = op2 & 0xf;
                adr = reg[reg1];//регистр указывает на участок памяти, в котором расположены последовательно direction, speed, n
                spriteSetDirectionAndSpeed(readInt(adr + 4), readInt(adr + 2), readInt(adr));
                break;
              case 0xA0:
                // DRW1BIT R D4AR
                reg1 = op2 & 0xf;
                adr = reg[reg1];//регистр указывает на участок памяти, в котором расположены последовательно h, w, y, x, адрес
                drawImageBit(readInt(adr + 8), readInt(adr + 6), readInt(adr + 4), readInt(adr + 2), readInt(adr));
                break;
            }
            break;
        case 0xD5:
          // LDSPRT R,R   D5RR
          reg1 = (op2 & 0xf0) >> 4;//номер спрайта
          reg2 = op2 & 0xf;//адрес спрайта
          setSpr(reg[reg1] & 0x1f, reg[reg2]);
          break;
        case 0xD6:
          // SPALET R,R   D6 RR
          reg1 = (op2 & 0xf0) >> 4;//номер спрайта
          reg2 = op2 & 0xf;//width
          changePalette(reg[reg1] & 15, reg[reg2]);
          break;
        case 0xD7:
            reg1 = op2 & 0xf;
            adr = reg[reg1];
            // SPART R     D7 0R
            if((op2 & 0xf0) == 0x0)
              //регистр указывает на участок памяти, в котором расположены последовательно count, time, gravity
              setParticle(readInt(adr + 4), readInt(adr + 2), readInt(adr));
            else if((op2 & 0xf0) == 0x10)
              //регистр указывает на участок памяти, в котором расположены последовательно speed, direction2, direction1, time
              setEmitter(readInt(adr + 6), readInt(adr + 4), readInt(adr + 2), readInt(adr));
            else if((op2 & 0xf0) == 0x20)
              //регистр указывает на участок памяти, в котором расположены последовательно color, y, x
              drawParticle(readInt(adr + 4), readInt(adr + 2), readInt(adr) & 0xf);
            else if((op2 & 0xf0) == 0x50)
              //регистр указывает на участок памяти, в котором расположены последовательно y2,x2,y1,x1
              reg[1] = distancepp(readInt(adr + 6), readInt(adr + 4), readInt(adr + 2), readInt(adr));
            break;
        case 0xD8:
          // SCROLL R,R   D8RR
          reg1 = (op2 & 0xf0) >> 4;//шаг
          reg2 = op2 & 0xf;//направление
          scrollScreen(1, reg[reg2]);
          break;
        case 0xD9:
          // GETPIX R,R   D9RR
          reg1 = (op2 & 0xf0) >> 4;//x
          reg2 = op2 & 0xf;//y
          reg[reg1] = getPix(reg[reg1], reg[reg2]);
          break;
        case 0xDA:
          // DRTILE R   DA RR
          reg1 = (op2 & 0xf0) >> 4;//x
          reg2 = op2 & 0xf;//y
          drawTile(reg[reg1], reg[reg2]);
          break;
        case 0xDB:
          // SPRSPX R,R   DB RR
          reg1 = (op2 & 0xf0) >> 4;//num
          reg2 = op2 & 0xf;//speed y

          break;
        case 0xDC:
          // SPRGTX R,X   DC RX
          reg1 = (op2 & 0xf0) >> 4;//num
          reg2 = op2 & 0xf;//value
          reg[reg1] = getSpriteValue(reg[reg1] & 31, reg[reg2]);
          break;
        case 0xDE:
          // AGBSPR R,R     DE RR
          reg1 = (op2 & 0xf0) >> 4;//n1
          reg2 = op2 & 0xf;//n2
          reg[reg1] = angleBetweenSprites(reg[reg1], reg[reg2]);
          break;
      }
      break;
    case 0xE:
      // DRSPRT R,R,R ERRR
      reg1 = (op1 & 0xf);//номер спрайта
      reg2 = (op2 & 0xf0) >> 4;//x
      reg3 = op2 & 0xf;//y
      setSprPosition(reg[reg1] & 0x1f, reg[reg2], reg[reg3]);
      if(getSpriteValue(reg[reg1] & 0x1f, 7) < 1)
        setSpriteValue(reg[reg1] & 0x1f, 7, 1);
      break;
    case 0xF:
      // SSPRTV R,R,R FR RR
      reg1 = (op1 & 0xf);//номер спрайта
      reg2 = (op2 & 0xf0) >> 4;//type
      reg3 = op2 & 0xf;//value
      setSpriteValue(reg[reg1] & 0x1f, reg[reg2], reg[reg3]); 
      break;
  }
}

display.ino

Arduino
Code accompanies ESP_ILI9341_game_engine.ino.
#include "font_a.c"

#define SCREEN_WIDTH 128
#define SCREEN_WIDTH_BYTES 64
#define SCREEN_REAL_WIDTH 320
#define SCREEN_HEIGHT 128
#define SCREEN_REAL_HEIGHT 240
#define SCREEN_SIZE (SCREEN_HEIGHT * SCREEN_WIDTH_BYTES)
// #define ONE_DIM_SCREEN_ARRAY

#ifndef ONE_DIM_SCREEN_ARRAY
#define SCREEN_ARRAY_DEF SCREEN_HEIGHT][SCREEN_WIDTH_BYTES
#define SCREEN_ADDR(x, y) y][x
#else
#define SCREEN_ARRAY_DEF SCREEN_SIZE
#define SCREEN_ADDR(x, y) ((int(y) << 6) + int(x))
#endif

#define PARTICLE_COUNT 32

struct sprite {
  uint16_t address;
  int16_t x;
  int16_t y;
  uint8_t width;
  uint8_t height;
  int8_t speedx;
  int8_t speedy;
  int16_t angle;
  int8_t lives;
  int8_t collision;
  uint8_t flags; //0 0 0 0 0 0 scrolled solid
  int8_t gravity;
  uint16_t oncollision;
  uint16_t onexitscreen;
};

struct Particle {
  int16_t time;
  int16_t x;
  int16_t y;
  int8_t gravity;
  int8_t speedx;
  int8_t speedy;
  int8_t color;
};

struct Emitter { 
  int16_t time;
  int16_t timer;
  int16_t timeparticle;
  uint8_t count;
  int8_t gravity;
  int16_t x;
  int16_t y;
  int8_t speedx;
  int8_t speedy;
  int8_t speedx1;
  int8_t speedy1;
  int8_t color;
};

struct Tile { 
  int16_t adr;
  uint8_t imgwidth;
  uint8_t imgheight;
  uint8_t width;
  uint8_t height;
  int16_t x;
  int16_t y;
};

static const int8_t cosT[] PROGMEM = {
  0x40, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 
  0x3c, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x38, 0x37, 0x37, 0x36, 0x36, 0x35, 0x35, 0x34, 0x33, 0x33, 0x32, 0x31, 
  0x31, 0x30, 0x2f, 0x2e, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 
  0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0xf, 0xe, 0xd, 0xc, 0xb, 
  0xa, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf5, 0xf4, 0xf3, 0xf2, 
  0xf1, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xe0, 0xdf, 0xde, 
  0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, 0xd7, 0xd6, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd1, 0xd0, 0xcf, 0xce, 0xce, 0xcd, 
  0xcc, 0xcc, 0xcb, 0xca, 0xca, 0xc9, 0xc9, 0xc8, 0xc8, 0xc7, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 
  0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 
  0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 
  0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcc, 0xcc, 0xcd, 0xce, 0xce, 0xcf, 0xd0, 
  0xd1, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xdf, 0xe0, 0xe1, 
  0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf7, 
  0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x1f, 0x20, 0x21, 0x22, 0x23, 
  0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33, 
  0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 
  0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f
};
static const int8_t sinT[] PROGMEM = {
  0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
  0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x29, 0x2a, 
  0x2b, 0x2c, 0x2d, 0x2e, 0x2e, 0x2f, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, 
  0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 
  0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 
  0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x38, 0x37, 0x37, 0x36, 0x36, 
  0x35, 0x35, 0x34, 0x33, 0x33, 0x32, 0x31, 0x31, 0x30, 0x2f, 0x2e, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x29, 0x28, 0x27, 
  0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x16, 0x15, 0x14, 0x13, 
  0x12, 0x11, 0x10, 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 
  0xf9, 0xf8, 0xf7, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, 0xe6, 0xe5, 0xe4, 
  0xe3, 0xe2, 0xe1, 0xe0, 0xe0, 0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, 0xd7, 0xd6, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 
  0xd1, 0xd1, 0xd0, 0xcf, 0xce, 0xce, 0xcd, 0xcc, 0xcc, 0xcb, 0xca, 0xca, 0xc9, 0xc9, 0xc8, 0xc8, 0xc7, 0xc6, 0xc6, 0xc5, 
  0xc5, 0xc5, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 
  0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc2, 
  0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 
  0xcc, 0xcc, 0xcd, 0xce, 0xce, 0xcf, 0xd0, 0xd1, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 
  0xdc, 0xdd, 0xde, 0xdf, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 
  0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe
};

uint8_t screen[SCREEN_ARRAY_DEF] __attribute__ ((aligned));
uint8_t sprite_screen[SCREEN_ARRAY_DEF] __attribute__ ((aligned));
uint8_t line_is_draw[128] __attribute__ ((aligned));
char charArray[340];
uint16_t pix_buffer[256] __attribute__ ((aligned));
uint16_t rscreenWidth;
uint16_t rscreenHeight;
uint16_t displayXOffset = 32;
struct sprite sprite_table[32];
struct Particle particles[PARTICLE_COUNT];
struct Emitter emitter;
struct Tile tile;
int8_t imageSize = 1;
int8_t regx = 0;
int8_t regy = 0;

#pragma GCC optimize ("-O2")
#pragma GCC push_options

int16_t getCos(int16_t g){
  if(g >= 360)
    g = g % 360;
  return (int16_t)(int8_t)pgm_read_byte_near(cosT + g);
}

int16_t getSin(int16_t g){
  if(g >= 360)
    g = g % 360;
  return (int16_t)(int8_t)pgm_read_byte_near(sinT + g);
}

#define MULTIPLY_FP_RESOLUTION_BITS  6

int16_t atan2_fp(int16_t y_fp, int16_t x_fp){
  int32_t coeff_1 = 45;
  int32_t coeff_1b = -56; // 56.24;
  int32_t coeff_1c = 11;  // 11.25
  int16_t coeff_2 = 135;
  int16_t angle = 0;
  int32_t r;
  int32_t r3;
  int16_t y_abs_fp = y_fp;
  if (y_abs_fp < 0)
    y_abs_fp = -y_abs_fp;
  if (y_fp == 0){
    if (x_fp >= 0){
      angle = 0;
    }
    else{
      angle = 180;
    }
  }
  else if (x_fp >= 0){
    r = (((int32_t)(x_fp - y_abs_fp)) << MULTIPLY_FP_RESOLUTION_BITS) / ((int32_t)(x_fp + y_abs_fp));
    r3 = r * r;
    r3 =  r3 >> MULTIPLY_FP_RESOLUTION_BITS;
    r3 *= r;
    r3 =  r3 >> MULTIPLY_FP_RESOLUTION_BITS;
    r3 *= coeff_1c;
    angle = (int16_t) (coeff_1 + ((coeff_1b * r + r3) >> MULTIPLY_FP_RESOLUTION_BITS));
  }
  else{
    r = (((int32_t)(x_fp + y_abs_fp)) << MULTIPLY_FP_RESOLUTION_BITS) / ((int32_t)(y_abs_fp - x_fp));
    r3 = r * r;
    r3 =  r3 >> MULTIPLY_FP_RESOLUTION_BITS;
    r3 *= r;
    r3 =  r3 >> MULTIPLY_FP_RESOLUTION_BITS;
    r3 *= coeff_1c;
    angle = coeff_2 + ((int16_t)  (((coeff_1b * r + r3) >> MULTIPLY_FP_RESOLUTION_BITS)));
  }
  if (y_fp < 0)
    return (-angle);     // negate if in quad III or IV
  else
    return (angle);
}

void display_init(){
  for(byte i = 0; i < 32; i++){
    sprite_table[i].address = 0;
    sprite_table[i].x = -255;
    sprite_table[i].y = -255;
    sprite_table[i].width = 8;
    sprite_table[i].height = 8;
    sprite_table[i].speedx = 0;
    sprite_table[i].speedy = 0;
    sprite_table[i].angle = 0;
    sprite_table[i].lives = 0;
    sprite_table[i].collision = -1;
    sprite_table[i].flags = 2; //scrolled = 1 solid = 0
    sprite_table[i].gravity = 0;
    sprite_table[i].oncollision = 0;
  }
  emitter.time = 0;
  emitter.timer = 0;
  tile.adr = 0;
  for(byte i = 0; i < PARTICLE_COUNT; i++)
    particles[i].time = 0;
  for(uint16_t i = 0; i < 340; i++)
      charArray[i] = 0;
  imageSize = 1;
  regx = 0;
  regy = 0;
}

int8_t randomD(int8_t a, int8_t b) {
  int8_t minv = a < b ? a : b;
  int8_t maxv = a > b ? a : b;
  return random(minv, maxv + 1);
}

void setParticle(int8_t gravity, uint8_t count, uint16_t time){
  emitter.gravity = gravity;
  emitter.count = count;
  emitter.timeparticle = time;
}

void setEmitter(uint16_t time, int16_t dir, int16_t dir1, int16_t speed){
  emitter.time = time;
  emitter.speedx = (int8_t)((speed * getCos(dir)) >> 6);
  emitter.speedy = (int8_t)((speed * getSin(dir)) >> 6);
  emitter.speedx1 = (int8_t)((speed * getCos(dir1)) >> 6);
  emitter.speedy1 = (int8_t)((speed * getSin(dir1)) >> 6);
}

void drawParticle(int16_t x, int16_t y, uint8_t color){
  emitter.x = x;
  emitter.y = y;
  emitter.color = color;
  emitter.timer = emitter.time;
}

void setScreenResolution(uint16_t nw, uint16_t nh){
  if(nw < SCREEN_REAL_WIDTH)
    rscreenWidth = nw;
  else
    rscreenWidth = SCREEN_REAL_WIDTH - 1;
  if(nh < SCREEN_REAL_HEIGHT)
    rscreenHeight = nh;
  else
    rscreenHeight = SCREEN_REAL_HEIGHT - 1;
  if(rscreenWidth < 95)
    rscreenWidth = 95;
  if(rscreenHeight < 95)
    rscreenHeight = 95;
  displayXOffset = (SCREEN_REAL_WIDTH - rscreenWidth) / 2;
  for(uint8_t i = 0; i < 128; i++)
      line_is_draw[i] = 3;
  tft.fillScreen(0x0000);
}

uint16_t getDisplayXOffset(){
  return displayXOffset;
}

void redrawScreen(){
    int x_ratio = ( int ) ((127 << 16) / rscreenWidth);
    int y_ratio = ( int ) ((127 << 16) / rscreenHeight);
    uint16_t x2, hx2, y2, startx, endx, startj;
    int16_t prevy2 = -1;
    for(int16_t i = 0; i < rscreenHeight; i++) {
      y2 = ((i * y_ratio) >> 16);
      if(line_is_draw[y2]){
        if(line_is_draw[y2] == 2){
          startx = displayXOffset + rscreenWidth / 2 - 2;
          startj = rscreenWidth / 2 - 2;
        }
        else{
          startx = displayXOffset;
          startj = 0;
        }
        if(line_is_draw[y2] == 1)
          endx = displayXOffset + rscreenWidth / 2 + 2;
        else
          endx = displayXOffset + rscreenWidth;
        tft.setAddrWindow(startx, i, endx, i  + 1);
        if(prevy2 != y2)
          for ( int j = startj; j < rscreenWidth; j++) {
            x2 = ((j * x_ratio) >> 16);
            hx2 = x2 / 2;
            if(x2 & 1){
              if((sprite_screen[SCREEN_ADDR(hx2,y2)] & 0xf) > 0)
                pix_buffer[j - startj] = palette[(sprite_screen[SCREEN_ADDR(hx2,y2)] & 0xf)];
              else
                pix_buffer[j - startj] = palette[(screen[SCREEN_ADDR(hx2,y2)] & 0xf)];
            }
            else{
              if((sprite_screen[SCREEN_ADDR(hx2,y2)] & 0xf0) > 0)
                pix_buffer[j - startj] = palette[(sprite_screen[SCREEN_ADDR(hx2,y2)] & 0xf0) >> 4];
              else
                pix_buffer[j - startj] = palette[(screen[SCREEN_ADDR(hx2,y2)] & 0xf0) >> 4];
            }
        }
        prevy2 = y2;
        tft.pushColors(pix_buffer, endx - startx);
      }              
    }
    for(uint8_t i = 0; i < 128; i++)
      line_is_draw[i] = 0;
}

void redrawParticles(){
  int16_t i, n;
  uint8_t x, y;
  if(emitter.timer > 0){
      emitter.timer -= 50;
      i = emitter.count;
      for(n = 0; n < PARTICLE_COUNT; n++){
        if(i == 0)
          break;
        if(particles[n].time <= 0){
          i--;
          particles[n].time = emitter.timeparticle;
          particles[n].x = emitter.x;
          particles[n].y = emitter.y;
          particles[n].color = emitter.color;
          particles[n].speedx = randomD(emitter.speedx, emitter.speedx1);
          particles[n].speedy = randomD(emitter.speedy, emitter.speedy1);
          particles[n].gravity = emitter.gravity;
        }
      }
    }
    for(n = 0; n < PARTICLE_COUNT; n++)
      if(particles[n].time > 0){
        x = (particles[n].x & 127) / 2;
        y = particles[n].y & 127;
        if(particles[n].x & 1)
          sprite_screen[SCREEN_ADDR(x,y)] = (sprite_screen[SCREEN_ADDR(x,y)] & 0xf0) + (particles[n].color & 0x0f);
        else
          sprite_screen[SCREEN_ADDR(x,y)] = (sprite_screen[SCREEN_ADDR(x,y)] & 0x0f) + ((particles[n].color & 0x0f) << 4);
        line_is_draw[y] |= 1 + x / 32;
        particles[n].time -= 50;
        if(random(0,2)){
          particles[n].x += particles[n].speedx;
          particles[n].speedy += particles[n].gravity;
          particles[n].y += particles[n].speedy;
        }
        else{
          particles[n].x += particles[n].speedx / 2;
          particles[n].y += particles[n].speedy / 2;
        }
        if(particles[n].x < 0 || particles[n].x > 128 || particles[n].y < 0 || particles[n].y > 128)
            particles[n].time = 0;
      }
}

void redrawSprites(){
  for(byte i = 0; i < 32; i++){
    if(sprite_table[i].lives > 0){   
      sprite_table[i].speedy += sprite_table[i].gravity;
      sprite_table[i].x += sprite_table[i].speedx;
      sprite_table[i].y += sprite_table[i].speedy;
      if(sprite_table[i].x + sprite_table[i].width < 0 || sprite_table[i].x > 127 || sprite_table[i].y + sprite_table[i].height < 0 || sprite_table[i].y > 127){
        if(sprite_table[i].onexitscreen > 0)
           setinterrupt(sprite_table[i].onexitscreen, i);
      }
      else
        drawSpr(i, sprite_table[i].x, sprite_table[i].y);
    }
  }
}

uint16_t getTail(int16_t x, int16_t y){
  if(x < 0 || x >= tile.width || y < 0 || y >= tile.height)
    return 0;
  return readInt(tile.adr + (x + y * tile.width) * 2);
}

void testSpriteCollision(){
  byte n, i;
  int16_t x0, y0, newspeed;
  for(n = 0; n < 32; n++)
    sprite_table[n].collision = -1;
  for(n = 0; n < 32; n++){
    if(sprite_table[n].lives > 0){
      for(i = 0; i < n; i++){
        if(sprite_table[i].lives > 0){
          if(sprite_table[n].x < sprite_table[i].x + sprite_table[i].width && 
          sprite_table[n].x + sprite_table[n].width > sprite_table[i].x &&
          sprite_table[n].y < sprite_table[i].y + sprite_table[i].height && 
          sprite_table[n].y + sprite_table[n].height > sprite_table[i].y){
            sprite_table[n].collision = i;
            sprite_table[i].collision = n;
            if(sprite_table[n].oncollision > 0)
              setinterrupt(sprite_table[n].oncollision, n);
            if(sprite_table[i].oncollision > 0)
              setinterrupt(sprite_table[i].oncollision, i);
            if((sprite_table[n].flags & 1) != 0 && (sprite_table[n].flags & 1) != 0){
              if((sprite_table[n].speedx >= 0 && sprite_table[i].speedx <= 0) || (sprite_table[n].speedx <= 0 && sprite_table[i].speedx >= 0)){
                newspeed = (abs(sprite_table[n].speedx) + abs(sprite_table[i].speedx)) / 2;
                if(sprite_table[n].x > sprite_table[i].x){
                  sprite_table[n].speedx = newspeed;
                  sprite_table[i].speedx = -newspeed;
                }
                else{
                  sprite_table[n].speedx = -newspeed;
                  sprite_table[i].speedx = newspeed;
                }
                sprite_table[n].x -= 2;
              }
              if((sprite_table[n].speedy >= 0 && sprite_table[i].speedy <= 0) || (sprite_table[n].speedy <= 0 && sprite_table[i].speedy >= 0)){
                newspeed = (abs(sprite_table[n].speedy) + abs(sprite_table[i].speedy)) / 2;
                if(sprite_table[n].y > sprite_table[i].y){
                  sprite_table[n].speedy = newspeed;
                  sprite_table[i].speedy = -newspeed;
                }
                else{
                  sprite_table[n].speedy = -newspeed;
                  sprite_table[i].speedy = newspeed;
                }
                sprite_table[n].y -=  2;
              }
            }
          }
        }
      }
      if((sprite_table[n].flags & 2) != 0 && tile.adr > 0){
          x0 = ((sprite_table[n].x + sprite_table[n].width / 2 - tile.x) / (int16_t)tile.imgwidth);
          y0 = ((sprite_table[n].y + sprite_table[n].height / 2 - tile.y + tile.imgheight) / (int16_t)tile.imgheight) - 1;
          if(x0 >= -1 && x0 <= tile.width && y0 >= -1 && y0 <= tile.height){
            if(getTail(x0, y0) != 0){
              if(sprite_table[n].speedx != 0){
                if(sprite_table[n].speedx > 0){
                  sprite_table[n].x = tile.x + x0 * tile.imgwidth - sprite_table[n].width ;
                  sprite_table[n].speedx /= 2;
                }
                else{
                  sprite_table[n].x = tile.x + (x0 + 1) * tile.imgwidth;
                  sprite_table[n].speedx /= 2;
                }
              }
              if(sprite_table[n].speedy != 0){
                if(sprite_table[n].speedy > 0){
                  sprite_table[n].y = tile.y + y0 * tile.imgheight - sprite_table[n].height ;
                  sprite_table[n].speedy /= 2;
                }
                else{
                  sprite_table[n].y = tile.y + (y0 + 1) * tile.imgheight;
                  sprite_table[n].speedy /= 2;
                }
              }
            }
            else{
              if(sprite_table[n].speedy > 0 && getTail(x0, y0 + 1) != 0){
                if((tile.y + (y0 + 1) * tile.imgheight) - (sprite_table[n].y  + sprite_table[n].height) < sprite_table[n].speedy * 2){
                  sprite_table[n].y = tile.y + (y0 + 1) * tile.imgheight - sprite_table[n].height;  
                  sprite_table[n].speedy = 0;
                }
              }
              else if(sprite_table[n].speedy < 0 && getTail(x0, y0 - 1) != 0){
                if(sprite_table[n].y - (tile.y + y0 * tile.imgheight) < sprite_table[n].speedy * 2){
                  sprite_table[n].y = tile.y + y0 * tile.imgheight;  
                  sprite_table[n].speedy = 0;
                }
              }
              if(sprite_table[n].speedx > 0  && getTail(x0 + 1, y0) != 0){
                if((tile.x + (x0 + 1) * tile.imgwidth - sprite_table[n].width) - sprite_table[n].x < sprite_table[n].speedx * 2){
                  sprite_table[n].x = tile.x + (x0 + 1) * tile.imgwidth - sprite_table[n].width;  
                  sprite_table[n].speedx = 0;
                }
              }
              else if(sprite_table[n].speedx < 0 && getTail(x0 - 1, y0) != 0){
                if(sprite_table[n].x - (tile.x + x0 * tile.imgwidth) < sprite_table[n].speedx * 2){
                  sprite_table[n].x = tile.x + x0 * tile.imgwidth; 
                  sprite_table[n].speedx = 0;
                }
              } 
            } 
          }
        }
        
    }
  }
}

void clearSpriteScr(){
  for(byte y = 0; y < 128; y ++)
    for(byte x = 0; x < 64; x += 4){
      if(*((uint32_t*)&sprite_screen[SCREEN_ADDR(x,y)]) > 0)
        line_is_draw[y] |= 1 + x / 32;
    }
  memset(sprite_screen, 0, SCREEN_SIZE);
}

void clearScr(uint8_t color){
  for(byte y = 0; y < 128; y ++){
    for(byte x = 0; x < 128; x++)
      setPix(x, y, color);
  }
}

void setImageSize(uint8_t size){
  imageSize = size;
}

void setSpr(uint16_t n, uint16_t adr){
  sprite_table[n].address = adr;
}

void setSprPosition(int8_t n, uint16_t x, uint16_t y){
  sprite_table[n].x = x;
  sprite_table[n].y = y;
}

void spriteSetDirectionAndSpeed(int8_t n, uint16_t speed, int16_t dir){
  sprite_table[n].speedx = ((speed * getCos(dir)) >> 6);
  sprite_table[n].speedy = ((speed * getSin(dir)) >> 6);
}

void setSprWidth(int8_t n, uint8_t w){
  sprite_table[n].width = w;
}

void setSprHeight(int8_t n, uint8_t w){
  sprite_table[n].height = w;
}

void setSprSpeedx(int8_t n, int8_t s){
  sprite_table[n].speedx = s;
}

void setSprSpeedy(int8_t n, int8_t s){
  sprite_table[n].speedy = s;
}

int16_t angleBetweenSprites(int8_t n1, int8_t n2){
  int16_t A = atan2_fp(sprite_table[n1].y - sprite_table[n2].y, sprite_table[n1].x - sprite_table[n2].x);
  A = (A < 0) ? A + 360 : A;
  return A;
}

int16_t getSpriteValue(int8_t n, uint8_t t){
  switch(t){
    case 0:
      return sprite_table[n].x;
    case 1:
      return sprite_table[n].y;
    case 2:
      return sprite_table[n].speedx;
    case 3:
      return sprite_table[n].speedy;
    case 4:
      return sprite_table[n].width;
    case 5:
      return sprite_table[n].height;
    case 6:
      return sprite_table[n].angle;
    case 7:
      return sprite_table[n].lives;
    case 8:
      return sprite_table[n].collision;
    case 9:
      return sprite_table[n].flags & 2;
    case 10:
      return sprite_table[n].gravity;
  }
  return 0;
}

void setSpriteValue(int8_t n, uint8_t t, int16_t v){
 switch(t){
    case 0:
      sprite_table[n].x = v;
      return;
    case 1:
      sprite_table[n].y = v;
      return;
    case 2:
      sprite_table[n].speedx = (int8_t) v;
      return;
    case 3:
      sprite_table[n].speedy = (int8_t) v;
      return;
    case 4:
      sprite_table[n].width = v;
      return;
    case 5:
      sprite_table[n].height = v;
      return;
    case 6:
      sprite_table[n].angle = (v % 360) & 0x01ff;
      return;
    case 7:
      sprite_table[n].lives = v;
      return;
    case 8:
      return;
    case 9:
      if(v != 0)
        sprite_table[n].flags |= 0x01;
      else
        sprite_table[n].flags &= ~0x01;
      return;
    case 10:
      sprite_table[n].gravity = v;
      return;
    case 11:
      sprite_table[n].oncollision = (uint16_t)v;
      return;
    case 12:
      sprite_table[n].onexitscreen = (uint16_t)v;
      return;
    case 13:
      if(v != 0)
        sprite_table[n].flags |= 0x02;
      else
        sprite_table[n].flags &= ~0x02;
      return;
 }
}

void drawRotateSprPixel(int8_t pixel, int8_t x0, int8_t y0, int16_t x, int16_t y, int16_t hw, int16_t hh, int16_t c, int16_t s){
  //int16_t nx = ((x * c - y * s) >> 6);
  //int16_t ny = ((y * c  + x * s) >> 6);
  int16_t nx = hw + (((x - hw) * c - (y - hh) * s) >> 6);
  int16_t ny = hh + (((y - hh) * c + (x - hw) * s) >> 6);
  int16_t nnx = nx / 2;
  int8_t nnx0 = x0 / 2;
  if(nnx0 + nnx >= 0 && nnx0 + nnx < 64 && y0 + ny >= 0 && y0 + ny < 128){
    if((x0 + nx) & 1)
      sprite_screen[SCREEN_ADDR(nnx0 + nnx, y0 + ny)] = (sprite_screen[SCREEN_ADDR(nnx0 + nnx, y0 + ny)] & 0x0f) + (pixel << 4);
    else
      sprite_screen[SCREEN_ADDR(nnx0 + nnx, y0 + ny)] = (sprite_screen[SCREEN_ADDR(nnx0 + nnx, y0 + ny)] & 0xf0) + pixel;
    line_is_draw[y0 + ny] |= 1 + (nnx0 + nnx) / 32;
  }
}

inline void drawSprPixel(int8_t pixel, int8_t x0, int8_t y0, int16_t x, int16_t y){
  if(x0 + x >= 0 && x0 + x < 128 && y0 + y >= 0 && y0 + y < 128){
    if((x0 + x) & 1)
      sprite_screen[SCREEN_ADDR((x0 + x) / 2, y0 + y)] = (sprite_screen[SCREEN_ADDR((x0 + x) / 2, y0 + y)] & 0xf0) + pixel;
    else
      sprite_screen[SCREEN_ADDR((x0 + x) / 2, y0 + y)] = (sprite_screen[SCREEN_ADDR((x0 + x) / 2, y0 + y)] & 0x0f) + (pixel << 4);
    line_is_draw[y0 + y] |= 1 + (x0 + x) / 64;
  }
}

void drawSpr(int8_t n, uint16_t x, uint16_t y){
  uint16_t adr = sprite_table[n].address;
  uint8_t w = sprite_table[n].width;
  uint8_t h = sprite_table[n].height;
  int16_t c = getCos(sprite_table[n].angle);
  int16_t s = getSin(sprite_table[n].angle);
  uint8_t pixel;
  w = w / 2;
  if(sprite_table[n].angle == 0){
    for(byte y1 = 0; y1 < h; y1 ++)
      if(y1 + y >= -h && y1 + y < 128 + h){
        for(byte x1 = 0; x1 < w; x1++){
          pixel = readMem(adr + x1 + y1 * w);
          if((pixel & 0xf0) > 0)
            drawSprPixel(pixel >> 4, x, y, x1 * 2, y1);
          if((pixel & 0x0f) > 0)
            drawSprPixel(pixel & 0xf, x, y, x1 * 2 + 1, y1);
        }
      }
  }
  else{
    for(byte y1 = 0; y1 < h; y1 ++)
      if(y1 + y >= -h && y1 + y < 128 + h){
        for(byte x1 = 0; x1 < w; x1++)
          if(x1 + x >= -w && x1 + x < 128 + w){
            pixel = readMem(adr + x1 + y1 * w);
            if((pixel & 0xf0) > 0)
              drawRotateSprPixel(pixel >> 4, x, y, x1 * 2, y1, w, h / 2, c, s);
            if((pixel & 0x0f) > 0)
              drawRotateSprPixel(pixel & 0xf, x, y, x1 * 2 + 1, y1, w, h / 2, c, s);
          }   
      }
  }
}

void drawImg(int16_t a, int16_t x, int16_t y, int16_t w, int16_t h){
  if(imageSize > 1){
    drawImgS(a, x, y, w, h);
    return;
  }
  uint8_t p, color;
  for(int16_t yi = 0; yi < h; yi++)
    for(int16_t xi = 0; xi < w; xi++){
      p = readMem(a);
      color = (p & 0xf0) >> 4;
      if(color > 0){
        setPix(xi + x, yi + y, color);
      }
      xi++;
      color = p & 0x0f;
      if(color > 0){
        setPix(xi + x, yi + y, color);
      }
      a++;
    }
}

void drawImgRLE(int16_t adr, int16_t x1, int16_t y1, int16_t w, int16_t h){
    if(imageSize > 1){
      drawImgRLES(adr, x1, y1, w, h);
      return;
    }
    int16_t i = 0;
    byte repeat = readMem(adr);
    adr++;
    int8_t color1 = (readMem(adr) & 0xf0) >> 4;
    int8_t color2 = readMem(adr) & 0xf;
    while(i < w * h){
      if(repeat > 0x81){
        if(color1 > 0){
          setPix(x1 + i % w, y1 + i / w, color1);
        }
        if(color2 > 0){
          setPix(x1 + i % w + 1, y1 + i / w, color2);
        }
        i += 2;
        adr++;
        repeat--;
        color1 = (readMem(adr) & 0xf0) >> 4;
        color2 = readMem(adr) & 0xf;
      }
      else if(repeat == 0x81){
        repeat = readMem(adr);
        adr++;
        color1 = (readMem(adr) & 0xf0) >> 4;
        color2 = readMem(adr) & 0xf;
      }
      else if(repeat > 0){
        if(color1 > 0){
          setPix(x1 + i % w, y1 + i / w, color1);
        }
        if(color2 > 0){
          setPix(x1 + i % w + 1, y1 + i / w, color2);
        }
        i += 2;
        repeat--;
      }
      else if(repeat == 0){
        adr++;
        repeat = readMem(adr);
        adr++;
        color1 = (readMem(adr) & 0xf0) >> 4;
        color2 = readMem(adr) & 0xf;
      }
    }
  }

void drawImageBit(int16_t adr, int16_t x1, int16_t y1, int16_t w, int16_t h){
  if(imageSize > 1){
    drawImageBitS(adr, x1, y1, w, h);
    return;
  }
  int16_t i = 0;
  uint8_t ibit;
  for(int16_t y = 0; y < h; y++)
    for(int16_t x = 0; x < w; x++){
      if(i % 8 == 0){
        ibit = readMem(adr);
        adr++;
      }
      if(ibit & 0x80)
        setPix(x1 + x, y1 + y, color);
      else
        setPix(x1 + x, y1 + y, bgcolor);
      ibit = ibit << 1;
      i++;
    }
}

void drawImgS(int16_t a, int16_t x, int16_t y, int16_t w, int16_t h){
  uint8_t p, jx, jy, color, s;
  s = imageSize;
  for(int16_t yi = 0; yi < h; yi++)
    for(int16_t xi = 0; xi < w; xi++){
      p = readMem(a);
      color = (p & 0xf0) >> 4;
      if(color > 0){
        for(jx = 0; jx < s; jx++)
              for(jy = 0; jy < s; jy++)
                setPix(xi * s + x + jx, yi * s + y + jy, color);
      }
      xi++;
      color = p & 0x0f;
      if(color > 0){
        for(jx = 0; jx < s; jx++)
              for(jy = 0; jy < s; jy++)
                setPix(xi * s + x + jx, yi * s + y + jy, color);
      }
      a++;
    }
}

void drawImgRLES(int16_t adr, int16_t x1, int16_t y1, int16_t w, int16_t h){
    int16_t i = 0;
    uint8_t jx, jy;
    byte repeat = readMem(adr);
    adr++;
    int8_t color1 = (readMem(adr) & 0xf0) >> 4;
    int8_t color2 = readMem(adr) & 0xf;
    while(i < w * h){
      if(repeat > 0x81){
        if(color1 > 0){
          for(jx = 0; jx < imageSize; jx++)
            for(jy = 0; jy < imageSize; jy++)
              setPix(x1 + (i % w) * imageSize + jx, y1 + i / w * imageSize + jy, color1);
        }
        if(color2 > 0){
          for(jx = 0; jx < imageSize; jx++)
            for(jy = 0; jy < imageSize; jy++)
              setPix(x1 + (i % w) * imageSize + imageSize + jx, y1 + i / w * imageSize + jy, color2);
        }
        i += 2;
        adr++;
        repeat--;
        color1 = (readMem(adr) & 0xf0) >> 4;
        color2 = readMem(adr) & 0xf;
      }
      else if(repeat == 0x81){
        repeat = readMem(adr);
        adr++;
        color1 = (readMem(adr) & 0xf0) >> 4;
        color2 = readMem(adr) & 0xf;
      }
      else if(repeat > 0){
        if(color1 > 0){
          for(jx = 0; jx < imageSize; jx++)
                for(jy = 0; jy < imageSize; jy++)
                  setPix(x1 + (i % w) * imageSize + jx, y1 + i / w * imageSize + jy, color1);
        }
        if(color2 > 0){
          for(jx = 0; jx < imageSize; jx++)
                for(jy = 0; jy < imageSize; jy++)
                  setPix(x1 + (i % w) * imageSize + imageSize + jx, y1 + i / w * imageSize + jy, color2);
        }
        i += 2;
        repeat--;
      }
      else if(repeat == 0){
        adr++;
        repeat = readMem(adr);
        adr++;
        color1 = (readMem(adr) & 0xf0) >> 4;
        color2 = readMem(adr) & 0xf;
      }
    }
  }

void drawImageBitS(int16_t adr, int16_t x1, int16_t y1, int16_t w, int16_t h){
  int16_t i = 0;
  uint8_t ibit, jx, jy;
  for(int16_t y = 0; y < h; y++)
    for(int16_t x = 0; x < w; x++){
      if(i % 8 == 0){
        ibit = readMem(adr);
        adr++;
      }
      if(ibit & 0x80){
        for(jx = 0; jx < imageSize; jx++)
          for(jy = 0; jy < imageSize; jy++)
          setPix(x1 + x * imageSize + jx, y1 + y * imageSize + jy, color);
      } 
      else{
        for(jx = 0; jx < imageSize; jx++)
          for(jy = 0; jy < imageSize; jy++)
            setPix(x1 + x * imageSize + jx, y1 + y * imageSize + jy, bgcolor);
      } 
      ibit = ibit << 1;
      i++;
    }
}

void loadTile(int16_t adr, uint8_t iwidth, uint8_t iheight, uint8_t width, uint8_t height){
    tile.adr = adr;
    tile.imgwidth = iwidth;
    tile.imgheight = iheight;
    tile.width = width;
    tile.height = height;
  }

void drawTile(int16_t x0, int16_t y0){
    int16_t x, y, nx, ny;
    uint16_t imgadr;
    tile.x = x0;
    tile.y = y0;
    for(x = 0; x < tile.width; x++){
      nx = x0 + x * tile.imgwidth;
      for(y = 0; y < tile.height; y++){
        ny = y0 + y * tile.imgheight;
        if(nx >= -tile.width && nx < 128 && ny >= -tile.height && ny < 128){
          imgadr = readInt(tile.adr + (x + y * tile.width) * 2);
          if(imgadr > 0)
            drawImg(imgadr, nx, ny, tile.imgwidth, tile.imgheight); 
        }
      }
    }
  }

void drawFVLine(int16_t x, int16_t y1, int16_t y2){
  for(int16_t  i = y1; i <= y2; i++)
    setPix(x, i, color);
}

void drawFHLine(int16_t x1, int16_t x2, int16_t y){
  for(int16_t  i = x1; i <= x2; i++)
    setPix(i, y, color);
}

void drwLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2) {
    if(x1 == x2){
      if(y1 > y2)
        drawFVLine(x1, y2, y1);
      else
        drawFVLine(x1, y1, y2);
      return;
    }
    else if(y1 == y2){
      if(x1 > x2)
        drawFHLine(x2, x1, y1);
      else
        drawFHLine(x1, x2, y1);
      return;
    }
    int16_t deltaX = abs(x2 - x1);
    int16_t deltaY = abs(y2 - y1);
    int16_t signX = x1 < x2 ? 1 : -1;
    int16_t signY = y1 < y2 ? 1 : -1;
    int16_t error = deltaX - deltaY;
    int16_t error2;
    setPix(x2, y2, color);
    while(x1 != x2 || y1 != y2){
      setPix(x1, y1, color);
      error2 = error * 2;
      if(error2 > -deltaY){
        error -= deltaY;
        x1 += signX;
      }
      if(error2 < deltaX){
        error += deltaX;
        y1 += signY;
      }
    }
  }

inline void setPix(uint16_t x, uint16_t y, uint8_t c){
  uint8_t xi = x / 2;
  uint8_t b;
  if(x < 128 && y < 128){
    b = screen[SCREEN_ADDR(xi, y)];
    if(x & 1)
      screen[SCREEN_ADDR(xi, y)] = (screen[SCREEN_ADDR(xi, y)] & 0xf0) + c;
    else
      screen[SCREEN_ADDR(xi, y)] = (screen[SCREEN_ADDR(xi, y)] & 0x0f) + ( c << 4);
    if(b != screen[SCREEN_ADDR(xi, y)])
      line_is_draw[y] |= 1 + x / 64;
  }
}

inline void setPix2(uint16_t xi, uint16_t y, uint8_t c){
  if(xi < 64 && y < 128){
    if (screen[SCREEN_ADDR(xi, y)] != c) {
      screen[SCREEN_ADDR(xi, y)] = c;
      line_is_draw[y] |= 1 + xi /32;
    }
  }
}

byte getPix(byte x, byte y){
  byte b = 0;
  byte xi = x / 2;
  if(x >= 0 && x < 128 && y >= 0 && y < 128){
    if(x % 2 == 0)
      b = (screen[SCREEN_ADDR(xi, y)] & 0xf0) >> 4;
    else
      b = (screen[SCREEN_ADDR(xi, y)] & 0x0f);
  }
  return b;
}

void changePalette(uint8_t n, uint16_t c){
  palette[n] = c;
}

void scrollScreen(uint8_t step, uint8_t direction){
    uint8_t bufPixel;
    if(direction == 2){
      for(uint8_t y = 0; y < 128; y++){
        bufPixel = screen[SCREEN_ADDR(0, y)];
        for(uint8_t x = 1; x < 64; x++){
          if(screen[SCREEN_ADDR(x - 1, y)] != screen[SCREEN_ADDR(x,y)])
            line_is_draw[y] |= 1 + x / 32;
          screen[SCREEN_ADDR(x - 1,  y)] = screen[SCREEN_ADDR(x,y)];
...

This file has been truncated, please download it to see its full contents.

file_manager.ino

Arduino
Code accompanies ESP_ILI9341_game_engine.ino.
void fileList(String path) {
  Dir dir = SPIFFS.openDir(path);
  char s[32];
  char thisF[32];
  int16_t lst = 1;
  int16_t pos = 0;
  int16_t startpos = 0;
  int16_t fileCount = 0;
  int16_t skip = 0;
  while (dir.next()) {
    File entry = dir.openFile("r");
    //strcpy(s, entry.name());
    //Serial.println(s);
    entry.close();
    fileCount++;
  }
  Serial.print(F("find "));
  Serial.print(fileCount);
  Serial.println(F(" files"));
  while(1){
    skip = startpos;
    lst = 1;
    dir = SPIFFS.openDir(path);
    setColor(1);
    while (dir.next() && lst < 14) {
      File entry = dir.openFile("r");
      if(skip > 0){
        skip--;
      }
      else{
        strcpy(s, entry.name());
        if(lst + startpos - 1 == pos)
          strcpy(thisF, entry.name());
        putString(s, lst * 8);
        lst++;
      }
      entry.close();   
    }
    if(lst + startpos - 1 < pos){
      if(fileCount > pos)
        startpos++;
      else
        pos--;
    }
    else if(startpos > pos){
      startpos = pos;
    }
    setColor(8);
    drwLine(2, (pos - startpos + 1) * 8, 124,  (pos - startpos + 1) * 8);
    drwLine(2, (pos - startpos + 1) * 8 + 7, 124,  (pos - startpos + 1) * 8 + 7);
    redrawScreen();
    clearScr(0);  
    getKey();
    delay(200);
    while(thiskey == 0){   
      getKey();
      delay(100);
      changeSettings();
    }
    if(thiskey & 16){//ok
      cpuInit();
      loadFromSPIFS(thisF);
      return;
    }
    else if(thiskey & 2){//down
      if(pos < fileCount - 1)
        pos++;
      if(pos - startpos > 12)
        startpos++;
    }
    else if(thiskey & 1){//up
      if(pos > 0)
        pos--;
      if(pos - startpos < 0)
        startpos--;
    }
    if(thiskey & 4){//left
      cpuInit();
      return;
    }
  }
}

void loadFromSPIFS(char fileName[]){
  int i;
  File f = SPIFFS.open(fileName, "r");
  if(f.size() < RAM_SIZE)
    for(i = 0; i < f.size(); i++){
      mem[i] = (uint8_t)f.read();
    }
  Serial.print(F("loaded "));
  Serial.print(i);
  Serial.println(F(" byte"));
  Serial.print(F("free heap "));
  Serial.println(system_get_free_heap_size());
  f.close();  //Close file
}

font_a.c

C/C++
Code accompanies ESP_ILI9341_game_engine.ino.
#ifdef __AVR__
 #include <avr/io.h>
 #include <avr/pgmspace.h>
#elif defined(ESP8266)
 #include <pgmspace.h>
#else
 #define PROGMEM
#endif

// Standard ASCII 5x7 font

static const unsigned char font_a[] PROGMEM = {
0x00,0x00,0x00,0x00,0x00,  // NUL
0x3E,0x55,0x51,0x55,0x3E, // SOH
0x3E,0x6B,0x6F,0x6B,0x3E, // STX
0x0C,0x1E,0x3C,0x1E,0x0C, // ETX
0x08,0x1C,0x3E,0x1C,0x08, // EOT
0x1C,0x4A,0x7F,0x4A,0x1C, // ENQ
0x18,0x5C,0x7F,0x5C,0x18, // ACK
0x00,0x1C,0x1C,0x1C,0x00, // BEL
0x7F,0x63,0x63,0x63,0x7F, // BS
0x00,0x1C,0x14,0x1C,0x00, // TAB
0x7F,0x63,0x6B,0x63,0x7F, // LF
0x30,0x48,0x4D,0x33,0x07, // VT
0x06,0x29,0x79,0x29,0x06, // FF
0x20,0x50,0x3F,0x02,0x0C, // CR
0x60,0x7F,0x05,0x35,0x3F, // SO
0x2A,0x1C,0x77,0x1C,0x2A, // SI
0x00,0x7F,0x3E,0x1C,0x08, // DLE
0x08,0x1C,0x3E,0x7F,0x00, // DC1
0x14,0x22,0x7F,0x22,0x14, // DC2
0x00,0x5F,0x00,0x5F,0x00, // DC3
0x06,0x09,0x7F,0x01,0x7F, // DC4
0x4A,0x55,0x55,0x55,0x29, // NAK
0x60,0x60,0x60,0x60,0x60, // SYN
0x54,0x62,0x7F,0x62,0x54, // ETB
0x08,0x04,0x7E,0x04,0x08, // CAN
0x08,0x10,0x3F,0x10,0x08, // EM
0x08,0x08,0x2A,0x1C,0x08, // SUB
0x08,0x1C,0x2A,0x08,0x08, // ESC
0x1C,0x10,0x10,0x10,0x10, // FS
0x1C,0x3E,0x08,0x3E,0x1C, // GS
0x30,0x3C,0x3F,0x3C,0x30, // RS
0x06,0x1E,0x7E,0x1E,0x06, // US
0x00,0x00,0x00,0x00,0x00, // SPC
0x00,0x00,0x5F,0x00,0x00, // symbol '!'
0x00,0x07,0x00,0x07,0x00, // symbol
0x14,0x7F,0x14,0x7F,0x14, // symbol '#'
0x24,0x2A,0x7F,0x2A,0x12, // symbol '$'
0x23,0x13,0x08,0x64,0x62, // symbol '%'
0x36,0x49,0x56,0x20,0x50, // symbol '&'
0x00,0x00,0x07,0x00,0x00, // symbol '''
0x00,0x1C,0x22,0x41,0x00, // symbol '('
0x00,0x41,0x22,0x1C,0x00, // symbol ')'
0x14,0x08,0x3E,0x08,0x14, // symbol '*'
0x08,0x08,0x3E,0x08,0x08, // symbol '+'
0x00,0x00,0x00,0xe0,0x00, // symbol ','
0x08,0x08,0x08,0x08,0x08, // symbol '-'
0x00,0x60,0x60,0x00,0x00, // symbol '.'
0x20,0x10,0x08,0x04,0x02, // symbol '/'
0x3E,0x51,0x49,0x45,0x3E, // digit '0'
0x44,0x42,0x7F,0x40,0x40, // digit '1'
0x42,0x61,0x51,0x49,0x46, // digit '2'
0x21,0x41,0x45,0x4B,0x31, // digit '3'
0x18,0x14,0x12,0x7F,0x10, // digit '4'
0x27,0x45,0x45,0x45,0x39, // digit '5'
0x3C,0x4A,0x49,0x49,0x30, // digit '6'
0x01,0x71,0x09,0x05,0x03, // digit '7'
0x36,0x49,0x49,0x49,0x36, // digit '8'
0x06,0x49,0x49,0x29,0x1E, // digit '9'
0x00,0x6C,0x6C,0x00,0x00, // symbol ':'
0x00,0x2C,0x59,0x01,0x00, // symbol ';'
0x08,0x14,0x22,0x41,0x00, // symbol '<'
0x14,0x14,0x14,0x14,0x14, // symbol '='
0x00,0x41,0x22,0x14,0x08, // symbol '>'
0x02,0x01,0x51,0x09,0x06, // symbol '?'
0x3E,0x41,0x5D,0x55,0x5E, // symbol '@'
0x7C,0x12,0x11,0x12,0x7C, // eng 'A'
0x7F,0x49,0x49,0x49,0x36, // eng 'B'
0x3E,0x41,0x41,0x41,0x22, // eng 'C'
0x7F,0x41,0x41,0x22,0x1C, // eng 'D'
0x7F,0x49,0x49,0x49,0x41, // eng 'E'
0x7F,0x09,0x09,0x09,0x01, // eng 'F'
0x3E,0x41,0x49,0x49,0x7A, // eng 'G'
0x7F,0x08,0x08,0x08,0x7F, // eng 'H'
0x00,0x41,0x7F,0x41,0x00, // eng 'I'
0x20,0x40,0x41,0x3F,0x01, // eng 'J'
0x7F,0x08,0x14,0x22,0x41, // eng 'K'
0x7F,0x40,0x40,0x40,0x60, // eng 'L'
0x7F,0x02,0x0C,0x02,0x7F, // eng 'M'
0x7F,0x04,0x08,0x10,0x7F, // eng 'N'
0x3E,0x41,0x41,0x41,0x3E, // eng 'O'
0x7F,0x09,0x09,0x09,0x06, // eng 'P'
0x3E,0x41,0x51,0x21,0x5E, // eng 'Q'
0x7F,0x09,0x19,0x29,0x46, // eng 'R'
0x46,0x49,0x49,0x49,0x31, // eng 'S'
0x03,0x01,0x7F,0x01,0x03, // eng 'T'
0x3F,0x40,0x40,0x40,0x3F, // eng 'U'
0x1F,0x20,0x40,0x20,0x1F, // eng 'V'
0x3F,0x40,0x3C,0x40,0x3F, // eng 'W'
0x63,0x14,0x08,0x14,0x63, // eng 'X'
0x07,0x08,0x70,0x08,0x07, // eng 'Y'
0x61,0x51,0x49,0x45,0x43, // eng 'Z'
0x00,0x7F,0x41,0x41,0x00, // symbol '['
0x02,0x04,0x08,0x10,0x20, // symbol '\'
0x00,0x41,0x41,0x7F,0x00, // symbol ']'
0x04,0x02,0x01,0x02,0x04, // symbol '^'
0x40,0x40,0x40,0x40,0x40, // symbol '_'
0x00,0x01,0x02,0x04,0x00, // symbol '`'
0x20,0x54,0x54,0x54,0x78, // eng 'a'
0x7F,0x48,0x44,0x44,0x38, // eng 'b'
0x38,0x44,0x44,0x44,0x48, // eng 'c'
0x38,0x44,0x44,0x48,0x7F, // eng 'd'
0x38,0x54,0x54,0x54,0x18, // eng 'e'
0x08,0x7E,0x09,0x01,0x02, // eng 'f'
0x08,0x54,0x54,0x58,0x3C, // eng 'g'
0x7F,0x08,0x04,0x04,0x78, // eng 'h'
0x00,0x44,0x7D,0x40,0x00, // eng 'i'
0x20,0x40,0x44,0x3D,0x00, // eng 'j'
0x7F,0x10,0x10,0x28,0x44, // eng 'k'
0x00,0x41,0x7F,0x40,0x00, // eng 'l'
0x7C,0x04,0x78,0x04,0x78, // eng 'm'
0x7C,0x08,0x04,0x04,0x78, // eng 'n'
0x38,0x44,0x44,0x44,0x38, // eng 'o'
0x7C,0x14,0x14,0x14,0x08, // eng 'p'
0x08,0x14,0x14,0x0C,0x7C, // eng 'q'
0x7C,0x08,0x04,0x04,0x08, // eng 'r'
0x48,0x54,0x54,0x54,0x24, // eng 's'
0x04,0x3F,0x44,0x40,0x20, // eng 't'
0x3C,0x40,0x40,0x20,0x7C, // eng 'u'
0x1C,0x20,0x40,0x20,0x1C, // eng 'v'
0x3C,0x40,0x38,0x40,0x3C, // eng 'w'
0x44,0x28,0x10,0x28,0x44, // eng 'x'
0x0C,0x50,0x50,0x50,0x3C, // eng 'y'
0x44,0x64,0x54,0x4C,0x44, // eng 'z'
0x00,0x08,0x36,0x41,0x00, // symbol '{'
0x00,0x00,0x7F,0x00,0x00, // symbol '|'
0x00,0x41,0x36,0x08,0x00, // symbol '}'
0x02,0x01,0x02,0x04,0x02, // symbol '~'
0x70,0x48,0x44,0x48,0x70, // DEL
0x00,0x0E,0x11,0x0E,0x00, // symbol '-'
0x00,0x12,0x1F,0x10,0x00, // symbol '¦'
0x00,0x12,0x19,0x16,0x00, // symbol '-'
0x00,0x11,0x15,0x0B,0x00, // symbol '¬'
0x00,0x07,0x04,0x1F,0x00, // symbol 'L'
0x00,0x17,0x15,0x09,0x00, // symbol '-'
0x00,0x0E,0x15,0x09,0x00, // symbol '+'
0x00,0x01,0x1D,0x03,0x00, // symbol '+'
0x00,0x0A,0x15,0x0A,0x00, // symbol 'T'
0x00,0x12,0x15,0x0E,0x00, // symbol '+'
0x00,0x04,0x04,0x04,0x00, // symbol '+'
0x7F,0x7F,0x7F,0x7F,0x7F, // symbol '-'
0x3E,0x00,0x00,0x00,0x00, // symbol '-'
0x3E,0x3E,0x00,0x00,0x00, // symbol '-'
0x3E,0x3E,0x00,0x3E,0x00, // symbol '¦'
0x3E,0x3E,0x00,0x3E,0x3E, // symbol '¦'
0x00,0x01,0x02,0x04,0x08, // symbol '-'
0x40,0x01,0x03,0x06,0x0C, // symbol '-'
0x50,0x21,0x43,0x06,0x0D, // symbol '-'
0x58,0x31,0x63,0x46,0x0D, // symbol '¦'
0x5A,0x35,0x6B,0x56,0x2D, // symbol
0x5B,0x37,0x6F,0x5E,0x3D, // symbol '•'
0x40,0x00,0x40,0x00,0x40, // symbol 'v'
0x60,0x00,0x40,0x00,0x40, // symbol '¦'
0x60,0x00,0x70,0x00,0x40, // symbol '-'
0x60,0x00,0x70,0x00,0x78, // symbol 'г'
0x7C,0x00,0x40,0x00,0x40, // symbol 'L'
0x7C,0x00,0x7E,0x00,0x40, // symbol '¦'
0x7C,0x00,0x7E,0x00,0x7F, // symbol 'T'
0x1C,0x77,0x41,0x41,0x41, // symbol 'T'
0x41,0x41,0x41,0x41,0x41, // symbol '¦'
0x41,0x41,0x41,0x7F,0x00, // symbol '¦'
0x1C,0x77,0x41,0x5D,0x5D, // symbol '='
0x41,0x41,0x41,0x5D,0x5D, // symbol 'Ў'
0x5D,0x5D,0x41,0x5D,0x5D, // symbol 'ў'
0x5D,0x5D,0x41,0x7F,0x00, // symbol '¬'
0x22,0x1C,0x14,0x1C,0x22, // symbol '¤'
0x00,0x08,0x1C,0x08,0x00, // symbol 'г'
0x00,0x00,0x77,0x00,0x00, // symbol '¬'
0x46,0x5D,0x55,0x5D,0x31, // symbol '¬'
0x7C,0x55,0x54,0x55,0x44, // rus 'Ё'
0x08,0x08,0x2A,0x08,0x08, // symbol 'L'
0x00,0x14,0x08,0x14,0x00, // symbol 'Є'
0x08,0x14,0x22,0x08,0x14, // symbol 'L'
0x7F,0x41,0x71,0x31,0x1F, // symbol '-'
0x03,0x05,0x7F,0x05,0x03, // symbol '-'
0x22,0x14,0x7F,0x55,0x22, // symbol '-'
0x02,0x55,0x7D,0x05,0x02, // symbol 'Ї'
0x06,0x09,0x09,0x06,0x00, // symbol '°'
0x44,0x44,0x5F,0x44,0x44, // symbol '¦'
0x1C,0x14,0x1C,0x22,0x7F, // symbol '¦'
0x20,0x3E,0x61,0x3E,0x20, // symbol '¦'
0x20,0x50,0x3F,0x02,0x0C, // symbol '¦'
0x00,0x79,0x41,0x78,0x00, // symbol '¦'
0x44,0x3C,0x04,0x7C,0x44, // symbol 'T'
0x00,0x00,0x08,0x00,0x00, // symbol '·'
0x38,0x55,0x54,0x55,0x18, // rus 'ё'
0x7E,0x08,0x10,0x7F,0x01, // symbol '№'
0x08,0x10,0x08,0x04,0x02, // symbol 'є'
0x14,0x08,0x22,0x14,0x08, // symbol '¦'
0x0E,0x06,0x0A,0x10,0x20, // symbol '+'
0x20,0x10,0x0A,0x06,0x0E, // symbol '+'
0x38,0x30,0x28,0x04,0x02, // symbol '+'
0x02,0x04,0x28,0x30,0x38, // symbol 'ї'
0x7E,0x11,0x11,0x11,0x7E, // rus 'А'
0x7F,0x49,0x49,0x49,0x31, // rus 'Б'
0x7F,0x49,0x49,0x49,0x36, // rus 'В'
0x7F,0x01,0x01,0x01,0x03, // rus 'Г'
0x40,0x7F,0x03,0x7F,0x01, // rus 'Д'
0x7F,0x49,0x49,0x49,0x41, // rus 'Е'
0x77,0x08,0x7F,0x08,0x77, // rus 'Ж'
0x41,0x49,0x49,0x49,0x36, // rus 'З'
0x7F,0x10,0x08,0x04,0x7F, // rus 'И'
0x7C,0x21,0x12,0x09,0x7C, // rus 'Й'
0x7F,0x08,0x14,0x22,0x41, // rus 'К'
0x40,0x3E,0x01,0x01,0x7F, // rus 'Л'
0x7F,0x02,0x0C,0x02,0x7F, // rus 'М'
0x7F,0x08,0x08,0x08,0x7F, // rus 'Н'
0x3E,0x41,0x41,0x41,0x3E, // rus 'О'
0x7F,0x01,0x01,0x01,0x7F, // rus 'П'
0x7F,0x09,0x09,0x09,0x06, // rus 'Р'
0x3E,0x41,0x41,0x41,0x22, // rus 'С'
0x01,0x01,0x7F,0x01,0x01, // rus 'Т'
0x07,0x48,0x48,0x48,0x3F, // rus 'У'
0x0E,0x11,0x7F,0x11,0x0E, // rus 'Ф'
0x63,0x14,0x08,0x14,0x63, // rus 'Х'
0x7F,0x40,0x40,0x7F,0x40, // rus 'Ц'
0x07,0x08,0x08,0x08,0x7F, // rus 'Ч'
0x7F,0x40,0x7F,0x40,0x7F, // rus 'Ш'
0x7F,0x40,0x7F,0x40,0x7F, // rus 'Щ'
0x01,0x7F,0x48,0x48,0x30, // rus 'Ъ'
0x7F,0x48,0x48,0x30,0x7F, // rus 'Ы'
0x7F,0x48,0x48,0x48,0x30, // rus 'Ь'
0x22,0x41,0x49,0x49,0x3E, // rus 'Э'
0x7F,0x08,0x3E,0x41,0x3E, // rus 'Ю'
0x46,0x29,0x19,0x09,0x7F, // rus 'Я'
0x20,0x54,0x54,0x54,0x78, // rus 'а'
0x3C,0x4A,0x4A,0x49,0x31, // rus 'б'
0x7C,0x54,0x54,0x54,0x28, // rus 'в'
0x7C,0x04,0x04,0x04,0x0C, // rus 'г'
0x40,0x71,0x09,0x79,0x01, // rus 'д'
0x38,0x54,0x54,0x54,0x18, // rus 'е'
0x6C,0x10,0x7C,0x10,0x6C, // rus 'ж'
0x44,0x54,0x54,0x54,0x28, // rus 'з'
0x7C,0x20,0x10,0x08,0x7C, // rus 'и'
0x7C,0x40,0x26,0x10,0x7C, // rus 'й'
0x7C,0x10,0x10,0x28,0x44, // rus 'к'
0x40,0x38,0x04,0x04,0x7C, // rus 'л'
0x7C,0x08,0x10,0x08,0x7C, // rus 'м'
0x7C,0x10,0x10,0x10,0x7C, // rus 'н'
0x38,0x44,0x44,0x44,0x38, // rus 'о'
0x7C,0x04,0x04,0x04,0x7C, // rus 'п'
0x7C,0x14,0x14,0x14,0x08, // rus 'р'
0x38,0x44,0x44,0x44,0x48, // rus 'с'
0x04,0x04,0x7C,0x04,0x04, // rus 'т'
0x0C,0x50,0x50,0x50,0x3C, // rus 'у'
0x18,0x24,0x7C,0x49,0x30, // rus 'ф'
0x44,0x28,0x10,0x28,0x44, // rus 'х'
0x7C,0x40,0x40,0x7C,0x40, // rus 'ц'
0x0C,0x10,0x10,0x10,0x7C, // rus 'ч'
0x7C,0x40,0x7C,0x40,0x7C, // rus 'ш'
0x7C,0x40,0x7C,0x40,0x7C, // rus 'щ'
0x04,0x7C,0x50,0x50,0x20, // rus 'ъ'
0x7C,0x50,0x50,0x20,0x7C, // rus 'ы'
0x7C,0x50,0x50,0x50,0x20, // rus 'ь'
0x28,0x44,0x54,0x54,0x38, // rus 'э'
0x7C,0x10,0x38,0x44,0x38, // rus 'ю'
0x48,0x34,0x14,0x14,0x7C  // rus 'я'
};

keyboard.ino

Arduino
Code accompanies ESP_ILI9341_game_engine.ino.
#include <NintendoExtensionCtrl.h>
#include "keys.h"

void getKey(){
  boolean success = nchuk.update();  // Get new data from the controller

  thiskey = 0;
  if (nchuk.joyY() > 192) {
    thiskey |= KEY_UP;
  } else if (nchuk.joyY() < 64) {
    thiskey |= KEY_DOWN;
  }
  if (nchuk.joyX() > 192) {
    thiskey |= KEY_RIGHT;
  } else if (nchuk.joyX() < 64) {
    thiskey |= KEY_LEFT;
  }
  if (nchuk.buttonZ()) thiskey |= KEY_A;
  if (nchuk.buttonC()) thiskey |= KEY_B;
  // KEY_SELECT and KEY_START omitted
}

keys.h

C Header File
Code accompanies ESP_ILI9341_game_engine.ino.
/** User Input Keys */

#ifndef _KEYS_H_
#define _KEYS_H_

#define KEY_UP 1
#define KEY_DOWN 2
#define KEY_LEFT 4
#define KEY_RIGHT 8
#define KEY_A 16
#define KEY_B 32
#define KEY_SELECT 64
#define KEY_START 128

#endif

Github ESPLGE Nunchuk Repo

NOTE: Go to Nunchuk2 branch if the Main branch doesn't work for some reason. :V

Credits

HyperChiicken

HyperChiicken

7 projects • 12 followers
I cobble things together :]

Comments