/ MCUME_esp32 / espboot / main / emuapi.cpp
emuapi.cpp
   1  #define KEYMAP_PRESENT 1
   2  
   3  #include <stdint.h>
   4  #include <stdbool.h>
   5  #include <stddef.h>
   6  #include <string.h>
   7  
   8  extern "C" {
   9    #include "emuapi.h"
  10    #include "iopins.h"
  11  }
  12  
  13  #include "ili9341_t3dma.h"
  14  //#include "logo.h"
  15  #include "bmpjoy.h"
  16  #include "bmpvbar.h"
  17  
  18  #include "esp_event.h"
  19  #include "esp_vfs_fat.h"
  20  #include "driver/sdspi_host.h"
  21  #include <dirent.h>
  22  #include <sys/stat.h>
  23  #include <driver/adc.h>
  24  
  25  #ifdef HAS_I2CKBD
  26  #ifdef USE_WIRE
  27  #include "Wire.h"
  28  #else
  29  #include <driver/i2c.h>
  30  #define ACK_CHECK_EN          0x1              /*!< I2C master will check ack from slave*/
  31  #define ACK_CHECK_DIS         0x0              /*!< I2C master will not check ack from slave */
  32  #define ACK_VAL               0x0              /*!< I2C ack value */
  33  #define NACK_VAL              0x1              /*!< I2C nack value */
  34  #endif
  35  
  36  #define I2C_FREQ_HZ           400000           /*!< I2C master clock frequency */
  37  static bool i2cKeyboardPresent = false;
  38  #endif
  39  
  40  
  41  extern ILI9341_t3DMA tft;
  42  
  43  static char romspath[64];
  44  static int calMinX=-1,calMinY=-1,calMaxX=-1,calMaxY=-1;
  45  static sdmmc_card_t* card;
  46  const uint16_t deflogo[] = {
  47    0x0000,0x0000
  48  };
  49  static const uint16_t * logo = deflogo;
  50  
  51  #define CALIBRATION_FILE    "/sdcard/cal.cfg"
  52  
  53  #define MAX_FILENAME_SIZE   28
  54  #define MAX_MENULINES       (MKEY_L9)
  55  #define TEXT_HEIGHT         16
  56  #define TEXT_WIDTH          8
  57  #define MENU_FILE_XOFFSET   (6*TEXT_WIDTH)
  58  #define MENU_FILE_YOFFSET   (2*TEXT_HEIGHT)
  59  #define MENU_FILE_W         (MAX_FILENAME_SIZE*TEXT_WIDTH)
  60  #define MENU_FILE_H         (MAX_MENULINES*TEXT_HEIGHT)
  61  #define MENU_FILE_FGCOLOR   RGBVAL16(0xff,0xff,0xff)
  62  #define MENU_FILE_BGCOLOR   RGBVAL16(0x00,0x00,0x20)
  63  #define MENU_JOYS_YOFFSET   (12*TEXT_HEIGHT)
  64  #define MENU_VBAR_XOFFSET   (0*TEXT_WIDTH)
  65  #define MENU_VBAR_YOFFSET   (MENU_FILE_YOFFSET)
  66  
  67  
  68  #define MKEY_L1             1
  69  #define MKEY_L2             2
  70  #define MKEY_L3             3
  71  #define MKEY_L4             4
  72  #define MKEY_L5             5
  73  #define MKEY_L6             6
  74  #define MKEY_L7             7
  75  #define MKEY_L8             8
  76  #define MKEY_L9             9
  77  #define MKEY_UP             20
  78  #define MKEY_DOWN           21
  79  #define MKEY_JOY            22
  80  
  81  
  82  const unsigned short menutouchareas[] = {
  83    TAREA_XY,MENU_FILE_XOFFSET,MENU_FILE_YOFFSET,
  84    TAREA_WH,MENU_FILE_W, TEXT_HEIGHT,
  85    TAREA_NEW_COL,TEXT_HEIGHT,TEXT_HEIGHT,TEXT_HEIGHT,TEXT_HEIGHT,TEXT_HEIGHT,TEXT_HEIGHT,TEXT_HEIGHT,TEXT_HEIGHT,TEXT_HEIGHT,
  86    
  87    TAREA_XY,MENU_VBAR_XOFFSET,MENU_VBAR_YOFFSET,
  88    TAREA_WH,32,48,
  89    TAREA_NEW_COL, 72,72,8,40,
  90      
  91    TAREA_END};
  92     
  93  const unsigned short menutouchactions[] = {
  94    MKEY_L1,MKEY_L2,MKEY_L3,MKEY_L4,MKEY_L5,MKEY_L6,MKEY_L7,MKEY_L8,MKEY_L9,
  95    MKEY_UP,MKEY_DOWN,ACTION_NONE,MKEY_JOY}; 
  96  
  97    
  98  static bool menuOn=true;
  99  static bool callibrationOn=false;
 100  static int callibrationStep=0;
 101  static bool menuRedraw=true;
 102  static int nbFiles=0;
 103  static int curFile=0;
 104  static int topFile=0;
 105  static char selection[MAX_FILENAME_SIZE+1]="";
 106  static uint8_t prev_zt=0; 
 107  
 108  static int readNbFiles(void) {
 109    int totalFiles = 0;
 110  
 111    DIR* dir = opendir(romspath);
 112    while (true) {
 113      struct dirent* de = readdir(dir);
 114      if (!de) {
 115        // no more files
 116        break;
 117      }    
 118      if (de->d_type == DT_REG) {
 119        totalFiles++;
 120      }
 121      else if (de->d_type == DT_DIR) {
 122        if ( (strcmp(de->d_name,".")) && (strcmp(de->d_name,"..")) ) {
 123          totalFiles++;
 124        }
 125      }  
 126    }
 127    closedir(dir);
 128    printf("Directory read: %d files",totalFiles);
 129    return totalFiles;  
 130  }
 131  
 132  static char captureTouchZone(const unsigned short * areas, const unsigned short * actions, int *rx, int *ry, int *rw, int * rh) {
 133      uint16_t xt=0;
 134      uint16_t yt=0;
 135      uint16_t zt=0;
 136      bool hDir=true;  
 137    
 138      if (tft.isTouching())
 139      {
 140          if (prev_zt == 0) {
 141              prev_zt =1;
 142              tft.readCal(&xt,&yt,&zt);
 143              if (zt<1000) {
 144                prev_zt=0; 
 145                return ACTION_NONE;
 146              }
 147              int i=0;
 148              int k=0;
 149              int y2=0, y1=0;
 150              int x2=0, x1=0;
 151              int x=KEYBOARD_X,y=KEYBOARD_Y;
 152              int w=TAREA_W_DEF,h=TAREA_H_DEF;
 153              uint8_t s;
 154              while ( (s=areas[i++]) != TAREA_END ) {
 155                  if (s == TAREA_XY) {
 156                      x = areas[i++];
 157                      y = areas[i++];                    
 158                      x2 = x;
 159                      y2 = y;  
 160                  }
 161                  else if (s == TAREA_WH) {
 162                      w = areas[i++];
 163                      h = areas[i++];
 164                  }                     
 165                  else if (s == TAREA_NEW_ROW) {
 166                    hDir = true;
 167                    y1 = y2;
 168                    y2 = y1 + h;
 169                    x2 = x;
 170                  }  
 171                  else if (s == TAREA_NEW_COL) {
 172                    hDir = false;
 173                    x1 = x2;
 174                    x2 = x1 + w;
 175                    y2 = y;                  
 176                  }
 177                  else { 
 178                      if (hDir) {
 179                        x1 = x2;
 180                        x2 = x1+s;                                                            
 181                      } else {
 182                        y1 = y2;
 183                        y2 = y1+s;                      
 184                      }
 185                      if ( (yt >= y1) && (yt < y2) && (xt >= x1) && (xt < x2)  ) {
 186                          *rx = x1;
 187                          *ry = y1;
 188                          *rw = x2-x1;
 189                          *rh = y2-y1;
 190                          return (actions[k]);  
 191                      }
 192                      k++;
 193                  }                
 194              }
 195          } 
 196          prev_zt =1; 
 197      } else {
 198          prev_zt=0; 
 199      } 
 200    
 201      return ACTION_NONE;   
 202  } 
 203  
 204  void toggleMenu(bool on) {
 205    if (on) {
 206      callibrationOn=false;
 207      menuOn=true;
 208      menuRedraw=true;  
 209      tft.fillScreenNoDma(RGBVAL16(0x00,0x00,0x00));
 210      tft.drawTextNoDma(0,0, TITLE, RGBVAL16(0x00,0xff,0xff), RGBVAL16(0x00,0x00,0xff), true);  
 211      tft.drawSpriteNoDma(MENU_VBAR_XOFFSET,MENU_VBAR_YOFFSET,(uint16_t*)bmpvbar);
 212    } else {
 213      menuOn = false;    
 214    }
 215  }
 216  
 217  
 218  static void callibrationInit(void) 
 219  {
 220    callibrationOn=true;
 221    menuOn=false;
 222    callibrationStep = 0;
 223    calMinX=0,calMinY=0,calMaxX=0,calMaxY=0;
 224    tft.fillScreenNoDma(RGBVAL16(0xff,0xff,0xff));
 225    tft.drawTextNoDma(0,100, "          Callibration process:", RGBVAL16(0x00,0x00,0x00), RGBVAL16(0xff,0xff,0xff), true);
 226    tft.drawTextNoDma(0,116, "     Hit the red cross at each corner", RGBVAL16(0x00,0x00,0x00), RGBVAL16(0xff,0xff,0xff), true);
 227    tft.drawTextNoDma(0,0, "+", RGBVAL16(0xff,0x00,0x00), RGBVAL16(0xff,0xff,0xff), true);
 228    prev_zt = 1;  
 229  }
 230  
 231  static void readCallibration(void) 
 232  {
 233    FILE * file = fopen(CALIBRATION_FILE, "rb");
 234    if (file) {
 235      fscanf(file,"%d %d %d %d\n",&calMinX,&calMinY,&calMaxX,&calMaxY);
 236      fclose(file);
 237      printf("Current callibration params: %d %d %d %d\n",calMinX,calMinY,calMaxX,calMaxY);                
 238    }
 239    else {
 240      printf("Callibration read error\n");
 241    }  
 242    tft.callibrateTouch(calMinX,calMinY,calMaxX,calMaxY);   
 243  }
 244  
 245  static void writeCallibration(void) 
 246  {
 247    tft.callibrateTouch(calMinX,calMinY,calMaxX,calMaxY);
 248    FILE * file = fopen(CALIBRATION_FILE, "wb");
 249    if (file) {
 250      fprintf(file,"%d %d %d %d\n",calMinX,calMinY,calMaxX,calMaxY);
 251      fclose(file);
 252    }
 253    else {
 254      printf("Callibration write error\n");
 255    }  
 256  }
 257  
 258  
 259  bool callibrationActive(void) 
 260  {
 261    return (callibrationOn);
 262  }
 263  
 264  
 265  
 266  int handleCallibration(uint16_t bClick) {
 267    uint16_t xt=0;
 268    uint16_t yt=0;
 269    uint16_t zt=0;  
 270    if (tft.isTouching()) {
 271      if (prev_zt == 0) {
 272        prev_zt = 1;
 273        tft.readRaw(&xt,&yt,&zt);
 274        if (zt < 1000) {
 275          return 0;
 276        }
 277        switch (callibrationStep) 
 278        {
 279          case 0:
 280            callibrationStep++;
 281            tft.drawTextNoDma(0,0, " ", RGBVAL16(0xff,0xff,0xff), RGBVAL16(0xff,0xff,0xff), true);
 282            tft.drawTextNoDma(ILI9341_TFTREALWIDTH-8,0, "+", RGBVAL16(0xff,0x00,0x00), RGBVAL16(0xff,0xff,0xff), true);
 283            calMinX += xt;
 284            calMinY += yt;          
 285            break;
 286          case 1:
 287            callibrationStep++;
 288            tft.drawTextNoDma(ILI9341_TFTREALWIDTH-8,0, " ", RGBVAL16(0xff,0xff,0xff), RGBVAL16(0xff,0xff,0xff), true);
 289            tft.drawTextNoDma(ILI9341_TFTREALWIDTH-8,ILI9341_TFTREALHEIGHT-16, "+", RGBVAL16(0xff,0x00,0x00), RGBVAL16(0xff,0xff,0xff), true);
 290            calMaxX += xt;
 291            calMinY += yt;           
 292            break;
 293          case 2:
 294            callibrationStep++;
 295            tft.drawTextNoDma(ILI9341_TFTREALWIDTH-8,ILI9341_TFTREALHEIGHT-16, " ", RGBVAL16(0xff,0xff,0xff), RGBVAL16(0xff,0xff,0xff), true);
 296            tft.drawTextNoDma(0,ILI9341_TFTREALHEIGHT-16, "+", RGBVAL16(0xff,0x00,0x00), RGBVAL16(0xff,0xff,0xff), true);
 297            calMaxX += xt;
 298            calMaxY += yt;
 299            break;
 300          case 3:
 301            tft.fillScreenNoDma(RGBVAL16(0xff,0xff,0xff));
 302            tft.drawTextNoDma(0,100, "          Callibration done!", RGBVAL16(0x00,0x00,0x00), RGBVAL16(0xff,0xff,0xff), true);
 303            tft.drawTextNoDma(0,116, "        (Click center to exit)", RGBVAL16(0xff,0x00,0x00), RGBVAL16(0xff,0xff,0xff), true);           
 304            callibrationStep++;
 305            calMinX += xt;
 306            calMaxY += yt;       
 307            break;                 
 308          case 4:
 309            if ( (xt > (ILI9341_TFTREALWIDTH/4)) && (xt < (ILI9341_TFTREALWIDTH*3)/4) 
 310              && (yt > (ILI9341_TFTREALHEIGHT/4)) && (yt < (ILI9341_TFTREALHEIGHT*3)/4) ) {
 311              calMinX /= 2;
 312              calMinY /= 2;
 313              calMaxX /= 2;
 314              calMaxY /= 2;
 315              writeCallibration();                       
 316              toggleMenu(true);
 317            }
 318            else {
 319              callibrationInit();              
 320            }
 321            break; 
 322                             
 323        }
 324        vTaskDelay(100 / portTICK_PERIOD_MS); 
 325      }  
 326    }
 327    else {
 328      prev_zt = 0;
 329    } 
 330    return 1; 
 331  }
 332  
 333  
 334  
 335  bool menuActive(void) 
 336  {
 337    return (menuOn);
 338  }
 339  
 340  int handleMenu(uint16_t bClick)
 341  {
 342    int action = ACTION_NONE;
 343  
 344    char newpath[80];
 345    strcpy(newpath, romspath);
 346    strcat(newpath, "/");
 347    strcat(newpath, selection);
 348    
 349    struct stat st;
 350    bool newPathIsDir = false;
 351    if(stat(newpath,&st) == 0)
 352      if((st.st_mode & S_IFDIR) != 0)
 353        newPathIsDir = true;
 354  
 355    int rx=0,ry=0,rw=0,rh=0;
 356    char c = captureTouchZone(menutouchareas, menutouchactions, &rx,&ry,&rw,&rh);
 357    if ( ( (bClick & MASK_JOY2_BTN) || (bClick & MASK_KEY_USER1) )  && (newPathIsDir) ) {
 358        menuRedraw=true;
 359        strcpy(romspath,newpath);
 360        curFile = 0;
 361        nbFiles = readNbFiles();     
 362    }
 363    else if ( (c >= MKEY_L1) && (c <= MKEY_L9) ) {
 364      if ( (topFile+(int)c-1) <= (nbFiles-1)  )
 365      {
 366        curFile = topFile + (int)c -1;
 367        menuRedraw=true;
 368       //tft.drawRectNoDma( rx,ry,rw,rh, KEYBOARD_HIT_COLOR );
 369      }
 370    }
 371    else if ( (bClick & MASK_JOY2_BTN) ) {
 372        menuRedraw=true;
 373        action = ACTION_RUNTFT;       
 374    }
 375    else if (bClick & MASK_JOY2_UP) {
 376      if (curFile!=0) {
 377        menuRedraw=true;
 378        curFile--;
 379      }
 380    }
 381    else if ( (bClick & MASK_JOY2_RIGHT) || (c == MKEY_UP) ) {
 382      if ((curFile-9)>=0) {
 383        menuRedraw=true;
 384        curFile -= 9;
 385      } else if (curFile!=0) {
 386        menuRedraw=true;
 387        curFile--;
 388      }
 389    }  
 390    else if (bClick & MASK_JOY2_DOWN)  {
 391      if ((curFile<(nbFiles-1)) && (nbFiles)) {
 392        curFile++;
 393        menuRedraw=true;
 394      }
 395    }
 396    else if ( (bClick & MASK_JOY2_LEFT) || (c == MKEY_DOWN) ) {
 397      if ((curFile<(nbFiles-9)) && (nbFiles)) {
 398        curFile += 9;
 399        menuRedraw=true;
 400      }
 401      else if ((curFile<(nbFiles-1)) && (nbFiles)) {
 402        curFile++;
 403        menuRedraw=true;
 404      }
 405    }
 406    else if ( (bClick & MASK_KEY_USER1) || (c == MKEY_JOY) ) {
 407      emu_SwapJoysticks(0);
 408      menuRedraw=true;  
 409    }   
 410  
 411      
 412    if (menuRedraw && nbFiles) {
 413           
 414      int fileIndex = 0;
 415      DIR* dir = opendir(romspath);
 416      tft.drawRectNoDma(MENU_FILE_XOFFSET,MENU_FILE_YOFFSET, MENU_FILE_W, MENU_FILE_H, MENU_FILE_BGCOLOR);
 417  //    if (curFile <= (MAX_MENULINES/2-1)) topFile=0;
 418  //    else topFile=curFile-(MAX_MENULINES/2);
 419      if (curFile <= (MAX_MENULINES-1)) topFile=0;
 420      else topFile=curFile-(MAX_MENULINES/2);
 421      
 422      int i=0;
 423      while (i<MAX_MENULINES) {
 424        struct dirent* de = readdir(dir);
 425        if (!de) {
 426          break;
 427        }     
 428        if ( (de->d_type == DT_REG) || ((de->d_type == DT_DIR) && (strcmp(de->d_name,".")) && (strcmp(de->d_name,"..")) ) ) {
 429          if (fileIndex >= topFile) {              
 430            if ((i+topFile) < nbFiles ) {
 431              if ((i+topFile)==curFile) {
 432                tft.drawTextNoDma(MENU_FILE_XOFFSET,i*TEXT_HEIGHT+MENU_FILE_YOFFSET, de->d_name, RGBVAL16(0xff,0xff,0x00), RGBVAL16(0xff,0x00,0x00), true);
 433                strcpy(selection,de->d_name);            
 434              }
 435              else {
 436                tft.drawTextNoDma(MENU_FILE_XOFFSET,i*TEXT_HEIGHT+MENU_FILE_YOFFSET, de->d_name, MENU_FILE_FGCOLOR, MENU_FILE_BGCOLOR, true);      
 437              }
 438            }
 439            i++; 
 440          }
 441          fileIndex++;    
 442        }
 443      }
 444      closedir(dir);
 445      
 446      tft.drawSpriteNoDma(0,MENU_JOYS_YOFFSET,(uint16_t*)bmpjoy);  
 447      tft.drawTextNoDma(48,MENU_JOYS_YOFFSET+8, (emu_SwapJoysticks(1)?(char*)"SWAP=1":(char*)"SWAP=0"), RGBVAL16(0x00,0xff,0xff), RGBVAL16(0xff,0x00,0x00), false);
 448  
 449      menuRedraw=false;     
 450    }
 451  
 452  
 453    return (action);  
 454  }
 455  
 456  char * menuSelection(void)
 457  {
 458    return (selection);  
 459  }
 460    
 461  #ifdef HAS_I2CKBD
 462  #ifdef USE_WIRE
 463  #else
 464  static esp_err_t i2c_master_read_slave_reg(i2c_port_t i2c_num, uint8_t i2c_addr, uint8_t* data_rd, size_t size)
 465  {
 466      if (size == 0) {
 467          return ESP_OK;
 468      }
 469      i2c_cmd_handle_t cmd = i2c_cmd_link_create();
 470      i2c_master_start(cmd);
 471      i2c_master_write_byte(cmd, ( i2c_addr << 1 ) | I2C_MASTER_READ, ACK_CHECK_EN);
 472      if (size > 1) {
 473          i2c_master_read(cmd, data_rd, size - 1, (i2c_ack_type_t)ACK_VAL);
 474      }
 475      i2c_master_read_byte(cmd, data_rd + size - 1, (i2c_ack_type_t)NACK_VAL);
 476      i2c_master_stop(cmd);
 477      esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
 478      i2c_cmd_link_delete(cmd);
 479      return ret;
 480  }
 481  #endif
 482  #endif
 483  
 484  
 485  void emu_init(void)
 486  {
 487  
 488    esp_err_t ret = 0;
 489  
 490    printf("mounting sd...\n");
 491  
 492    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
 493    host.max_freq_khz = 10000;
 494    //host.slot = HSPI_HOST;
 495    
 496    sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
 497    slot_config.gpio_miso = SPIN_NUM_MISO;
 498    slot_config.gpio_mosi = SPIN_NUM_MOSI;
 499    slot_config.gpio_sck  = SPIN_NUM_CLK;
 500    slot_config.gpio_cs = SPIN_NUM_CS;
 501    slot_config.dma_channel = 2;
 502  
 503   
 504    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
 505        .format_if_mount_failed = false,
 506        .max_files = 5,
 507        .allocation_unit_size = 16 * 1024
 508    };
 509    
 510  
 511    while((ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card)) != ESP_OK) {
 512      printf("retrying\n");
 513      vTaskDelay(500 / portTICK_PERIOD_MS);   
 514    }
 515  
 516    strcpy(romspath,"/sdcard/");
 517    strcat(romspath,ROMSDIR);
 518    printf("dir is : %s\n",romspath);
 519  
 520    nbFiles = readNbFiles(); 
 521    printf("SD initialized, files found: %d\n",nbFiles);
 522  
 523   
 524    tft.touchBegin();
 525    //uint16_t xt=0;
 526    //uint16_t yt=0;
 527    //uint16_t zt=0;  
 528    //tft.readRo(&xt,&yt,&zt);
 529  
 530  
 531    emu_InitJoysticks();
 532    readCallibration();
 533    
 534    if ((tft.isTouching()) || (emu_ReadKeys() & MASK_JOY2_BTN) ) {
 535      callibrationInit();
 536    } else  {
 537      toggleMenu(true);
 538    }
 539  
 540  #ifdef HAS_I2CKBD
 541    uint8_t msg[7]={0,0,0,0,0,0,0};
 542    
 543  #ifdef USE_WIRE
 544    Wire.begin(I2C_SDA_IO, I2C_SCL_IO);
 545    Wire.requestFrom(8, 7, I2C_FREQ_HZ);  // request 5 bytes from slave device #8 
 546    int i = 0;
 547    int hitindex=-1;
 548    while (Wire.available() && (i<7) ) { // slave may send less than requested
 549      uint8_t b = Wire.read(); // receive a byte
 550      if (b != 0xff) hitindex=i; 
 551      msg[i++] = b;        
 552    }  
 553  #else
 554    int i2c_master_port = I2C_NUM_1;
 555    i2c_config_t conf;
 556    conf.mode = I2C_MODE_MASTER;
 557    conf.sda_io_num = I2C_SDA_IO;
 558    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
 559    conf.scl_io_num = I2C_SCL_IO;
 560    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
 561    conf.master.clk_speed = I2C_FREQ_HZ;
 562    i2c_param_config((i2c_port_t)i2c_master_port, &conf);
 563    if (i2c_driver_install((i2c_port_t)i2c_master_port, conf.mode,0, 0, 0) != ESP_OK)
 564      printf("I2C Failed initialized\n");
 565    
 566    if (i2c_master_read_slave_reg( I2C_NUM_1, 8, &msg[0], 7 ) != ESP_OK) 
 567        printf("I2C Failed \n");    
 568  #endif  
 569  
 570  
 571  
 572  
 573    if ( (msg[0] == 0xff) && (msg[1] == 0xff) && 
 574         (msg[2] == 0xff) && (msg[3] == 0xff) && 
 575         (msg[4] == 0xff) && (msg[5] == 0xff) && (msg[6] == 0xff)) {
 576      i2cKeyboardPresent = true;
 577      printf("i2C keyboard found\n");            
 578    }
 579  #endif 
 580  }
 581  
 582  
 583  void emu_printf(char * text)
 584  {
 585    printf("%s\n",text);
 586  }
 587  
 588  
 589  void emu_printi(int val)
 590  {
 591    printf("%d\n",val);
 592  }
 593  
 594  void * emu_Malloc(int size)
 595  {
 596    void * retval =  malloc(size);
 597    if (!retval) {
 598      printf("failled to allocate %d\n",size);
 599    }
 600    else {
 601      printf("could allocate %d\n",size); 
 602    }
 603    
 604    return retval;
 605  }
 606  
 607  void emu_Free(void * pt)
 608  {
 609    free(pt);
 610  }
 611  
 612  
 613  static FILE * lastfileOpened;
 614  
 615  
 616  int emu_FileOpen(char * filename)
 617  {
 618    int retval = 0;
 619  
 620    char filepath[80];
 621    strcpy(filepath, romspath);
 622    strcat(filepath, "/");
 623    strcat(filepath, filename);
 624    //printf("FileOpen...%s\n",filepath);
 625      
 626    lastfileOpened = fopen(filepath, "rb");
 627    if (lastfileOpened) {
 628      retval = 1;  
 629    }
 630    else {
 631      //printf("FileOpen failed\n");
 632    }
 633    return (retval);
 634  }
 635  
 636  int emu_FileRead(char * buf, int size)
 637  {
 638    int retval = fread(buf, 1, size, lastfileOpened);
 639    if (retval != size) {
 640      printf("FileRead failed\n");
 641    }
 642    return (retval);     
 643  }
 644  
 645  unsigned char emu_FileGetc(void) {
 646    unsigned char c;
 647    int retval = fread(&c, 1, 1, lastfileOpened);
 648    if (retval != 1) {
 649      printf("emu_FileGetc failed\n");
 650    }  
 651    return c; 
 652  }
 653  
 654  
 655  void emu_FileClose(void)
 656  {
 657    fclose(lastfileOpened);  
 658  }
 659  
 660  int emu_FileSize(char * filename) 
 661  {
 662    int filesize=0;
 663    char filepath[80];
 664    strcpy(filepath, romspath);
 665    strcat(filepath, "/");
 666    strcat(filepath, filename);
 667    printf("FileSize...%s\n",filepath);
 668  
 669    FILE * file = fopen(filepath, "rb");
 670    if (file) {
 671      fseek(file, 0L, SEEK_END);
 672      filesize = ftell(file);
 673      //fseek(file, 0L, SEEK_SET);
 674      printf("filesize is...%d\n",filesize);    
 675      fclose(file);    
 676    }
 677   
 678    return(filesize);  
 679  }
 680  
 681  int emu_FileSeek(int seek) 
 682  {
 683    fseek(lastfileOpened, seek, SEEK_SET);     
 684    return (seek);
 685  }
 686  
 687  int emu_LoadFile(char * filename, char * buf, int size)
 688  {
 689    int filesize = 0;
 690      
 691    char filepath[80];
 692    strcpy(filepath, romspath);
 693    strcat(filepath, "/");
 694    strcat(filepath, filename);
 695    printf("LoadFile...%s\n",filepath);  
 696  
 697    filesize = emu_FileSize(filename);
 698    FILE * file = fopen(filepath, "rb");
 699    if (file) {
 700      if (size >= filesize)
 701      {
 702        if (fread(buf, 1, filesize, file) != filesize) {
 703          printf("File read failed\n");
 704        }        
 705      }
 706      fclose(file);
 707    }
 708    
 709    return(filesize);
 710  }
 711  
 712  int emu_LoadFileSeek(char * filename, char * buf, int size, int seek)
 713  {
 714    int filesize = 0;
 715      
 716    char filepath[80];
 717    strcpy(filepath, romspath);
 718    strcat(filepath, "/");
 719    strcat(filepath, filename);
 720    printf("LoadFileSeek...%d bytes at %d from %s\n",size,seek,filepath); 
 721  
 722    FILE * file = fopen(filepath, "rb");
 723    if (file) {
 724      fseek(file, seek, SEEK_SET);       
 725      if (fread(buf, size, 1, file) != size) {
 726        printf("File read failed\n");
 727      }        
 728      fclose(file);
 729    }
 730    
 731    return(filesize);
 732  }
 733  
 734  static int keypadval=0; 
 735  static bool joySwapped = false;
 736  static uint16_t bLastState;
 737  static int xRef;
 738  static int yRef;
 739  
 740  int emu_ReadAnalogJoyX(int min, int max) 
 741  {
 742    int val; //adc1_get_raw((adc1_channel_t)PIN_JOY2_A1X);  
 743    adc2_get_raw((adc2_channel_t)PIN_JOY2_A1X, ADC_WIDTH_BIT_12,&val);
 744    //printf("refX:%d X:%d\n",xRef,val); 
 745    val = val-xRef;
 746    //val = ((val*140)/100);
 747    if ( (val > -xRef/4) && (val < xRef/4) ) val = 0;
 748  #if INVX
 749    val = xRef-val;
 750  #else
 751    val = val+xRef;
 752  #endif  
 753  
 754    return (val*(max-min))/(xRef*2);
 755  }
 756  
 757  int emu_ReadAnalogJoyY(int min, int max) 
 758  {
 759    int val; //= adc1_get_raw((adc1_channel_t)PIN_JOY2_A2Y);
 760    adc2_get_raw((adc2_channel_t)PIN_JOY2_A2Y, ADC_WIDTH_BIT_12,&val);
 761    //printf("refY:%d Y:%d\n",yRef,val); 
 762    val = val-yRef;
 763    //val = ((val*120)/100);
 764    if ( (val > -yRef/4) && (val < yRef/4) ) val = 0;
 765  #if INVY
 766    val = yRef-val;
 767  #else
 768    val = val+yRef;
 769  #endif  
 770    return (val*(max-min))/(yRef*2);
 771  }
 772  
 773  
 774  static uint16_t readAnalogJoystick(void)
 775  {
 776    uint16_t joysval = 0;
 777  
 778    int xReading = emu_ReadAnalogJoyX(0,256);
 779    if (xReading > 128) joysval |= MASK_JOY2_LEFT;
 780    else if (xReading < 128) joysval |= MASK_JOY2_RIGHT;
 781    
 782    int yReading = emu_ReadAnalogJoyY(0,256);
 783    if (yReading < 128) joysval |= MASK_JOY2_UP;
 784    else if (yReading > 128) joysval |= MASK_JOY2_DOWN;
 785    
 786    joysval |= ((gpio_get_level((gpio_num_t)PIN_JOY2_BTN) == 1) ? 0 : MASK_JOY2_BTN);
 787  
 788    return (joysval);     
 789  }
 790  
 791  
 792  int emu_SwapJoysticks(int statusOnly) {
 793    if (!statusOnly) {
 794      if (joySwapped) {
 795        joySwapped = false;
 796      }
 797      else {
 798        joySwapped = true;
 799      }
 800    }
 801    return(joySwapped?1:0);
 802  }
 803  
 804  int emu_GetPad(void) 
 805  {
 806    return(keypadval|((joySwapped?1:0)<<7));
 807  }
 808  
 809  int emu_ReadKeys(void) 
 810  {
 811    uint16_t retval;
 812    uint16_t j1 = readAnalogJoystick();
 813  
 814    uint16_t j2 = 0;
 815  
 816    if (joySwapped) {
 817      retval = ((j1 << 8) | j2);
 818    }
 819    else {
 820      retval = ((j2 << 8) | j1);
 821    }
 822  
 823    if (gpio_get_level((gpio_num_t)PIN_KEY_USER1) == 0 ) retval |= MASK_KEY_USER1;
 824    if (gpio_get_level((gpio_num_t)PIN_KEY_USER2) == 0 ) retval |= MASK_KEY_USER2;
 825  
 826    //printf("%d\n",retval);   
 827    return (retval);
 828  }
 829  
 830  unsigned short emu_DebounceLocalKeys(void)
 831  {  
 832    uint16_t bCurState = emu_ReadKeys();
 833    uint16_t bClick = bCurState & ~bLastState;
 834    bLastState = bCurState;
 835  
 836    return (bClick);
 837  }
 838  
 839  
 840  int emu_ReadI2CKeyboard(void) {
 841    int retval=0;
 842  #ifdef HAS_I2CKBD 
 843    if (i2cKeyboardPresent) {
 844      uint8_t msg[7]; 
 845  #ifdef USE_WIRE
 846      Wire.requestFrom(8, 7, I2C_FREQ_HZ);    // request 5 bytes from slave device #8 
 847      int i = 0;
 848      int hitindex=-1;
 849      while (Wire.available() && (i<7) ) { // slave may send less than requested
 850        uint8_t b = Wire.read(); // receive a byte
 851        if (b != 0xff) hitindex=i; 
 852        msg[i++] = b;        
 853      } 
 854  #else
 855      if (i2c_master_read_slave_reg( I2C_NUM_1, 8, &msg[0], 7 ) != ESP_OK) 
 856        printf("I2C Failed \n");
 857      int hitindex=-1;
 858      int i = 0;
 859      while (i<7) {
 860        if (msg[i] != 0xff) hitindex=i;
 861        i++;
 862      }
 863  #endif     
 864      //printf("I2C 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
 865      //  msg[0],msg[1],msg[2],msg[3],msg[4],msg[5],msg[6]);  
 866      if ((hitindex >=0 ) && (hitindex <=6 )) {
 867        unsigned short match = ((~msg[hitindex])&0x00FF) | (hitindex<<8);
 868        for (i=0; i<sizeof(i2ckeys); i++) {
 869          if (match == i2ckeys[i]) {
 870            //printf("I2C %d\n",keys[i]);          
 871            return (keys[i]);
 872          }
 873        }
 874      }    
 875    }
 876  #endif
 877    return(retval);
 878  }
 879  
 880  void emu_InitJoysticks(void) {  
 881    gpio_set_direction((gpio_num_t)PIN_JOY2_BTN, GPIO_MODE_INPUT);
 882    gpio_set_pull_mode((gpio_num_t)PIN_JOY2_BTN, GPIO_PULLUP_ONLY);
 883    gpio_set_direction((gpio_num_t)PIN_KEY_USER1, GPIO_MODE_INPUT);
 884    gpio_set_pull_mode((gpio_num_t)PIN_KEY_USER1, GPIO_PULLUP_ONLY);
 885    gpio_set_direction((gpio_num_t)PIN_KEY_USER2, GPIO_MODE_INPUT);
 886    gpio_set_pull_mode((gpio_num_t)PIN_KEY_USER2, GPIO_PULLUP_ONLY);
 887  
 888    //adc1_config_channel_atten((adc1_channel_t)PIN_JOY2_A1X,ADC_ATTEN_DB_11);
 889    //adc1_config_channel_atten((adc1_channel_t)PIN_JOY2_A2Y,ADC_ATTEN_DB_11);
 890    adc2_config_channel_atten((adc2_channel_t)PIN_JOY2_A1X,ADC_ATTEN_DB_11);
 891    adc2_config_channel_atten((adc2_channel_t)PIN_JOY2_A2Y,ADC_ATTEN_DB_11);
 892    xRef=0; yRef=0;
 893    for (int i=0; i<10; i++) {
 894      int val;
 895      adc2_get_raw((adc2_channel_t)PIN_JOY2_A1X, ADC_WIDTH_BIT_12, &val);
 896      //val = adc1_get_raw((adc1_channel_t)PIN_JOY2_A1X);
 897      xRef += val;
 898      adc2_get_raw((adc2_channel_t)PIN_JOY2_A2Y,ADC_WIDTH_BIT_12, &val);
 899      //val = adc1_get_raw((adc1_channel_t)PIN_JOY2_A2Y); 
 900      yRef += val;
 901      vTaskDelay(20 / portTICK_PERIOD_MS); 
 902    }
 903    xRef /= 10;
 904    yRef /= 10;
 905  
 906    printf("refs: %d %d\n",xRef,yRef); 
 907  }
 908  
 909  
 910  
 911  static bool vkbKeepOn = false;
 912  static bool vkbActive = false;
 913  static bool vkeyRefresh=false;
 914  static bool exitVkbd = false;
 915  static uint8_t keyPressCount=0; 
 916  
 917  
 918  bool virtualkeyboardIsActive(void) {
 919      return (vkbActive);
 920  }
 921  
 922  void toggleVirtualkeyboard(bool keepOn) {     
 923      if (keepOn) {      
 924          tft.drawSpriteNoDma(0,0,(uint16_t*)logo);
 925          //prev_zt = 0;
 926          vkbKeepOn = true;
 927          vkbActive = true;
 928          exitVkbd = false;  
 929      }
 930      else {
 931          vkbKeepOn = false;
 932          if ( (vkbActive) /*|| (exitVkbd)*/ ) {
 933              tft.fillScreenNoDma( RGBVAL16(0x00,0x00,0x00) );
 934  #ifdef DMA_FULLgpio_get_level
 935              tft.begin();
 936              tft.refresh();
 937  #endif                        
 938              //prev_zt = 0; 
 939              vkbActive = false;
 940              exitVkbd = false;
 941          }
 942          else {
 943  #ifdef DMA_FULL          
 944              tft.stop();
 945              tft.begin();      
 946              tft.start();
 947  #endif                       
 948              tft.drawSpriteNoDma(0,0,(uint16_t*)logo);           
 949              //prev_zt = 0;
 950              vkbActive = true;
 951              exitVkbd = false;
 952          }
 953      }   
 954  }
 955  
 956   
 957  void handleVirtualkeyboard() {
 958    int rx=0,ry=0,rw=0,rh=0;
 959  
 960      if (keyPressCount == 0) {
 961        keypadval = 0;      
 962      } else {
 963        keyPressCount--;
 964      }
 965  
 966      if ( (!virtualkeyboardIsActive()) && (tft.isTouching()) && (!keyPressCount) ) {
 967          toggleVirtualkeyboard(false);
 968          return;
 969      }
 970      
 971      if ( ( (vkbKeepOn) || (virtualkeyboardIsActive())  )  ) {
 972          char c = captureTouchZone(keysw, keys, &rx,&ry,&rw,&rh);
 973          if (c) {
 974              tft.drawRectNoDma( rx,ry,rw,rh, KEYBOARD_HIT_COLOR );
 975              if ( (c >=1) && (c <= ACTION_MAXKBDVAL) ) {
 976                keypadval = c;
 977                keyPressCount = 10;
 978                vTaskDelay(50 / portTICK_PERIOD_MS); 
 979                vkeyRefresh = true;
 980                exitVkbd = true;
 981              }
 982              else if (c == ACTION_EXITKBD) {
 983                vkeyRefresh = true;
 984                exitVkbd = true;  
 985              }
 986          }   
 987       }    
 988       
 989      if (vkeyRefresh) {
 990          vkeyRefresh = false;
 991          tft.drawSpriteNoDma(0,0,(uint16_t*)logo, rx, ry, rw, rh);
 992      }  
 993           
 994      if ( (exitVkbd) && (vkbActive) ) {      
 995          if (!vkbKeepOn) {             
 996              toggleVirtualkeyboard(false);
 997          }
 998          else {         
 999              toggleVirtualkeyboard(true);           
1000          } 
1001      }     
1002  }
1003  
1004  int emu_setKeymap(int index) {
1005    if (index) {
1006      //logo = ;
1007      //keysw = ;      
1008    }
1009    else {
1010      //logo = ;
1011      //keysw = ;  
1012    }
1013    return 0;
1014  }
1015  
1016  
1017  
1018  static unsigned short palette16[PALETTE_SIZE];
1019  static int fskip=0;
1020  
1021  void emu_SetPaletteEntry(unsigned char r, unsigned char g, unsigned char b, int index)
1022  {
1023    if (index<PALETTE_SIZE) {
1024      //printf("%d: %d %d %d\n", index, r,g,b);
1025      palette16[index] = RGBVAL16(r,g,b);    
1026    }
1027  }
1028  
1029  void emu_DrawVsync(void)
1030  {
1031    //printf("sync %d\n",skip);  
1032    fskip += 1;
1033    fskip &= VID_FRAME_SKIP;
1034  }
1035  
1036  void emu_DrawLine(unsigned char * VBuf, int width, int height, int line) 
1037  {
1038    if (fskip==0) {
1039      tft.writeLine(width,height,line, VBuf, palette16);
1040    }
1041  }  
1042  
1043  void emu_DrawScreen(unsigned char * VBuf, int width, int height, int stride) 
1044  {  
1045    if (fskip==0) {
1046      tft.writeScreen(width,height-TFT_VBUFFER_YCROP,stride, VBuf+(TFT_VBUFFER_YCROP/2)*stride, palette16);
1047    }
1048  }
1049  
1050  int emu_FrameSkip(void)
1051  {
1052    return fskip;
1053  }
1054  
1055  void * emu_LineBuffer(int line)
1056  {
1057    return (void*)tft.getLineBuffer(line);
1058  }
1059  
1060  #ifdef HAS_SND
1061  #include "AudioPlaySystem.h"
1062  extern AudioPlaySystem audio;
1063  
1064  void emu_sndInit() {
1065  }
1066  
1067  void emu_sndPlaySound(int chan, int volume, int freq)
1068  {
1069    if (chan < 6) {
1070      audio.sound(chan, freq, volume); 
1071    } 
1072  }
1073  
1074  void emu_sndPlayBuzz(int size, int val) {
1075    //mymixer.buzz(size,val);  
1076  }
1077  #endif
1078