From 9ae9c93b82ffdb1d245869114490fbdaf42db957 Mon Sep 17 00:00:00 2001 From: corax89 Date: Sun, 17 Feb 2019 12:44:07 +0300 Subject: [PATCH] init --- ESP_ILI9341_game_engine.ino | 213 +++++++ README.md | 5 + cpu.ino | 773 +++++++++++++++++++++++++ data/collision.bin | Bin 0 -> 1093 bytes data/pi.bin | Bin 0 -> 729 bytes data/platformer.bin | Bin 0 -> 1303 bytes data/snake.bin | Bin 0 -> 1194 bytes data/space.bin | Bin 0 -> 971 bytes display.ino | 1063 +++++++++++++++++++++++++++++++++++ file_manager.ino | 89 +++ font_a.c | 270 +++++++++ keyboard.ino | 42 ++ libraries.7z | Bin 0 -> 62658 bytes 13 files changed, 2455 insertions(+) create mode 100644 ESP_ILI9341_game_engine.ino create mode 100644 cpu.ino create mode 100644 data/collision.bin create mode 100644 data/pi.bin create mode 100644 data/platformer.bin create mode 100644 data/snake.bin create mode 100644 data/space.bin create mode 100644 display.ino create mode 100644 file_manager.ino create mode 100644 font_a.c create mode 100644 keyboard.ino create mode 100644 libraries.7z diff --git a/ESP_ILI9341_game_engine.ino b/ESP_ILI9341_game_engine.ino new file mode 100644 index 0000000..d9c44e2 --- /dev/null +++ b/ESP_ILI9341_game_engine.ino @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#include +#include +ADC_MODE(ADC_VCC); + +// 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; +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,0x99,0x03,0x90,0x00,0x18,0x02,0x11,0x02,0xD4,0x51,0x12,0x00,0x06,0x20,0x97,0x03,0x31,0x01,0x97,0x03,0x12,0x08,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0x92,0x00,0x01,0x02,0x62,0x03,0x82,0x02,0x31,0x02,0x97,0x03,0x13,0x10,0xA4,0x23,0x82,0x02,0x12,0x50,0x82,0x02,0x12,0x08,0x82,0x02,0x12,0x08,0x82,0x02,0x99,0x00,0x30,0x02,0xA8,0xA0,0x01,0x02,0x40,0x03,0x82,0x02,0x31,0x02,0x97,0x03,0x13,0x10,0xA4,0x23,0x82,0x02,0x12,0x60,0x82,0x02,0x12,0x08,0x82,0x02,0x12,0x08,0x82,0x02,0x99,0x00,0x30,0x02,0xA8,0xA0,0x01,0x02,0x40,0x03,0x82,0x02,0x31,0x02,0x97,0x03,0x13,0x10,0xA4,0x23,0x82,0x02,0x12,0x70,0x82,0x02,0x12,0x08,0x82,0x02,0x12,0x08,0x82,0x02,0x99,0x00,0x30,0x02,0xA8,0xA0,0xA8,0x10,0x97,0x03,0x90,0x00,0x14,0x00,0x12,0x00,0x06,0x20,0x95,0x03,0x31,0x01,0x95,0x03,0x12,0x20,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0xF0,0x00,0x31,0x01,0x95,0x03,0x01,0x02,0xBE,0x02,0xD5,0x12,0x31,0x01,0x95,0x03,0x12,0x78,0xAD,0x02,0x13,0x78,0xAD,0x03,0xE1,0x23,0x31,0x01,0x95,0x03,0x12,0x02,0x1F,0x02,0xF1,0xF2,0x31,0x01,0x95,0x03,0x12,0x02,0x1F,0x03,0xF1,0xF2,0x31,0x01,0x95,0x03,0x12,0x04,0x13,0x10,0xF1,0x23,0x31,0x01,0x95,0x03,0x12,0x05,0x13,0x10,0xF1,0x23,0xA8,0x10,0x95,0x03,0x90,0x00,0x98,0x00,0x12,0x01,0x82,0x02,0x12,0x04,0x82,0x02,0x01,0x02,0xD0,0x07,0x82,0x02,0x99,0x00,0x3A,0x02,0xA8,0x60,0x12,0xBE,0x82,0x02,0x12,0xDC,0x82,0x02,0x12,0x00,0x82,0x02,0x12,0x09,0x82,0x02,0x99,0x00,0x44,0x02,0xA8,0x80,0x11,0x01,0xD4,0x51,0x11,0x01,0xB1,0x00,0x92,0x00,0x16,0x02,0x11,0x01,0x12,0x02,0xD8,0x12,0x03,0x01,0x99,0x03,0xA8,0x10,0x99,0x03,0x12,0x40,0x82,0x02,0x12,0x40,0x82,0x02,0x03,0x02,0x99,0x03,0x82,0x02,0x99,0x00,0x4E,0x02,0xA8,0x60,0x12,0x00,0x06,0x20,0x95,0x03,0x31,0x01,0x95,0x03,0x12,0x20,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0xF2,0x01,0x31,0x01,0x95,0x03,0x12,0x06,0x03,0x03,0x99,0x03,0xF1,0x23,0x31,0x01,0x95,0x03,0x12,0x00,0xDC,0x12,0x12,0x02,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0x88,0x01,0x31,0x01,0x95,0x03,0x12,0x02,0x1F,0x02,0xF1,0xF2,0x31,0x01,0x95,0x03,0x12,0x00,0xDC,0x12,0x12,0x76,0xC1,0x12,0xC2,0x13,0xB1,0x00,0x92,0x00,0xAA,0x01,0x31,0x01,0x95,0x03,0x12,0x00,0x13,0x02,0xA2,0x23,0x1F,0x02,0xF1,0xF2,0x31,0x01,0x95,0x03,0x12,0x01,0xDC,0x12,0x12,0x02,0xC1,0x12,0xC2,0x12,0xB1,0x00,0x92,0x00,0xC8,0x01,0x31,0x01,0x95,0x03,0x12,0x02,0x1F,0x03,0xF1,0xF2,0x31,0x01,0x95,0x03,0x12,0x01,0xDC,0x12,0x12,0x76,0xC1,0x12,0xC2,0x13,0xB1,0x00,0x92,0x00,0xEA,0x01,0x31,0x01,0x95,0x03,0x12,0x00,0x13,0x02,0xA2,0x23,0x1F,0x03,0xF1,0xF2,0xA8,0x10,0x95,0x03,0x90,0x00,0x4E,0x01,0x11,0x04,0x12,0x02,0xD1,0x31,0xD1,0x42,0x01,0x02,0x84,0x03,0x82,0x02,0xD2,0x12,0x82,0x02,0x11,0x04,0x99,0x00,0x58,0x02,0x07,0x21,0xA8,0x40,0x99,0x00,0x26,0x02,0x90,0x00,0x1E,0x01,0x9A,0x00,0x01,0x0F,0x00,0x00,0x06,0xF0,0x9B,0x03,0x99,0x00,0x0A,0x00,0x50,0x00,0xC2,0x16,0xB1,0x00,0x92,0x00,0x26,0x02,0x9A,0x00,0x07,0x10,0x12,0x02,0xA0,0x12,0xD4,0x01,0x9A,0x00,0x07,0x10,0x12,0x02,0xA0,0x12,0xD7,0x01,0x9A,0x00,0x07,0x10,0x12,0x02,0xA0,0x12,0xD7,0x11,0x9A,0x00,0x07,0x10,0x12,0x02,0xA0,0x12,0xD7,0x21,0x9A,0x00,0x07,0x20,0xA0,0x21,0x02,0x22,0x20,0x32,0xB3,0x25,0x92,0x00,0x7C,0x02,0xD1,0x03,0xA8,0x02,0x20,0x32,0x91,0x00,0x60,0x02,0x9A,0x00,0xA8,0x02,0x20,0x32,0x91,0x00,0x60,0x02,0x9A,0x00,0xA8,0x02,0x20,0x32,0xB3,0x25,0x92,0x00,0xA6,0x02,0xA9,0x21,0x64,0x10,0xB3,0x44,0x92,0x00,0xAC,0x02,0xB3,0x49,0x92,0x00,0xAC,0x02,0xB3,0x53,0x92,0x00,0xB8,0x02,0xB3,0x43,0x92,0x00,0xB2,0x02,0x90,0x00,0x60,0x02,0xD1,0x03,0x90,0x00,0x72,0x02,0xD1,0x24,0x90,0x00,0x72,0x02,0xD1,0x04,0x90,0x00,0x72,0x02,0xD1,0x14,0x90,0x00,0x72,0x02,0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x66,0x77,0x77,0x66,0x00,0x00,0x00,0x06,0x77,0x8F,0xC8,0x88,0x60,0x00,0x00,0x67,0x88,0x8F,0xC8,0x88,0x86,0x00,0x06,0x78,0x88,0x8F,0xC8,0x88,0x88,0x60,0x06,0x78,0x88,0x8F,0xC8,0x88,0x88,0x60,0x67,0x88,0x88,0xFC,0xCC,0x88,0x88,0xB6,0x67,0xFF,0xFF,0xCC,0xCC,0xCC,0xCC,0xB6,0x67,0xCC,0xCC,0xCC,0xCC,0xBB,0xBB,0xB6,0x67,0x88,0x88,0xCC,0xCB,0x88,0x88,0xB6,0x06,0x88,0x88,0x8C,0xB8,0x88,0x8B,0x60,0x06,0x88,0x88,0x8C,0xB8,0x88,0x8B,0x60,0x00,0x68,0x88,0x8C,0xB8,0x88,0xB6,0x00,0x00,0x06,0x88,0x8C,0xB8,0xBB,0x60,0x00,0x00,0x00,0x66,0xBB,0xBB,0x66,0x00,0x00,0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x99,0x99,0x99,0x99,0xCC,0xCB,0x9C,0xCC,0xFF,0xCB,0x9F,0xFF,0xFF,0xFB,0x9F,0xFF,0x99,0x99,0x99,0x99,0xCC,0xCC,0xCC,0xB9,0xFF,0xFF,0xFC,0xB9,0xFF,0xFF,0xFF,0xB9,0x00,0x00,0x09,0x99,0x99,0x00,0x09,0xCC,0xB9,0x00,0x09,0xFC,0xB9,0x00,0x09,0xFF,0xB9,0x00,0x99,0x99,0x99,0x99,0xCC,0xCC,0xCC,0xB9,0xFF,0xFF,0xFC,0xB9,0xFF,0xFF,0xFF,0xB9,0x00,0x00,0x20,0x4B,0x45,0x59,0x20,0x50,0x52,0x45,0x53,0x53,0x45,0x44,0x20,0x25,0x44,0x20,0x00}; +uint16_t palette[16] = { + 0x0020, 0xE718, 0xB9A8, 0x7DB6, 0x41E9, 0x6D2D, 0x21EC, 0xD5CA, + 0xAC4D, 0x31A7, 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 coos_cpu(void){ + while(1){ + COOS_DELAY(1); // 1 ms + timeR = millis(); + cpuOPS += 1; + cpuRun(1000); + 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(); + tft.fillRect(0, 0, 32, 80, 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); + Wire.begin(D2, D1); + geti2cAdress(); + Serial.println(i2c_adress, HEX); + Serial.println(); + cpuInit(); + //Initialize File System + if(SPIFFS.begin()){ + Serial.println(F("SPIFFS Initialize....ok")); + fileList("/"); + } + else{ + Serial.println(F("SPIFFS Initialization...failed")); + } + voltaje = ESP.getVcc(); + randomSeed(ESP.getVcc()); + clearScr(); + setColor(1); + 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 + if(Serial.available()){ + char c = Serial.read(); + Serial.print(c); + if(c == 'm'){ + loadFromSerial(); + cpuInit(); + return; + } + if(c == 'r'){ + ESP.reset(); + return; + } + } +} diff --git a/README.md b/README.md index be478d5..f256331 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # esp8266_game_engine Used ili9341. Contains a virtual machine running games from RAM. +Used library: +[https://github.com/akouz/a_coos](https://github.com/akouz/a_coos) +[https://github.com/Bodmer/TFT_eSPI](https://github.com/Bodmer/TFT_eSPI) +Online emulator with compiler: +[https://corax89.github.io/esp8266Game/index.html](https://corax89.github.io/esp8266Game/index.html) diff --git a/cpu.ino b/cpu.ino new file mode 100644 index 0000000..3ef07fb --- /dev/null +++ b/cpu.ino @@ -0,0 +1,773 @@ +int16_t reg[15]; +int16_t pc = 0; +byte carry = 0; +byte zero = 0; +byte negative = 0; +byte redraw = 0; +int8_t color = 1; +int8_t bgcolor = 0; +String s_buffer; + +void cpuInit(){ + for(byte i = 1; i < 16; i++){ + reg[i] = 0; + } + display_init(); + reg[0] = RAM_SIZE - 1;//stack pointer + clearScr(); + color = 1; + bgcolor = 0; + setCharX(0); + setCharY(0); + pc = 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(" OP:"); + Serial.print(readMem(pc),HEX); + Serial.print(" PC:"); + Serial.println(pc); + delay(10); +}*/ + +inline void writeInt(uint16_t adr, int16_t n){ + writeMem(adr + 1, (n & 0xff00) >> 8); + writeMem(adr, n & 0xff); +} + +inline int16_t readInt(uint16_t adr){ + 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; +} + +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; + 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; + 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 + pc = readInt(reg[0]); + 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: + // RAND R,R AD 0R + reg1 = op2 & 0xf; + n = random(0, reg[reg1] + 1); + 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(); + 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; + } + 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); + break; + case 0xD8: + // SCROLL R,R D8RR + reg1 = (op2 & 0xf0) >> 4;//шаг + reg2 = op2 & 0xf;//направление + scrollScreen(reg[reg1], 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; + } + 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; + } +} diff --git a/data/collision.bin b/data/collision.bin new file mode 100644 index 0000000000000000000000000000000000000000..7c1b05a2da58f68b0594b8632859f9728ae23652 GIT binary patch literal 1093 zcmcIhy=xO;7=Pa9&P(E;cW;U~>6^y>0V)0g#pVo0K~mGjMKVazNyNcqaF7h;K&fda zuLup|a0(qH5DWpqPKDfHBt?-7x;ThK>T2M8-usAc6`XvI=lkpTyvIQkmfD1?Xy9m- zlUGo4tf2DbTfWC%L5n_yTy0@%-*AHT5f$AW+9Ewva|cx(f<9oKfYOJXZ_!f_Qw^; zF=y!e@s2YALWZ}M1Kx!5ktbg{e6MTFP&p2B!z&Cg)2;7$0}a)I4NPR*B?s292^ZKl zu(JT#WQ8)Pp$L2DB>pwrhJ?v`!p9S z{eHACRPzxD$`PRm)c@DfF)Ep>z5+Ge`)DB%N9U z9ZDCSL=X&u;GfW?_$PSI%~SGd;oZZ1_nz<9xjh`x!?6Obt1#>u#~4Xu(-r^2xiO`q=D3jT%3X_!$Ot}K^sAdlXA)BUGKM#j z;w7`>v3Z_dO}!%gveL@y6u$}LRuCS`z1k%+5Em=r;Ka!Vv_>X!YrV6;HKO1#Fte(N*5ao2x z;Ci|>YiIbvd1=CQ>nlF7kK#`6zjN9+j%WKBesXpY>W2(hob6@!&7PpU_V;BogdB{(#syOD;d+lFcujnLd{{lJ(lkKF zk<{@9-iqKIF$=>~5qAwabqji>BdQ?q9`krZc~dwk+hB};FiPCudDC+igAx~jucO}( z_Mu*5y!o#pVa3@J*NVE6$M16wy*{E++&h-RCr`ybUId!Y}k5$fjOyx5w=fvdVJJ9hC0i39@=zrhM3yEQ-o qO(@7u5yZyb3?Q1k$te?bHDu^cN8OPs)g4voj#NLyPk%_0vVQ?{2HVsC literal 0 HcmV?d00001 diff --git a/data/snake.bin b/data/snake.bin new file mode 100644 index 0000000000000000000000000000000000000000..e302c662306338553282dd9466aa73a80b7cf12b GIT binary patch literal 1194 zcma)5zl#$=6n<}JZWC?HzP-XqCKnX0%OZ%yA*e(vJ&ht3mg3)Fafk%D#w(WVHdTnH z#3P0nLI`+GQdycHg8zm17x>=HW;ZDmS!QS6?)QFt?|qZNbx7eAg!mbb;TmY17&%$~ zN?59<@EcQjhV-?iQg{G8n?M=bfENIQU$3`PF#NStA=RPp*ycFKDtJgCHeJL6jyN_1 zGn$7I_a!VTuw#?JQ;tFq*omPaumi>fc4$J*dX=UoMAB|onXO4V+hq1uwg=f>@;jZ6 zE`8FST3}C2aqb&bZ%s?Z>eNK~q$a0+b|T$UvQoGMy$&-lR9?i3TZpMyglwJj);qRh zj$2fMJ25T`S%fDi@{X9_S5vw6*p4H};Ke(zca<63u2^=anHL2I5 zpg`SFq&840M`3|_&OO~gm2;FTP>0G-UEX7}s$KdR<8VziKNRZ%Twl09P6tpAr-pWC z)%mt`bSVg@oCJ=v^L8BN-?nBoq4(~3Z*<;*aNZR`>`L6++p!YwrY>@4-adEkeP0lp z-dfm-_t9GJta6__uiPWnIO!82?C@IYK<4?A?N!V(EpwXiT@eH>gRYWD*d2Ivi*8ld=~j3M<|FIWDgQ7365!h0jo}+t dkq36HJn&^G58S68ek#CDJOSsAav`#3aG`)<@b7Tmy2 zsfH(@_=(^r#OWNrD|q!==FOqm;FFNQ2;T!c5$3yP=AzSSVA?seh(aZ07Exe+gB-`C zx^~Ty+C}&0=-{XGibcV?lkR3p>%9}2utw)zw=<+RQ*{oRhNk8JvBFu;z2bjo^T=GC z&1IJ&>YC6+Pa-N@5%EizQ&;t`2Ckge$2%?OW<;7*ja}Pj>%Ah%X+*a=?ft!MzqjM> z;|%rOV}DDYxp}o>n0)BUpwpYHn%5-~JsU+d%fk6y;1S2^A?0{T97#o|YPu+Wv(WdI z$#Fzq?1(hu--_)lux~Fle)8DCU#z~JRaQ<1G|Or@m4Ed;Z5h5N_E^k{UE4B!SfN3S zW=C@xSG%#CJ8}%#Q73qAYWO9V!ZzSdfcWfV)EDn5T(_(@irWBC10+=qO<5&nRV7tZ zHBDJJO&=0j3aIO(s;Z_`L|OH#vJ!L+NuyLnpbfS<18{EPe|K)h-C+qhD-X&BHY|zA a@|(u|2Uw3357DE-?oa??C>$}Aet!UEyykiU literal 0 HcmV?d00001 diff --git a/display.ino b/display.ino new file mode 100644 index 0000000..85950c5 --- /dev/null +++ b/display.ino @@ -0,0 +1,1063 @@ +#include "font_a.c" + +#define PARTICLE_COUNT 32 + +struct sprite { + int16_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 solid; + int8_t gravity; +}; + +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[64][128]; +uint8_t line_is_draw[128]; +uint8_t sprite_screen[64][128]; +char charArray[340]; +uint16_t pix_buffer[256]; +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; + +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); +} + +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].solid = 0; + sprite_table[i].gravity = 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 redrawScreen(){ + uint8_t i; + cadr_count++; + for(uint8_t y = 0; y < 128; y++){ + i = 0; + if(line_is_draw[y] == 1){ + if(y < 8) + tft.setAddrWindow(32 + 0, y, 32 + 127, y + 1); + else if(y > 120) + tft.setAddrWindow(32 + 0, 232 - 120 + y, 32 + 127, 232 - 120 + y + 1); + else + tft.setAddrWindow(32 + 0, y * 2 - 8, 32 + 127, y * 2 + 2 - 8); + //в одной ячейке памяти содержится два пикселя + for(uint8_t x = 0; x < 32; x++){ + if((sprite_screen[x][y] & 0xf0) > 0) + pix_buffer[i] = palette[(sprite_screen[x][y] & 0xf0) >> 4]; + else + pix_buffer[i] = palette[(screen[x][y] & 0xf0) >> 4]; + i++; + pix_buffer[i] = pix_buffer[i - 1]; + i++; + if((sprite_screen[x][y] & 0x0f) > 0) + pix_buffer[i] = palette[sprite_screen[x][y] & 0x0f]; + else + pix_buffer[i] = palette[screen[x][y] & 0x0f]; + i++; + pix_buffer[i] = pix_buffer[i - 1]; + i++; + } + tft.pushColors(pix_buffer, 128); + if(y >= 8 && y <= 120) + tft.pushColors(pix_buffer, 128); + line_is_draw[y] = 0; + } + else if(line_is_draw[y] == 2){ + if(y < 8) + tft.setAddrWindow(32 + 128, y, 32 + 255, y + 1); + else if(y > 120) + tft.setAddrWindow(32 + 128, 232 - 120 + y, 32 + 255, 232 - 120 + y + 1); + else + tft.setAddrWindow(32 + 128, y * 2 - 8, 32 + 255, y * 2 + 2 - 8); + //в одной ячейке памяти содержится два пикселя + for(uint8_t x = 0; x < 32; x++){ + if((sprite_screen[x + 32][y] & 0xf0) > 0) + pix_buffer[i] = palette[(sprite_screen[x + 32][y] & 0xf0) >> 4]; + else + pix_buffer[i] = palette[(screen[x + 32][y] & 0xf0) >> 4]; + i++; + pix_buffer[i] = pix_buffer[i - 1]; + i++; + if((sprite_screen[x + 32][y] & 0x0f) > 0) + pix_buffer[i] = palette[sprite_screen[x + 32][y] & 0x0f]; + else + pix_buffer[i] = palette[screen[x + 32][y] & 0x0f]; + i++; + pix_buffer[i] = pix_buffer[i - 1]; + i++; + } + tft.pushColors(pix_buffer, 128); + if(y >= 8 && y <= 120) + tft.pushColors(pix_buffer, 128); + line_is_draw[y] = 0; + } + else if(line_is_draw[y] == 3){ + if(y < 8) + tft.setAddrWindow(32 + 0, y, 32 + 255, y + 1); + else if(y > 120) + tft.setAddrWindow(32 + 0, 232 - 120 + y, 32 + 255, 232 - 120 + y + 1); + else + tft.setAddrWindow(32 + 0, y * 2 - 8, 32 + 255, y * 2 + 2 - 8); + //в одной ячейке памяти содержится два пикселя + for(uint8_t x = 0; x < 64; x++){ + if((sprite_screen[x][y] & 0xf0) > 0) + pix_buffer[i] = palette[(sprite_screen[x][y] & 0xf0) >> 4]; + else + pix_buffer[i] = palette[(screen[x][y] & 0xf0) >> 4]; + i++; + pix_buffer[i] = pix_buffer[i - 1]; + i++; + if((sprite_screen[x][y] & 0x0f) > 0) + pix_buffer[i] = palette[sprite_screen[x][y] & 0x0f]; + else + pix_buffer[i] = palette[screen[x][y] & 0x0f]; + i++; + pix_buffer[i] = pix_buffer[i - 1]; + i++; + } + tft.pushColors(pix_buffer, 256); + if(y >= 8 && y <= 120) + tft.pushColors(pix_buffer, 256); + line_is_draw[y] = 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[x][y] = (sprite_screen[x][y] & 0xf0) + (particles[n].color & 0x0f); + else + sprite_screen[x][y] = (sprite_screen[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) + if(sprite_table[i].x < 128 && sprite_table[i].y < 128) + drawSpr(i, sprite_table[i].x, sprite_table[i].y); + sprite_table[i].speedy += sprite_table[i].gravity; + sprite_table[i].x += sprite_table[i].speedx; + sprite_table[i].y += sprite_table[i].speedy; + } +} + +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; + //int16_t adr; + 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].solid != 0 && sprite_table[i].solid != 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].solid != 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++){ + if(sprite_screen[x][y] > 0) + line_is_draw[y] |= 1 + x / 32; + sprite_screen[x][y] = 0; + } +} + +void clearScr(){ + for(byte y = 0; y < 128; y ++){ + line_is_draw[y] = 3; + for(byte x = 0; x < 64; x++) + screen[x][y] = 0; + } +} + +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 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 getSpriteValue(int8_t n, uint8_t t){ + if(t == 0) + return sprite_table[n].x; + else if(t == 1) + return sprite_table[n].y; + else if(t == 2) + return sprite_table[n].speedx; + else if(t == 3) + return sprite_table[n].speedy; + else if(t == 4) + return sprite_table[n].width; + else if(t == 5) + return sprite_table[n].height; + else if(t == 6) + return sprite_table[n].angle; + else if(t == 7) + return sprite_table[n].lives; + else if(t == 8) + return sprite_table[n].collision; + else if(t == 9) + return sprite_table[n].solid; + else if(t == 10) + return sprite_table[n].gravity; + return 0; +} + +void setSpriteValue(int8_t n, uint8_t t, int16_t v){ + if(t == 0) + sprite_table[n].x = v; + else if(t == 1) + sprite_table[n].y = v; + else if(t == 2){ + sprite_table[n].speedx = (int8_t) v; + } + else if(t == 3){ + sprite_table[n].speedy = (int8_t) v; + } + else if(t == 4) + sprite_table[n].width = v; + else if(t == 5) + sprite_table[n].height = v; + else if(t == 6) + sprite_table[n].angle = (v % 360) & 0x01ff; + else if(t == 7) + sprite_table[n].lives = v; + else if(t == 9) + sprite_table[n].solid = v; + else if(t == 10) + sprite_table[n].gravity = v; +} + +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; + x0 = x0/2; + if(x0 + nnx >= 0 && x0 + nnx < 64 && y0 + ny >= 0 && y0 + ny < 128){ + if(nx & 1) + sprite_screen[x0 + nnx][y0 + ny] = (sprite_screen[x0 + nnx][y0 + ny] & 0xf0) + pixel; + else + sprite_screen[x0 + nnx][y0 + ny] = (sprite_screen[x0 + nnx][y0 + ny] & 0x0f) + (pixel << 4); + line_is_draw[y0 + ny] |= 1 + (x0 + 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(x & 1) + sprite_screen[(x0 + x) / 2][y0 + y] = (sprite_screen[(x0 + x) / 2][y0 + y] & 0xf0) + pixel; + else + sprite_screen[(x0 + x) / 2][y0 + y] = (sprite_screen[(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, uint16_t x, uint16_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, uint16_t x1, uint16_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 drawImgS(int16_t a, uint16_t x, uint16_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, uint16_t x1, uint16_t y1, int16_t w, int16_t h){ + int16_t i = 0; + uint8_t jx, jy, s; + s = imageSize; + 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 < s; jx++) + for(jy = 0; jy < s; jy++) + setPix(x1 + (i % w) * s + jx, y1 + i / w * s + jy, color1); + } + if(color2 > 0){ + for(jx = 0; jx < s; jx++) + for(jy = 0; jy < s; jy++) + setPix(x1 + (i % w) * s + s + jx, y1 + i / w * s + 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 < s; jx++) + for(jy = 0; jy < s; jy++) + setPix(x1 + (i % w) * s + jx, y1 + i / w * s + jy, color1); + } + if(color2 > 0){ + for(jx = 0; jx < s; jx++) + for(jy = 0; jy < s; jy++) + setPix(x1 + (i % w) * s + s + jx, y1 + i / w * s + jy, color2);; + } + i += 2; + repeat--; + } + else if(repeat == 0){ + adr++; + repeat = readMem(adr); + adr++; + color1 = (readMem(adr) & 0xf0) >> 4; + color2 = readMem(adr) & 0xf; + } + } + } + +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 > 0 && nx < 128 && ny > 0 && ny < 128){ + imgadr = readInt(tile.adr + (x + y * tile.width) * 2); + if(imgadr > 0) + drawImg(imgadr, nx, ny, tile.imgwidth, tile.imgheight); + } + } + } + } + +void drwLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2) { + 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(byte x, byte y, byte c){ + byte xi = x / 2; + if(x >= 0 && x < 128 && y >= 0 && y < 128){ + if(x & 1) + screen[xi][y] = (screen[xi][y] & 0xf0) + c; + else + screen[xi][y] = (screen[xi][y] & 0x0f) + ( c << 4); + line_is_draw[y] |= 1 + x / 64; + } +} + +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[xi][y] & 0xf0) >> 4; + else + b = (screen[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[0][ y]; + for(uint8_t x = 1; x < 64; x++){ + if(screen[x - 1][y] != screen[x][y]) + line_is_draw[y] |= 1 + x / 32; + screen[x - 1][ y] = screen[x][y]; + } + if(screen[63][y] != bufPixel) + line_is_draw[y] |= 1; + screen[63][ y] = bufPixel; + } + for(uint8_t n = 0; n < 32; n++) + sprite_table[n].x--; + } + else if(direction == 1){ + for(uint8_t x = 0; x < 64; x++){ + bufPixel = screen[x][0]; + for(uint8_t y = 1; y < 128; y++){ + if(screen[x][y-1] != screen[x][y]) + line_is_draw[y] |= 1 + x / 32; + screen[x][y - 1] = screen[x][y]; + } + if(screen[x][127] != bufPixel) + line_is_draw[127] |= 2; + screen[x][127] = bufPixel; + } + for(uint8_t n = 0; n < 32; n++) + sprite_table[n].y++; + } + else if(direction == 0){ + for(uint8_t y = 0; y < 128; y++){ + bufPixel = screen[63][y]; + for(uint8_t x = 63; x > 0; x--){ + if(screen[x][y] != screen[x - 1][y]) + line_is_draw[y] |= 1 + x / 32; + screen[x][y] = screen[x - 1][y]; + } + if(screen[0][y] != bufPixel) + line_is_draw[y] |= 1; + screen[0][y] = bufPixel; + } + for(uint8_t n = 0; n < 32; n++) + sprite_table[n].x++; + } + else { + for(uint8_t x = 0; x < 64; x++){ + bufPixel = screen[x][127]; + for(uint8_t y = 127; y > 0; y--){ + if(screen[x][y] != screen[x][y - 1]) + line_is_draw[y] |= 1 + x / 32; + screen[x][y] = screen[x][y - 1]; + } + if(screen[x][0] != bufPixel) + line_is_draw[0] |= 1 + x / 32; + screen[x][0] = bufPixel; + } + for(uint8_t n = 0; n < 32; n++) + sprite_table[n].y--; + } + if(tile.adr > 0) + tileDrawLine(step, direction); +} + +void tileDrawLine(uint8_t step, uint8_t direction){ + int16_t x,y,x0,y0,y1,nx,ny; + uint16_t imgadr; + if(direction == 2){ + tile.x -= step*2; + x0 = tile.x; + y0 = tile.y; + x = (127 - x0) / tile.imgwidth; + nx = x0 + x * tile.imgwidth; + if(x < tile.width && x >= 0){ + for(y = 0; y < tile.height; y++){ + ny = y0 + y * tile.imgheight; + if(ny > 0 && ny < 128){ + imgadr = readInt(tile.adr + (x + y * tile.width) * 2); + if(imgadr > 0) + drawImg(imgadr, nx, ny, tile.imgwidth, tile.imgheight); + else + fillRect(nx, ny, tile.imgwidth, tile.imgheight, bgcolor); + } + } + } + else if(tile.width * tile.imgwidth + x0 >= 0){ + y0 = (y0 > 0) ? y0 : 0; + y1 = (tile.y + tile.height * tile.imgheight < 128) ? tile.y + tile.height * tile.imgheight - y0 : 127 - y0; + if(y0 < 127 && y1 > 0) + fillRect(127 - step * 2, y0, step * 2, y1, bgcolor); + } + } + else if(direction == 1){ + tile.y -= step; + x0 = tile.x; + y0 = tile.y; + y = (127 - y0) / tile.imgheight; + ny = y0 + y * tile.imgheight; + if(y < tile.height && y >= 0) + for(x = 0; x < tile.width; x++){ + nx = x0 + x * tile.imgwidth; + if(nx > 0 && nx < 128){ + imgadr = readInt(tile.adr + (x + y * tile.width) * 2); + if(imgadr > 0) + drawImg(imgadr, nx, ny, tile.imgwidth, tile.imgheight); + else + fillRect(nx, ny, tile.imgwidth, tile.imgheight, bgcolor); + } + } + } + else if(direction == 0){ + tile.x += step*2; + x0 = tile.x; + y0 = tile.y; + x = (0 - x0) / tile.imgwidth; + nx = x0 + x * tile.imgwidth; + if(x0 < 0 && x >= 0){ + for(y = 0; y < tile.height; y++){ + ny = y0 + y * tile.imgheight; + if(ny > 0 && ny < 128){ + imgadr = readInt(tile.adr + (x + y * tile.width) * 2); + if(imgadr > 0) + drawImg(imgadr, nx, ny, tile.imgwidth, tile.imgheight); + else + fillRect(nx, ny, tile.imgwidth, tile.imgheight, bgcolor); + } + } + } + else if(x0 < 128){ + y0 = (y0 > 0) ? y0 : 0; + y1 = (tile.y + tile.height * tile.imgheight < 128) ? tile.y + tile.height * tile.imgheight - y0 : 127 - y0; + if(y0 < 127 && y1 > 0) + fillRect(0, y0, step * 2, y1, bgcolor); + } + } + else if(direction == 3){ + tile.y += step; + x0 = tile.x; + y0 = tile.y; + y = (0 - y0) / tile.imgheight; + ny = y0 + y * tile.imgheight; + if(y < tile.height && y >= 0) + for(x = 0; x < tile.width; x++){ + if(nx > 0 && nx < 128){ + imgadr = readInt(tile.adr + (x + y * tile.width) * 2); + if(imgadr > 0) + drawImg(imgadr, nx, ny, tile.imgwidth, tile.imgheight); + else + fillRect(nx, ny, tile.imgwidth, tile.imgheight, bgcolor); + } + } + } + } + +void charLineUp(byte n){ + clearScr(); + for(uint16_t i = 0; i < 336 - n * 21; i++){ + charArray[i] = charArray[i + n * 21]; + putchar(charArray[i], (i % 21) * 6, (i / 21) * 8); + } +} + +void setCharX(int8_t x){ + regx = x; +} + +void setCharY(int8_t y){ + regy = y; +} + +void printc(char c, byte fc, byte bc){ + if(c == '\n'){ + fillRect(regx * 6, regy * 8, 127 - regx * 6, 8, 0); + for(byte i = regx; i <= 21; i++){ + charArray[regx + regy * 21] = ' '; + } + regy++; + regx = 0; + if(regy > 15){ + regy = 15; + charLineUp(1); + } + } + else if(c == '\t'){ + for(byte i = 0; i <= regx % 5; i++){ + fillRect(regx * 6, regy * 8, 6, 8, 0); + charArray[regx + regy * 21] = ' '; + regx++; + if(regx > 21){ + regy++; + regx = 0; + if(regy > 15){ + regy = 15; + charLineUp(1); + } + } + } + } + else{ + fillRect(regx * 6, regy * 8, 6, 8, 0); + putchar(c, regx * 6, regy * 8); + charArray[regx + regy * 21] = c; + regx++; + if(regx > 20){ + regy++; + regx = 0; + if(regy > 15){ + regy = 15; + charLineUp(1); + } + } + } +} + +void setColor(uint8_t c){ + color = c & 0xf; +} + +void fillRect(int8_t x, int8_t y, uint8_t w, uint8_t h, uint8_t c){ + for(int16_t jx = x; jx < x + w; jx++) + for(int16_t jy = y; jy < y + h; jy++) + setPix(jx, jy, c); +} + +void putString(char s[], int8_t y){ + int8_t i = 0; + while(s[i] != 0 && i < 21){ + putchar(s[i], i * 6, y); + i++; + } +} + +void putchar(char c, uint8_t x, uint8_t y) { + for(int8_t i=0; i<5; i++ ) { // Char bitmap = 5 columns + uint8_t line = pgm_read_byte(&font_a[c * 5 + i]); + for(int8_t j=0; j<8; j++, line >>= 1) { + if(line & 1) + setPix(x+i, y+j, color); + } + } +} + diff --git a/file_manager.ino b/file_manager.ino new file mode 100644 index 0000000..2b450c1 --- /dev/null +++ b/file_manager.ino @@ -0,0 +1,89 @@ +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++; + } + 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(); + while(thiskey != 0){ + getKey(); + delay(100); + } + while(thiskey == 0){ + getKey(); + delay(100); + } + if(thiskey & 16){//ok + loadFromSPIFS(thisF); + return; + } + else if(thiskey & 2){//down + if(pos < fileCount - 1) + pos++; + } + else if(thiskey & 1){//up + if(pos > 0) + pos--; + } + if(thiskey & 4)//left + 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 +} + diff --git a/font_a.c b/font_a.c new file mode 100644 index 0000000..c7c1d09 --- /dev/null +++ b/font_a.c @@ -0,0 +1,270 @@ +#ifdef __AVR__ + #include + #include +#elif defined(ESP8266) + #include +#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,0x20,0x41,0x01,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 'я' +}; + diff --git a/keyboard.ino b/keyboard.ino new file mode 100644 index 0000000..3b6afe1 --- /dev/null +++ b/keyboard.ino @@ -0,0 +1,42 @@ +#include + +void geti2cAdress(){ + byte error,address; + i2c_adress=0; + for(address = 1; address < 127; address++ ) + { + Wire.beginTransmission(address); + error = Wire.endTransmission(); + if (error == 0){ + i2c_adress=address; + return; + } + yield(); + } +} + +void getKey(){ + byte dio_in; + Wire.beginTransmission(i2c_adress); + Wire.write(B11111111); //Конфигурация всех портов PCF8574P на клавиатуре как входа + Wire.endTransmission(); + Wire.requestFrom(i2c_adress,1); + dio_in = Wire.read(); //читаем состояние портов PCF8574P(кнопок) + thiskey = 0; + if((dio_in & 128) == 0) + thiskey |= 4; + if((dio_in & 64) == 0) + thiskey |= 8; + if((dio_in & 32) == 0) + thiskey |= 2; + if((dio_in & 16) == 0) + thiskey |= 1; //up + if((dio_in & 8) == 0) + thiskey |= 64; + if((dio_in & 4) == 0) + thiskey |= 32; + if((dio_in & 2) == 0) + thiskey |= 16; + if((dio_in & 1) == 0) + thiskey |= 128; +} diff --git a/libraries.7z b/libraries.7z new file mode 100644 index 0000000000000000000000000000000000000000..107962f80bf990079b8accd6e590aaf4d09d0548 GIT binary patch literal 62658 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 z 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%0A{}1c!*s%+tAbhc0 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 literal 0 HcmV?d00001