PokittoLib is the library needed for programming the Pokitto DIY game console (www.pokitto.com)

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers PokittoCore.cpp Source File

PokittoCore.cpp

Go to the documentation of this file.
00001 /**************************************************************************/
00002 /*!
00003     @file     PokittoCore.cpp
00004     @author   Jonne Valola
00005 
00006     @section LICENSE
00007 
00008     Software License Agreement (BSD License)
00009 
00010     Copyright (c) 2016, Jonne Valola
00011     All rights reserved.
00012 
00013     Redistribution and use in source and binary forms, with or without
00014     modification, are permitted provided that the following conditions are met:
00015     1. Redistributions of source code must retain the above copyright
00016     notice, this list of conditions and the following disclaimer.
00017     2. Redistributions in binary form must reproduce the above copyright
00018     notice, this list of conditions and the following disclaimer in the
00019     documentation and/or other materials provided with the distribution.
00020     3. Neither the name of the copyright holders nor the
00021     names of its contributors may be used to endorse or promote products
00022     derived from this software without specific prior written permission.
00023 
00024     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
00025     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00026     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00027     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
00028     DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00029     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00031     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00032     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00033     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00034 */
00035 /**************************************************************************/
00036 
00037 #include "Pokitto_settings.h "
00038 #include "PokittoCore.h "
00039 #include "PokittoConsole.h "
00040 #include "PokittoFonts.h "
00041 #include "PokittoTimer.h "
00042 #include "PokittoLogos.h "
00043 #include <stdlib.h>
00044 
00045 #ifndef DISABLEAVRMIN
00046 #define max(a,b) ((a)>(b)?(a):(b))
00047 #endif // DISABLEAVRMIN
00048 
00049 char selectedfile[25];
00050 
00051 //#define F
00052 #ifdef __ARMCC_VERSION
00053 typedef void (*func_t)(void);
00054 #endif
00055 
00056 #ifndef POK_SIM
00057 #include "iap.h"
00058 /** start the user application
00059 * https://community.nxp.com/thread/417695
00060 *
00061 */
00062 void start_application(unsigned long app_link_location){
00063     //asm(" ldr sp, [r0,#0]");
00064     //asm(" ldr pc, [r0,#4]");
00065     //This code is not valid for the Cortex-m0+ instruction set.
00066     // The equivalent for this (as used by the KL26) is
00067 __disable_irq();// Start by disabling interrupts, before changing interrupt vectors
00068 
00069 // delete buttons
00070 //pokDeleteButtons();
00071 
00072 // completely kill button interrupts in preparation for reset
00073 LPC_PINT->IENR = 0;
00074 LPC_PINT->IENF = 0;
00075 
00076 SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk & ~(SysTick_CTRL_ENABLE_Msk); //disable systick
00077 LPC_SYSCON->PDRUNCFG     |=  (1 << 10);       /* Power-down USB PHY         */
00078 LPC_SYSCON->PDRUNCFG     |=  (1 <<  8);       /* Power-down USB PLL         */
00079 
00080 // reset clock source to IRC
00081 LPC_SYSCON->MAINCLKUEN    = 0x01;             /* Update MCLK Clock Source   */
00082 LPC_SYSCON->MAINCLKUEN    = 0x00;             /* Toggle Update Register     */
00083 while (LPC_SYSCON->MAINCLKUEN & 0x01);     /* Wait Until Updated         */
00084 // switch clock selection to IRC
00085 LPC_SYSCON->MAINCLKSEL    = 0;             /* Select Clock Source        */
00086 LPC_SYSCON->MAINCLKUEN    = 0x01;             /* Update MCLK Clock Source   */
00087 LPC_SYSCON->MAINCLKUEN    = 0x00;             /* Toggle Update Register     */
00088 while (LPC_SYSCON->MAINCLKUEN & 0x01);     /* Wait Until Updated         */
00089 //disable PLL clock output
00090 LPC_SYSCON->SYSPLLCLKUEN = 0;
00091 while (LPC_SYSCON->SYSPLLCLKUEN & 0x00);
00092 LPC_SYSCON->SYSPLLCTRL = 0;
00093 
00094 //kill peripherals
00095 LPC_SYSCON->MAINCLKSEL = 0;
00096 LPC_SYSCON->PRESETCTRL = 0; //disable all peripherals
00097 
00098 //power down PLL
00099 volatile uint32_t tmp;
00100 tmp = (LPC_SYSCON->PDRUNCFG & 0x000025FFL);
00101 tmp |= ((1<<7) & 0x000025FFL);
00102 LPC_SYSCON->PDRUNCFG = (tmp | 0x0000C800L); /* Power-down SYSPLL          */
00103 
00104 //Chip_Clock_SetMainClockSource(SYSCTL_MAINCLKSRC_IRC); //switch to IRC
00105 
00106 // clear all gpio states
00107 LPC_GPIO_PORT->PIN[0] = 0;
00108 LPC_GPIO_PORT->PIN[1] = 0;
00109 LPC_GPIO_PORT->PIN[2] = 0;
00110 
00111 SCB->VTOR = app_link_location;//APPL_ADDRESS; /* Change vector table address */
00112 #ifndef __ARMCC_VERSION
00113 asm(" mov r0, %[address]"::[address] "r" (app_link_location));
00114 asm(" ldr r1, [r0,#0]"); // get the stack pointer value from the program's reset vector
00115 asm(" mov sp, r1");      // copy the value to the stack pointer
00116 asm(" ldr r0, [r0,#4]"); // get the program counter value from the program's reset vector
00117 asm(" blx r0");          // jump to the' start address
00118 #else
00119 uint32_t *app_loc = (uint32_t*)app_link_location;
00120 __set_MSP (app_loc[0]);
00121 ((func_t)(app_loc[1]))();
00122 #endif
00123 }
00124 #endif
00125 
00126 // returns a random integar between 0 and maxVal
00127 int random(int maxVal)
00128 {
00129   return random( 0, maxVal);
00130 }
00131 
00132 // returns a random integar between minVal and maxVal
00133 int random(int minVal, int maxVal)
00134 {
00135   // int rand(void); included by default from newlib
00136   #ifdef OLDINCLUSIVERANDOM
00137   return rand() % (maxVal-minVal+1) + minVal;
00138   #else
00139   return rand() % (maxVal-minVal) + minVal;
00140   #endif
00141 }
00142 
00143 using namespace Pokitto;
00144 
00145 bool Core::run_state; // this definition needed
00146 
00147 /** Components */
00148 Backlight Core::backlight;
00149 Buttons Core::buttons;
00150 Battery Core::battery;
00151 #if POK_ENABLE_SOUND > 0
00152 Sound Core::sound;
00153 #endif
00154 Display Core::display;
00155 
00156 //GB Related
00157 uint8_t Core::startMenuTimer;
00158 uint8_t Core::timePerFrame;
00159 uint32_t Core::nextFrameMillis;
00160 uint32_t Core::frameCount;
00161 const char* Core::popupText;
00162 uint8_t Core::popupTimeLeft;
00163 uint16_t Core::frameDurationMicros;
00164 uint32_t Core::frameStartMicros, Core::frameEndMicros;
00165 uint8_t Core::volbar_visible=0;
00166 
00167 uint32_t Core::fps_counter;
00168 bool Core::fps_counter_updated;
00169 uint32_t Core::fps_refreshtime;
00170 uint32_t Core::fps_frameCount;
00171 
00172 Core::Core() {
00173 
00174 }
00175 
00176 
00177 int Core::updateLoader (uint32_t version, uint32_t jumpaddress) {
00178     #ifndef POK_SIM
00179     uint32_t counter=0;
00180     uint8_t data[256];
00181     /** prepare the flash writing **/
00182     //float progress=0;
00183     //int opg=-1;
00184     uint32_t fsize=0;
00185     fileEnd(); //
00186     fsize = fileGetPosition();
00187     if (fsize>0x40000-jumpaddress) fsize = 0x40000-jumpaddress; // shouldn't happen!!
00188     fileRewind();
00189     display.println("PLEASE WAIT");
00190     while (1) {
00191         //if (counter >= fsize-0x200) {
00192         //    display.println("gotcha");
00193         //}
00194         if (counter >= fsize) {
00195                 break;
00196         }
00197         //opg=progress;
00198         if (fileReadBytes(&data[0],0x100)<0x100) {
00199                 if (fsize-counter>0x100) {
00200                         display.println("ERROR READING LOA.DER FILE");
00201                         return 1; // 1 means error
00202                 }
00203         }
00204         if (CopyPageToFlash(jumpaddress+counter,data)) {
00205                     display.println("FLASH WRITE ERROR");
00206                     return 1;
00207         } else {
00208             counter += 0x100;
00209             display.print(".");
00210         }
00211         }
00212         #endif // POK_SIM
00213     return 0; //success
00214 }
00215 
00216 void Core::showWarning() {
00217     display.enableDirectPrinting(true);
00218     display.directbgcolor = COLOR_BLACK;
00219     display.clearLCD();
00220     display.directcolor = COLOR_RED;
00221     display.setFont(fntC64UIGfx);
00222     display.adjustCharStep = 0;
00223     display.adjustLineStep = 0;
00224     display.setCursor(10*8,9);
00225     display.print("WARNING!");
00226     display.directcolor = COLOR_WHITE;
00227     display.setCursor(5*8,4*9);
00228     display.print("LOUD SOUND THROUGH");
00229     display.setCursor(2*8,5*9);
00230     display.print("HEADPHONES COULD DAMAGE");
00231     display.setCursor(7*8,6*9);
00232     display.print("YOUR HEARING.");
00233     display.setCursor(5*8,8*9);
00234     display.print("USE "); display.directcolor = COLOR_GREEN;
00235     display.print("0-100% VOLUME");
00236     display.directcolor = COLOR_WHITE;
00237     display.setCursor(5*8,9*9);
00238     display.print("FOR LISTENING WITH");
00239     display.setCursor(8*8,10*9);
00240     display.print("HEADPHONES");
00241     display.setCursor(5*8,12*9);
00242     display.print("USE "); display.directcolor = COLOR_RED;
00243     display.print("> 100% VOLUME"); display.directcolor = COLOR_GREEN;
00244     display.directcolor = COLOR_WHITE;
00245     display.setCursor(1*8,13*9);
00246     display.print("ONLY FOR LISTENING VIA THE");
00247     display.setCursor(5*8,14*9);
00248     display.print("BUILT-IN SPEAKER");
00249     display.setCursor(5*8,17*9);
00250     display.print("PRESS ");display.directcolor = COLOR_GREEN;
00251     display.print("C ");display.directcolor = COLOR_WHITE;
00252     display.print("TO ACCEPT");
00253     while (!buttons.cBtn()) {
00254         wait(100);
00255     }
00256     display.clearLCD();
00257     display.enableDirectPrinting(false);
00258 }
00259 
00260 void Core::jumpToLoader() {
00261     //display.setFont(font5x7);
00262     //display.adjustCharStep=1;
00263     //display.adjustLineStep=2;
00264     display.fontSize=1;
00265     display.directbgcolor=COLOR_BLACK;
00266     display.directcolor=COLOR_GREEN;
00267     display.clearLCD();
00268     display.setCursor(0,0);
00269     display.enableDirectPrinting(true);
00270     #ifdef POK_SIM
00271     display.println("LOADER IS NOT AVAILABLE ON THE SIMULATOR. PRESS A TO RETURN.");
00272     #else
00273     uint32_t* bootinfo;
00274     uint32_t sdversion=0, sdjump=0;
00275     bool flashloader=false, checkforboot=true;
00276     //check for loa.der on SD card
00277     #if POK_ENABLE_LOADER_UPDATES > 0
00278     pokInitSD();
00279     if (fileOpen("LOA.DER", FILE_MODE_BINARY)==0) {
00280         //LOA.DER found on SD
00281         fileEnd(); // go to end
00282         fileSeekRelative(-8); //rewind 8 bytes
00283         uint32_t* tptr = &sdversion;
00284         fileReadBytes((uint8_t*)tptr,4); //read version number of loader on SD card
00285         tptr = &sdjump;
00286         fileReadBytes((uint8_t*)tptr,4); //read jump address of loader on sd card
00287         fileRewind();
00288     }
00289     #endif
00290     //now start searching for bootkey
00291     while (checkforboot)
00292     {
00293     checkforboot=false; flashloader=false;
00294     bootinfo = (uint32_t*)0x3FFF4;
00295     if (*bootinfo != 0xB007AB1E) bootinfo = (uint32_t*)0x3FF04; //allow couple of alternative locations
00296     if (*bootinfo != 0xB007AB1E) bootinfo = (uint32_t*)0x3FE04; //allow couple of alternative locations
00297     if (*bootinfo != 0xB007AB1E) bootinfo = (uint32_t*)0x3F004; //for futureproofing
00298     if (*bootinfo != 0xB007AB1E) {
00299         // no bootkey found at all
00300         display.directcolor=COLOR_YELLOW;
00301         display.println("NO LOADER INSTALLED");
00302         if (sdversion==0 || sdjump < 0x38000) {
00303                 //file open of loader failed
00304                 display.println("NO VALID LOA.DER ON SD");
00305                 display.println("");
00306                 display.directcolor=COLOR_GREEN;
00307                 display.println("PUT LOA.DER ON SD & REBOOT");
00308         } else flashloader=true;
00309     } else {
00310         //loader was found
00311         //check if we should update the loader
00312         display.directcolor=COLOR_CYAN;
00313         display.print("LOADER V.");
00314         display.directcolor=COLOR_WHITE;
00315         display.println(*(bootinfo+1));
00316         #if POK_ENABLE_LOADER_UPDATES
00317         if (sdversion>(*(bootinfo+1))) flashloader=true;
00318         else start_application(*(bootinfo+2)); //never returns
00319         #else
00320         start_application(*(bootinfo+2)); //never returns
00321         #endif
00322     }
00323     // update loader if it was requested
00324     if(flashloader) {
00325             display.directcolor=COLOR_MAGENTA;
00326             display.print("NEW LOADER ON SD V.");
00327             display.directcolor=COLOR_WHITE;
00328             display.println(sdversion);
00329             display.directcolor=COLOR_GREEN;
00330             display.println("UPDATE LOADER?\n(UP=YES, DOWN=CANCEL)");
00331             while(1) {
00332                     if (buttons.upBtn()) {
00333                         if (updateLoader(sdversion,sdjump)) {
00334                             display.println("PUT LOA.DER ON SD AND RETRY");
00335                         } else {
00336                         display.println("SUCCESS!!");
00337                         checkforboot=true; //recheck
00338                         }
00339                     break;
00340                     }
00341                     if (buttons.downBtn()) return;
00342             }
00343     } // if flashloader
00344     } // while checkforboot
00345     #endif // POK_SIM
00346     while (!buttons.aBtn()) {
00347         buttons.pollButtons();
00348         if (buttons.aBtn()) {
00349             while (buttons.aBtn()) {
00350                 buttons.pollButtons();
00351             }
00352             return;
00353         }
00354     }
00355 }
00356 
00357 void Core::askLoader() {
00358     display.enableDirectPrinting(true);
00359     display.directbgcolor = COLOR_BLACK;
00360     display.clearLCD();
00361     display.directcolor = COLOR_RED;
00362     display.setFont(fntC64UIGfx);
00363     display.fontSize=1;
00364     display.adjustCharStep = 0;
00365     display.adjustLineStep = 0;
00366     display.directcolor=COLOR_GREEN;
00367     display.set_cursor(12*8,6*8);
00368     display.print("ijkl");
00369     display.set_cursor(12*8,7*8);
00370     display.print("mnop");
00371     display.set_cursor(12*8,8*8);
00372     display.print("qrst");
00373     display.set_cursor(12*8,9*8);
00374     display.print("uvwx");
00375     display.set_cursor(5*8,12*8);
00376     display.print("PRESS");
00377     display.directcolor=COLOR_WHITE;
00378     display.print(" C ");
00379     display.directcolor=COLOR_GREEN;
00380     display.print("FOR LOADER");
00381     display.directcolor=COLOR_WHITE;
00382     display.fontSize=2;
00383     int countd = 0;
00384     #ifndef POK_SIM
00385     //read countdown time from settings
00386     countd = eeprom_read_byte((uint16_t*)EESETTINGS_LOADERWAIT);
00387     #endif
00388     if (countd<=0 || countd > 5) countd=3;
00389     uint16_t c2 = getTime();
00390     while (countd) {
00391         buttons.pollButtons();
00392         display.set_cursor(13*8,15*8);
00393         display.print(countd);
00394         if (getTime()>c2+1000) {
00395             c2=getTime();
00396             countd--;
00397         }
00398         if (cBtn()) {while (cBtn()) buttons.pollButtons();jumpToLoader();countd=0;}
00399         if (aBtn()) {while (aBtn()) buttons.pollButtons();countd=0;}
00400         if (bBtn()) {while (bBtn()) buttons.pollButtons();countd=0;}
00401     }
00402     display.fontSize=1;
00403     display.clearLCD();
00404     display.enableDirectPrinting(false);
00405 }
00406 
00407 
00408 void Core::drawvolbar(int x, int y, int level, bool text) {
00409     uint16_t oldcol = display.directcolor;
00410     level = level >> 5;
00411     if (text) display.directRectangle(0,0,50,50,COLOR_BLACK);
00412     if (level<4)  display.directcolor = COLOR_GREEN;
00413     if (level==4) display.directcolor = COLOR_YELLOW;
00414     if (level>4) display.directcolor = COLOR_RED;
00415 
00416     if (text) {
00417             bool temp = display.isDirectPrintingEnabled();
00418             display.enableDirectPrinting(true);
00419             display.print(x-1,y-20,(int)sound.getVolume());
00420             display.print("  ");
00421             display.enableDirectPrinting(temp);
00422     }
00423     if (level<1) display.directcolor = COLOR_GRAY_80;
00424     display.directBitmap(x,y,Pokitto_volumebar,1,1);
00425     if (level<2) display.directcolor = COLOR_GRAY_80;
00426     display.directBitmap(x+8,y,Pokitto_volumebar,1,1);
00427     display.directBitmap(x+8,y-4,Pokitto_volumebar,1,1);
00428     if (level<3) display.directcolor = COLOR_GRAY_80;
00429     display.directBitmap(x+16,y,Pokitto_volumebar,1,1);
00430     display.directBitmap(x+16,y-4,Pokitto_volumebar,1,1);
00431     display.directBitmap(x+16,y-8,Pokitto_volumebar,1,1);
00432     if (level<4) {
00433             display.directcolor = COLOR_GRAY_80;
00434     }
00435     display.directBitmap(x+24,y,Pokitto_volumebar,1,1);
00436     display.directBitmap(x+24,y-4,Pokitto_volumebar,1,1);
00437     display.directBitmap(x+24,y-8,Pokitto_volumebar,1,1);
00438     display.directBitmap(x+24,y-12,Pokitto_volumebar,1,1);
00439 
00440     if (level<5) {
00441             display.directcolor = COLOR_GRAY_80;
00442     }
00443     display.directBitmap(x+32,y,Pokitto_volumebar,1,1);
00444     display.directBitmap(x+32,y-4,Pokitto_volumebar,1,1);
00445     display.directBitmap(x+32,y-8,Pokitto_volumebar,1,1);
00446     display.directBitmap(x+32,y-12,Pokitto_volumebar,1,1);
00447     display.directBitmap(x+32,y-16,Pokitto_volumebar,1,1);
00448 
00449     if (level<6) {
00450             display.directcolor = COLOR_GRAY_80;
00451     }
00452     display.directBitmap(x+40,y,Pokitto_volumebar,1,1);
00453     display.directBitmap(x+40,y-4,Pokitto_volumebar,1,1);
00454     display.directBitmap(x+40,y-8,Pokitto_volumebar,1,1);
00455     display.directBitmap(x+40,y-12,Pokitto_volumebar,1,1);
00456     display.directBitmap(x+40,y-16,Pokitto_volumebar,1,1);
00457     display.directBitmap(x+40,y-20,Pokitto_volumebar,1,1);
00458     display.directcolor = oldcol;
00459 }
00460 
00461 
00462 #ifdef POK_SIM
00463 #define VINCMULT 0.9f
00464 #else
00465 #define VINCMULT 50
00466 #endif //POK_SIM
00467 void Core::setVolLimit() {
00468     
00469     display.enableDirectPrinting(true);
00470     display.adjustCharStep = 0;
00471     //sound.setMaxVol(VOLUME_HEADPHONE_MAX);
00472     int dstate=1;
00473     bool wipe = true;
00474     float vol = sound.getVolume(); 
00475     #ifndef POK_SIM
00476     vol=eeprom_read_byte((uint16_t*)EESETTINGS_VOL);
00477     Pokitto::Sound::globalVolume=vol;
00478     #endif
00479     if (vol>VOLUME_HEADPHONE_MAX) sound.setMaxVol(VOLUME_SPEAKER_MAX);
00480     else sound.setMaxVol(VOLUME_HEADPHONE_MAX);
00481     #ifdef PRODUCTIONTESTING
00482     vol=170;
00483     sound.setMaxVol(VOLUME_SPEAKER_MAX);
00484     #endif
00485     //sound.setVolume(0);
00486     //if (vol) {
00487     //for (uint8_t t=0;t<=vol;t++) sound.setVolume(t);
00488     //}
00489     discrete_vol = ((int)vol>>5); //get rid of 0 vol number
00490     sound.setVolume(vol);
00491     volbar_visible=0;
00492     int countd=0;
00493     #ifndef POK_SIM
00494     //read countdown time from settings
00495     //countd = eeprom_read_byte((uint16_t*)EESETTINGS_VOLWAIT);
00496     #endif
00497     if (countd<=0 || countd > 10) countd=0xFFFF;
00498     #ifdef PRODUCTIONTESTING
00499     countd=2;
00500     #endif
00501     uint16_t c2 = getTime();
00502     while (core.isRunning() && dstate && countd){
00503         if (getTime()>c2+1000) {
00504             c2=getTime();
00505             if (countd<0xFFFF) countd--;
00506         }
00507         switch (dstate) {
00508         case 1:
00509             //redraw
00510             if (wipe) {
00511             display.clearLCD();
00512             display.directcolor = COLOR_WHITE;
00513             display.setCursor(4*8,2*8);
00514             display.print("SELECT VOLUME LIMIT");
00515             display.setCursor(5*8,17*9);
00516             display.print("PRESS ");
00517             display.directcolor = COLOR_GREEN;
00518             display.print("A");
00519             display.directcolor = COLOR_WHITE;
00520             display.print(" TO ACCEPT");
00521             display.directcolor = COLOR_GREEN;
00522             // draw frame below first
00523             display.setCursor(0,11*8);
00524             display.println(" abbbbbbbbbbbbbbbbbbbbbbbc");
00525             display.println(" |                       |");
00526             display.println(" |                       |");
00527             display.println(" |                       |");
00528             display.println(" |                       |");
00529             display.println(" dbbbbbbbbbbbbbbbbbbbbbbbe");
00530             } // wipe = true
00531             display.setCursor(6*8,17*9);
00532             if (discrete_vol<4) display.directcolor = COLOR_WHITE;
00533             else display.directcolor = COLOR_RED;
00534             display.directBitmap(21*8-4,6*8,Pokitto_headphones,1,2);
00535             display.setCursor(3*8,6*8+6);
00536             display.print("HEADPHONES");
00537             display.setCursor(3*8,8*8+2);
00538             if (discrete_vol>3) display.print("TOO LOUD!");
00539             else display.print("OK        ");
00540             display.directcolor = COLOR_GREEN;
00541             display.directBitmap(21*8-4,12*8,Pokitto_speaker,1,2);
00542             display.setCursor(3*8,12*8+6);
00543             display.print("VOLUME MAX");
00544             display.setCursor(3*8,14*8+2);
00545             //tvol = (vol/float(VOLUME_HEADPHONE_MAX))*100;
00546             //if (tvol > 100 && tvol < 120) tvol=100;
00547             //if (sound.getVolume()-5>VOLUME_HEADPHONE_MAX) { display.directcolor=COLOR_RED;}
00548             if ((sound.getVolume()>>5)>3) { display.directcolor=COLOR_RED;}
00549             else display.directcolor=COLOR_GREEN;
00550             if (discrete_vol==4) display.directcolor=COLOR_YELLOW;
00551             if (discrete_vol==7) discrete_vol=6;
00552             display.print((int)discrete_vol);
00553             //display.print(int(tvol));
00554             display.print("  ");
00555             display.directcolor=COLOR_GREEN;
00556             drawvolbar(14*8,14*8+4+3,sound.getVolume(),false);
00557             //display.setCursor(1,10);
00558             //display.print(vol);
00559             dstate=2; break;
00560         case 2:
00561             #ifndef POK_SIM
00562             buttons.pollButtons();
00563             #endif // POK_SIM
00564             buttons.update();
00565             if (aBtn()) {dstate=0;break;}
00566             if (buttons.pressed(BTN_RIGHT)) {
00567                     countd=0xFFFF; //disable countdown
00568                     /*if (vol >= VOLUME_HEADPHONE_MAX && vol < VOLUME_HEADPHONE_MAX+1 ) vol += 0.00025f*VINCMULT;
00569                     else if (vol >= VOLUME_HEADPHONE_MAX) vol += 0.25f*VINCMULT;
00570                     else vol += 0.05f*VINCMULT;
00571                     if (vol > VOLUME_HEADPHONE_MAX + 20) {
00572                             sound.setMaxVol(VOLUME_SPEAKER_MAX);
00573                     }
00574                     if (vol > VOLUME_SPEAKER_MAX) vol=VOLUME_SPEAKER_MAX;
00575                     sound.setVolume(vol);*/
00576                     sound.volumeUp();
00577                     dstate=1; wipe=false;
00578                     break;
00579                     }
00580             if (buttons.pressed(BTN_LEFT)) {
00581                     countd=0xFFFF; //disable countdown
00582                     /*if (vol >= VOLUME_HEADPHONE_MAX) vol -= 0.25f*VINCMULT;
00583                     else vol -= 0.05f*VINCMULT;
00584                     if (vol <= VOLUME_HEADPHONE_MAX) sound.setMaxVol(VOLUME_HEADPHONE_MAX);
00585                     if (vol < 0) vol=0;
00586                     sound.setVolume(vol);*/
00587                     sound.volumeDown();
00588                     dstate=1; wipe=false;
00589                     break;
00590                     }
00591             break;
00592         }
00593     }
00594     vol = sound.getVolume();
00595     #ifndef POK_SIM
00596     if (vol != eeprom_read_byte((uint16_t*)EESETTINGS_VOL)) eeprom_write_byte((uint16_t*)EESETTINGS_VOL,(uint8_t)vol);
00597     #endif
00598     sound.setVolume(vol);
00599     //sound.volumeUp();
00600     display.setCursor(0,0);
00601 }
00602 
00603 void Core::begin() {
00604 
00605     init(); // original functions
00606     timePerFrame = POK_FRAMEDURATION;
00607     //nextFrameMillis = 0;
00608     //frameCount = 0;
00609     frameEndMicros = 1;
00610     startMenuTimer = 255;
00611     //read default settings from flash memory (set using settings.hex)
00612     readSettings();
00613     //init everything
00614     backlight.begin();
00615     backlight.set(BACKLIGHT_MAX);
00616     buttons.begin();
00617     buttons.update();
00618     battery.begin();
00619     display.begin();
00620     #if POK_DISPLAYLOGO
00621         #if PROJ_DEVELOPER_MODE != 1
00622         showLogo();
00623         #endif // PROJ_DEVELOPER_MODE
00624     #endif // POK_DISPLAYLOGO
00625 
00626     display.enableDirectPrinting(true);
00627     display.directbgcolor = COLOR_BLACK;
00628     display.clearLCD();
00629     display.setFont(fntC64UIGfx);
00630 
00631     display.enableDirectPrinting(true);
00632     display.directbgcolor = COLOR_BLACK;
00633     display.directcolor = COLOR_GREEN;
00634     display.clearLCD();
00635     display.setFont(fntC64UIGfx);
00636     display.adjustCharStep=0;
00637     display.adjustLineStep=1;
00638     #ifdef JUSTLOAD
00639     jumpToLoader();
00640     #endif
00641 
00642     #ifndef DISABLE_LOADER
00643     #if PROJ_DEVELOPER_MODE != 1
00644     askLoader();
00645     #endif // PROJ_DEVELOPER_MODE
00646     #endif
00647 
00648     #if PROJ_DEVELOPER_MODE==1
00649     sound.setMaxVol(VOLUME_SPEAKER_MAX);
00650     sound.setVolume(VOLUME_SPEAKER_MAX);
00651     #else
00652     //showWarning();
00653     setVolLimit();
00654     display.clear();
00655     display.update();
00656 
00657     while(buttons.states[BTN_A])
00658     {
00659         buttons.update();
00660     }
00661 
00662     //sound.setVolume(sound.getVolume());//make sure we're at set volume before continue
00663     #endif
00664     display.enableDirectPrinting(false);
00665     display.adjustCharStep=1;
00666     display.adjustLineStep=1;
00667     display.fontSize=1;
00668     display.textWrap=true;
00669     #if POK_GAMEBUINO_SUPPORT > 0
00670     display.setFont(font5x7);
00671     #else
00672     display.setFont(fontC64);
00673     #endif
00674     #if POK_ENABLE_SOUND > 0
00675         sound.begin();
00676 
00677     //mute when B is held during start up or if battery is low
00678     battery.update();
00679     if(buttons.pressed(BTN_B)){
00680         sound.setVolume(0);
00681     }
00682     else{ //play the startup sound on each channel for it to be louder
00683         #if POK_GBSOUND > 0
00684         #if(NUM_CHANNELS > 0)
00685             sound.playPattern(startupSound, 0);
00686         #endif
00687         #if(NUM_CHANNELS > 1)
00688             sound.playPattern(startupSound, 1);
00689         #endif
00690         #if(NUM_CHANNELS > 2)
00691             sound.playPattern(startupSound, 2);
00692         #endif
00693         #if(NUM_CHANNELS > 3)
00694             sound.playPattern(startupSound, 3);
00695         #endif
00696         #endif // POK_GBSOUND
00697     }
00698     #endif // POK ENABLE_SOUND
00699 }
00700 
00701 void Core::init() {
00702     run_state = true;
00703     display.enableDirectPrinting(false);
00704     display.setFont(DEFAULT_FONT);
00705     initClock();
00706     initGPIO();
00707     initButtons();
00708     initRandom();
00709     //initAudio();
00710     //initDisplay();
00711 }
00712 
00713 void Core::init(uint8_t switches) {
00714     run_state = true;
00715     display.enableDirectPrinting(false);
00716     display.setFont(DEFAULT_FONT);
00717     initClock();
00718     initGPIO();
00719     initButtons();
00720     initRandom();
00721     //initAudio();
00722     //initDisplay();
00723 }
00724 
00725 void Core::initButtons() {
00726     #ifndef POK_SIM
00727     Pokitto::initButtons();
00728     #endif
00729 }
00730 
00731 bool Core::isRunning() {
00732     #ifdef POK_SIM
00733     run_state = simulator.isRunning();
00734     #endif // POK_SIM
00735     return run_state;
00736 }
00737 
00738 void Core::initDisplay() {
00739     #if POK_DISPLAYLOGO > 0
00740         showLogo();
00741     #endif
00742     #if POK_USE_CONSOLE > 0
00743         console.AddMessage(MSOURCE_LCD,MSG_INIT_OK);
00744     #endif
00745 }
00746 
00747 void Core::showLogo() {
00748     uint32_t now;
00749     uint8_t state=0; //jump directly to logo, bypass teeth
00750     uint16_t i=0;
00751     uint16_t sc;
00752     while (state < 255/6) {
00753     now=getTime();
00754     if (now>refreshtime) {
00755             refreshtime=now+30;
00756             switch (state) {
00757             case 0:
00758                 /** POKITTO CLEAN **/
00759                 display.directbgcolor = COLOR_BLACK;
00760                 display.fillLCD(display.directbgcolor);
00761                 sc = COLOR_BLACK;
00762                 state++;
00763                 break;
00764             case 1:
00765                 /** POKITTO FADE IN **/
00766                 display.directcolor = display.interpolateColor(sc, COLOR_GREEN, i);
00767                 display.directBitmap(POK_LCD_W/2 - (*Pokitto_logo/2),POK_LCD_H/2-(*(Pokitto_logo+1)/2),Pokitto_logo,1,1);
00768                 i += 28;
00769                 if (i>=0xFF) { state++; i=0;}
00770                 break;
00771             case 2:
00772                 /** POKITTO WAIT **/
00773                 display.directcolor = COLOR_GREEN;
00774                 display.directBitmap(POK_LCD_W/2 - (*Pokitto_logo/2),POK_LCD_H/2-(*(Pokitto_logo+1)/2),Pokitto_logo,1,1);
00775                 i+= 0x3F;
00776                 if (i>0x3FF) state = 255;
00777                 break;
00778             }
00779             if(buttons.aBtn()) state=255;
00780     }
00781     }
00782 }
00783 
00784 void Core::readSettings() {
00785     // ToDo
00786         /*display.contrast = SCR_CONTRAST;
00787         backlight.backlightMin = BACKLIGHT_MIN;
00788         backlight.backlightMax = BACKLIGHT_MAX;
00789         backlight.ambientLightMin = AMBIENTLIGHT_MIN;
00790         backlight.ambientLightMax = AMBIENTLIGHT_MAX;
00791 */
00792         sound.setMaxVol(VOLUME_HEADPHONE_MAX);
00793         sound.globalVolume = VOLUME_STARTUP;
00794 
00795         startMenuTimer = START_MENU_TIMER;
00796 /*
00797         battery.thresolds[0] = BAT_LVL_CRITIC;
00798         battery.thresolds[1] = BAT_LVL_LOW;
00799         battery.thresolds[2] = BAT_LVL_MED;
00800         battery.thresolds[3] = BAT_LVL_FULL;*/
00801 }
00802 
00803 void Core::titleScreen(const char* name){
00804     titleScreen(name, 0);
00805 }
00806 
00807 void Core::titleScreen(const uint8_t* logo){
00808     titleScreen((""), logo);
00809 }
00810 
00811 void Core::titleScreen(){
00812     titleScreen((""));
00813 }
00814 
00815 void Core::titleScreen(const char*  name, const uint8_t *logo){
00816     display.setFont(font5x7);
00817     while(buttons.aBtn()) wait(10); //don't accidentally skip menu
00818     if(startMenuTimer){
00819         display.fontSize = 1;
00820         display.textWrap = false;
00821         display.persistence = false;
00822         battery.show = false;
00823         display.setColor(BLACK);
00824         while(isRunning()){
00825             if(update()){
00826                 uint8_t logoOffset = name[0]?display.fontHeight:0; //add an offset the logo when there is a name to display
00827                 //draw graphics
00828                 display.setColorDepth(1);
00829                 display.setColor(3);
00830                 //display.drawBitmap(0,0, gamebuinoLogo);
00831                 display.setColor(1);
00832                 if(logo){
00833                     display.drawBitmap(0, 12+logoOffset, logo);
00834                 }
00835                 display.cursorX = 0;
00836                 display.cursorY = 12;
00837                 display.print(name);
00838 
00839                 //A button
00840                 display.cursorX = LCDWIDTH - display.fontWidth*3 -1;
00841                 display.cursorY = LCDHEIGHT - display.fontHeight*3 - 3;
00842                 if((frameCount/16)%2)
00843                   display.println(("\25 \20"));
00844                 else
00845                   display.println(("\25\20 "));
00846                 //B button
00847                 display.cursorX = LCDWIDTH - display.fontWidth*3 - 1;
00848                 display.cursorY++;
00849                 if(sound.globalVolume)
00850                     display.println(("\26\23\24"));
00851                 else
00852                     display.println(("\26\23x"));
00853                 //C button
00854                 display.cursorX = LCDWIDTH - display.fontWidth*3 - 1;
00855                 display.cursorY++;
00856                 //display.println(F("\27SD"));
00857 
00858                 //toggle volume when B is pressed
00859                 if(buttons.pressed(BTN_B)){
00860                     sound.setVolume(sound.getVolume() + 1);
00861                     sound.playTick();
00862                 }
00863                 //leave the menu
00864                 if(buttons.pressed(BTN_A) || ((frameCount>=startMenuTimer)&&(startMenuTimer != 255))){
00865                     startMenuTimer = 255; //don't automatically skip the title screen next time it's displayed
00866                     sound.stopPattern(0);
00867                     sound.playOK();
00868                     break;
00869                 }
00870                 //flash the loader
00871                 //if(buttons.pressed(BTN_C))
00872                     // ToDo changeGame();
00873             }
00874         }
00875         battery.show = true;
00876     }
00877 }
00878 
00879 /**
00880  * Update all the subsystems, like graphics, audio, events, etc.
00881  * Note: the update rect is used for drawing only part of the screen buffer to LCD. Because of speed optimizations,
00882  * the x, y, and width of the update rect must be dividable by 4 pixels, and the height must be dividable by 8 pixels.
00883  * The update rect is currently used for 220x176, 4 colors, screen mode only.
00884  * @param useDirectMode True, if only direct screen drawing is used. False, if the screen buffer is drawn. Note: If sprites are enabled, they are drawn in both modes.
00885  * @param updRectX The update rect.
00886  * @param updRectY The update rect.
00887  * @param updRectW The update rect.
00888  * @param updRectH The update rect.
00889  */
00890 bool Core::update(bool useDirectMode, uint8_t updRectX, uint8_t updRectY, uint8_t updRectW, uint8_t updRectH) {
00891 
00892     #if POK_STREAMING_MUSIC
00893         sound.updateStream();
00894     #endif
00895 
00896     uint32_t now = getTime();
00897     if ((((nextFrameMillis - now)) > timePerFrame) && frameEndMicros) { //if time to render a new frame is reached and the frame end has ran once
00898         nextFrameMillis = now + timePerFrame;
00899         frameCount++;
00900 
00901         frameEndMicros = 0;
00902         backlight.update();
00903         buttons.update();
00904         battery.update();
00905 
00906         // FPS counter
00907         #if defined(PROJ_USE_FPS_COUNTER) ||  defined(PROJ_SHOW_FPS_COUNTER)
00908         const uint32_t fpsInterval_ms = 1000*3;
00909 
00910         fps_frameCount++;
00911         if (now > fps_refreshtime) {
00912             fps_counter = (1000*fps_frameCount) / (now - fps_refreshtime + fpsInterval_ms);
00913             fps_refreshtime = now + fpsInterval_ms;
00914             fps_frameCount = 0;
00915             fps_counter_updated = true;
00916         }
00917         #endif
00918 
00919         return true;
00920 
00921     } else {
00922         if (!frameEndMicros) { //runs once at the end of the frame
00923             #if POK_ENABLE_SOUND > 0
00924             sound.updateTrack();
00925             sound.updatePattern();
00926             sound.updateNote();
00927             #endif
00928             updatePopup();
00929             displayBattery();
00930 
00931             display.update(useDirectMode, updRectX, updRectY, updRectW, updRectH); //send the buffer to the screen
00932 
00933             frameEndMicros = 1; //jonne
00934 
00935         }
00936         return false;
00937     }
00938 }
00939 
00940 void Core::displayBattery(){
00941 #if (ENABLE_BATTERY > 0)
00942     //display.setColor(BLACK, WHITE);
00943     uint8_t ox,oy;
00944     ox=display.cursorX;
00945     oy=display.cursorY;
00946     display.cursorX = LCDWIDTH-display.fontWidth+1;
00947     display.cursorY = 0;
00948     switch(battery.level){
00949     case 0://battery critic, power down
00950         sound.stopPattern();
00951         backlight.set(0);
00952         display.clear();
00953         display.fontSize = 1;
00954         display.print(("LOW BATTERY\n"));
00955         display.print(battery.voltage);
00956         display.print(("mV\n\nPLEASE\nTURN OFF"));
00957         display.update();
00958         break;
00959     case 1: //empty battery
00960         if((frameCount % 16) < 8) display.print('\7'); //blinking battery
00961         else display.print('x');
00962         break;
00963     case 2://low battery
00964     case 3://full battery
00965     case 4://full battery
00966         if(battery.show){
00967             display.print(char(5+battery.level));
00968         }
00969         break;
00970     default:
00971         if(battery.show){
00972             display.print('/');
00973         }
00974         break;
00975     }
00976 display.cursorX = ox;
00977 display.cursorY = oy;
00978 #endif
00979 }
00980 
00981 char* Core::filemenu(char *ext) {
00982     uint8_t oldPersistence = display.persistence;
00983     display.persistence = false;
00984     uint16_t oldpal0=display.palette[0];
00985     uint16_t oldpal1=display.palette[1];
00986     uint16_t oldpal2=display.palette[2];
00987     display.palette[2]=COLOR_GREEN;
00988     display.palette[1]=COLOR_WHITE;
00989     display.palette[0]=COLOR_BLACK;
00990     uint8_t oldbg=display.bgcolor;
00991     uint8_t oldfg=display.color;
00992     display.color=1;
00993     display.bgcolor=0;
00994 
00995     int16_t rowh = display.fontHeight + 2;
00996     int8_t activeItem = 0;
00997     int16_t currentTopItem = 0;
00998     int16_t itemsPerScreen = (display.height+1) / rowh;
00999     int16_t numOfItemsFound = 0;
01000     boolean exit = false;
01001 
01002     char* txt;
01003 
01004     pokInitSD();
01005 
01006     bool updated = true;
01007     while (isRunning()) {
01008         if (update(true)) {
01009             if (buttons.pressed(BTN_A) || buttons.pressed(BTN_B) || buttons.pressed(BTN_C)) {
01010                 exit = true; //time to exit menu !
01011                 if (buttons.pressed(BTN_A)) {
01012                     sound.playOK();
01013                 } else {
01014                     *selectedfile = 0;
01015                     sound.playCancel();
01016                 }
01017                 updated = true; // update screen
01018             }
01019             if (exit == false) {
01020                 if (buttons.repeat(BTN_DOWN,4)) {
01021                     if( ++activeItem >= numOfItemsFound ) activeItem = numOfItemsFound - 1;
01022                     if( activeItem >= currentTopItem + itemsPerScreen) currentTopItem += itemsPerScreen; // next page
01023                     sound.playTick();
01024                     updated = true; // update screen
01025                 }
01026                 if (buttons.repeat(BTN_UP,4)) {
01027                     if( --activeItem < 0 ) activeItem = 0;
01028                     if( activeItem < currentTopItem) currentTopItem -= itemsPerScreen;  // previous page
01029                     sound.playTick();
01030                     updated = true; // update screen
01031                 }
01032 
01033             } else { //exit :
01034                     display.bgcolor=oldbg;
01035                     display.color=oldfg;
01036                     display.palette[0] = oldpal0;
01037                     display.palette[1] = oldpal1;
01038                     display.palette[2] = oldpal2;
01039                     display.persistence = oldPersistence;
01040                     return selectedfile;
01041             }
01042 
01043             //draw a fancy menu
01044             display.textWrap = false;
01045             //uint16_t fc,bc;
01046             //fc = display.color;
01047             //bc = display.bgcolor;
01048             if( updated ) { // update screen?
01049                 #if POK_SIM
01050                 getFirstFile(ext);
01051                 #else
01052                 bool isFirstFile = true;
01053                 #endif
01054                 for (int i = 0; ; i++) {
01055                     display.cursorX = 0;
01056                     display.invisiblecolor=255;
01057                     display.cursorY = (i - currentTopItem ) * rowh;
01058 
01059                     // read the file name from SD
01060                     #ifndef POK_SIM
01061                     if(isFirstFile) {
01062                         txt = getFirstFile(ext);
01063                         isFirstFile = false;
01064                     }
01065                     else
01066                     #endif
01067 
01068                     txt = getNextFile(ext);
01069 
01070                     if (txt) {
01071 
01072                         numOfItemsFound = i+1;
01073 
01074                         // Draw active line with diffrent backgtound and char color
01075                         if (i == activeItem){
01076                             display.cursorX = 3;
01077                             display.color=2;
01078                             display.fillRect(0, display.cursorY - 2, LCDWIDTH, rowh);
01079                             display.setColor(0,2);
01080                         } else display.setColor(1,0);
01081 
01082                         // break loop if going out of screen
01083                         if(i >= currentTopItem + itemsPerScreen ) {
01084                             break;
01085                         }
01086 
01087                         // Display only if the file is on the current page
01088                         if( i >= currentTopItem) {
01089                             display.print(display.cursorX, display.cursorY, txt);
01090                             if (i == activeItem)
01091                                 strcpy(selectedfile,txt);
01092                         }
01093                     } else
01094                         break; // break loop as no more files found
01095 
01096                     display.setColor(1,0);
01097                 } // end for
01098 
01099                 display.update();
01100             }
01101             updated = false;
01102         } // update
01103 
01104         display.setColor(1,0);
01105     }
01106     return 0;
01107 }
01108 
01109 char* Core::filemenu() {
01110     return filemenu("");
01111 }
01112 
01113 int8_t Core::menu(const char* const* items, uint8_t length) {
01114 if (display.color>3) display.color=1;
01115 #if (ENABLE_GUI > 0)
01116     display.persistence = false;
01117     int8_t activeItem = 0;
01118     int16_t currentY = display.height;
01119     int16_t targetY = 0, rowh = display.fontHeight + 2;
01120     boolean exit = false;
01121     int8_t answer = -1;
01122     while (isRunning()) {
01123         if (update()) {
01124             if (buttons.pressed(BTN_A) || buttons.pressed(BTN_B) || buttons.pressed(BTN_C)) {
01125                 exit = true; //time to exit menu !
01126                 targetY = - display.fontHeight * length - 2; //send the menu out of the screen
01127                 if (buttons.pressed(BTN_A)) {
01128                     answer = activeItem;
01129                     sound.playOK();
01130                 } else {
01131                     sound.playCancel();
01132                 }
01133             }
01134             if (exit == false) {
01135                 if (buttons.repeat(BTN_DOWN,4)) {
01136                     activeItem++;
01137                     sound.playTick();
01138                 }
01139                 if (buttons.repeat(BTN_UP,4)) {
01140                     activeItem--;
01141                     sound.playTick();
01142                 }
01143                 //don't go out of the menu
01144                 if (activeItem == length) activeItem = 0;
01145                 if (activeItem < 0) activeItem = length - 1;
01146 
01147                 targetY = -rowh * activeItem + (rowh+4); //center the menu on the active item
01148             } else { //exit :
01149                 if ((currentY - targetY) <= 1)
01150                 return (answer);
01151             }
01152             //draw a fancy menu
01153             currentY = (currentY + targetY) / 2;
01154             display.cursorX = 0;
01155             display.cursorY = currentY;
01156             display.textWrap = false;
01157             uint16_t fc,bc;
01158             fc = display.color;
01159             bc = display.bgcolor;
01160             for (byte i = 0; i < length; i++) {
01161                 display.cursorY = currentY + rowh * i;
01162                 if (i == activeItem){
01163                     display.cursorX = 3;
01164 
01165                     //display.fillRoundRect(0, currentY + display.fontHeight * activeItem - 2, LCDWIDTH, (display.fontHeight+3), 3);
01166                     display.fillRect(0, currentY + rowh * activeItem - 2, LCDWIDTH, (rowh));
01167                     display.setColor(bc,fc);
01168                 } else display.setColor(fc,bc);
01169 
01170                 display.println((char*)*(const unsigned int*)(items+i));
01171                 display.setColor(fc,bc);
01172             }
01173 
01174         }
01175     }
01176 #else
01177     return 0;
01178 #endif
01179     return 0;
01180 }
01181 
01182 void Core::keyboard(char* text, uint8_t length) {
01183 #if (ENABLE_GUI > 0)
01184     display.persistence = false;
01185     //memset(text, 0, length); //clear the text
01186     text[length-1] = '\0';
01187     //active character in the typing area
01188     int8_t activeChar = 0;
01189     //selected char on the keyboard
01190     int8_t activeX = 0;
01191     int8_t activeY = 2;
01192     //position of the keyboard on the screen
01193     int8_t currentX = LCDWIDTH;
01194     int8_t currentY = LCDHEIGHT;
01195     int8_t targetX = 0;
01196     int8_t targetY = 0;
01197 
01198     while (1) {
01199         if (update()) {
01200             //move the character selector
01201             if (buttons.repeat(BTN_DOWN, 4)) {
01202                 activeY++;
01203                 sound.playTick();
01204             }
01205             if (buttons.repeat(BTN_UP, 4)) {
01206                 activeY--;
01207                 sound.playTick();
01208             }
01209             if (buttons.repeat(BTN_RIGHT, 4)) {
01210                 activeX++;
01211                 sound.playTick();
01212             }
01213             if (buttons.repeat(BTN_LEFT, 4)) {
01214                 activeX--;
01215                 sound.playTick();
01216             }
01217             //don't go out of the keyboard
01218             if (activeX == KEYBOARD_W) activeX = 0;
01219             if (activeX < 0) activeX = KEYBOARD_W - 1;
01220             if (activeY == KEYBOARD_H) activeY = 0;
01221             if (activeY < 0) activeY = KEYBOARD_H - 1;
01222             //set the keyboard position on screen
01223             targetX = -(display.fontWidth+1) * activeX + LCDWIDTH / 2 - 3;
01224             targetY = -(display.fontHeight+1) * activeY + LCDHEIGHT / 2 - 4 - display.fontHeight;
01225             //smooth the keyboard displacement
01226             currentX = (targetX + currentX) / 2;
01227             currentY = (targetY + currentY) / 2;
01228             //type character
01229             if (buttons.pressed(BTN_A)) {
01230                 if (activeChar < (length-1)) {
01231                     byte thisChar = activeX + KEYBOARD_W * activeY;
01232                     if((thisChar == 0)||(thisChar == 10)||(thisChar == 13)) //avoid line feed and carriage return
01233                     continue;
01234                     text[activeChar] = thisChar;
01235                     text[activeChar+1] = '\0';
01236                 }
01237                 activeChar++;
01238                 sound.playOK();
01239                 if (activeChar > length)
01240                 activeChar = length;
01241             }
01242             //erase character
01243             if (buttons.pressed(BTN_B)) {
01244                 activeChar--;
01245                 sound.playCancel();
01246                 if (activeChar >= 0)
01247                 text[activeChar] = 0;
01248                 else
01249                 activeChar = 0;
01250             }
01251             //leave menu
01252             if (buttons.pressed(BTN_C)) {
01253                 sound.playOK();
01254                 while (1) {
01255                     if (update()) {
01256                         //display.setCursor(0,0);
01257                         display.println(("You entered\n"));
01258                         display.print(text);
01259                         display.println(("\n\n\n\x15:okay \x16:edit"));
01260                         if(buttons.pressed(BTN_A)){
01261                             sound.playOK();
01262                             return;
01263                         }
01264                         if(buttons.pressed(BTN_B)){
01265                             sound.playCancel();
01266                             break;
01267                         }
01268                     }
01269                 }
01270             }
01271             //draw the keyboard
01272             for (int8_t y = 0; y < KEYBOARD_H; y++) {
01273                 for (int8_t x = 0; x < KEYBOARD_W; x++) {
01274                     display.drawChar(currentX + x * (display.fontWidth+1), currentY + y * (display.fontHeight+1), x + y * KEYBOARD_W, 1);
01275                 }
01276             }
01277             //draw instruction
01278             display.cursorX = currentX-display.fontWidth*6-2;
01279             display.cursorY = currentY+1*(display.fontHeight+1);
01280             display.print(("\25type"));
01281 
01282             display.cursorX = currentX-display.fontWidth*6-2;
01283             display.cursorY = currentY+2*(display.fontHeight+1);
01284             display.print(("\26back"));
01285 
01286             display.cursorX = currentX-display.fontWidth*6-2;
01287             display.cursorY = currentY+3*(display.fontHeight+1);
01288             display.print(("\27save"));
01289 
01290             //erase some pixels around the selected character
01291             display.setColor(WHITE);
01292             display.drawFastHLine(currentX + activeX * (display.fontWidth+1) - 1, currentY + activeY * (display.fontHeight+1) - 2, 7);
01293             //draw the selection rectangle
01294             display.setColor(BLACK);
01295             display.drawRoundRect(currentX + activeX * (display.fontWidth+1) - 2, currentY + activeY * (display.fontHeight+1) - 3, (display.fontWidth+2)+(display.fontWidth-1)%2, (display.fontHeight+5), 3);
01296             //draw keyboard outline
01297             //display.drawRoundRect(currentX - 6, currentY - 6, KEYBOARD_W * (display.fontWidth+1) + 12, KEYBOARD_H * (display.fontHeight+1) + 12, 8, BLACK);
01298             //text field
01299             display.drawFastHLine(0, LCDHEIGHT-display.fontHeight-2, LCDWIDTH);
01300             display.setColor(WHITE);
01301             display.fillRect(0, LCDHEIGHT-display.fontHeight-1, LCDWIDTH, display.fontHeight+1);
01302             //typed text
01303             display.cursorX = 0;
01304             display.cursorY = LCDHEIGHT-display.fontHeight;
01305             display.setColor(BLACK);
01306             display.print(text);
01307             //blinking cursor
01308             if (((frameCount % 8) < 4) && (activeChar < (length-1)))
01309             display.drawChar(display.fontWidth * activeChar, LCDHEIGHT-display.fontHeight, '_',1);
01310         }
01311     }
01312 #endif
01313 }
01314 
01315 void Core::popup(const char* text, uint8_t duration){
01316 #if (ENABLE_GUI > 0)
01317     popupText = text;
01318     popupTimeLeft = duration+12;
01319 #endif
01320 }
01321 
01322 void Core::updatePopup(){
01323 #if (ENABLE_GUI > 0)
01324     if (popupTimeLeft){
01325         uint8_t yOffset = 0;
01326         if(popupTimeLeft<12){
01327             yOffset = 12-popupTimeLeft;
01328         }
01329         display.fontSize = 1;
01330         display.setColor(WHITE);
01331         display.fillRoundRect(0,LCDHEIGHT-display.fontHeight+yOffset-3,84,display.fontHeight+3,3);
01332         display.setColor(BLACK);
01333         display.drawRoundRect(0,LCDHEIGHT-display.fontHeight+yOffset-3,84,display.fontHeight+3,3);
01334         display.cursorX = 4;
01335         display.cursorY = LCDHEIGHT-display.fontHeight+yOffset-1;
01336         display.print(popupText);
01337         popupTimeLeft--;
01338     }
01339 #endif
01340 }
01341 
01342 void Core::setFrameRate(uint8_t fps) {
01343     timePerFrame = 1000 / fps;
01344     sound.prescaler = fps / 20;
01345     sound.prescaler = __avrmax(1, sound.prescaler);
01346 }
01347 
01348 void Core::pickRandomSeed(){
01349         initRandom();
01350 }
01351 
01352 bool Core::collidePointRect(int16_t x1, int16_t y1 ,int16_t x2 ,int16_t y2, int16_t w, int16_t h){
01353     if((x1>=x2)&&(x1<x2+w))
01354     if((y1>=y2)&&(y1<y2+h))
01355     return true;
01356     return false;
01357 }
01358 
01359 bool Core::collideRectRect(int16_t x1, int16_t y1, int16_t w1, int16_t h1 ,int16_t x2 ,int16_t y2, int16_t w2, int16_t h2){
01360   return !( x2     >=  x1+w1  ||
01361             x2+w2  <=  x1     ||
01362             y2     >=  y1+h1  ||
01363             y2+h2  <=  y1     );
01364 }
01365 
01366 bool Core::collideBitmapBitmap(int16_t x1, int16_t y1, const uint8_t* b1, int16_t x2, int16_t y2, const uint8_t* b2){
01367   int16_t w1 = pgm_read_byte(b1);
01368   int16_t h1 = pgm_read_byte(b1 + 1);
01369   int16_t w2 = pgm_read_byte(b2);
01370   int16_t h2 = pgm_read_byte(b2 + 1);
01371 
01372   if(collideRectRect(x1, y1, w1, h1, x2, y2, w2, h2) == false){
01373     return false;
01374   }
01375 
01376   int16_t xmin = (x1>=x2)? 0 : x2-x1;
01377   int16_t ymin = (y1>=y2)? 0 : y2-y1;
01378   int16_t xmax = (x1+w1>=x2+w2)? x2+w2-x1 : w1;
01379   int16_t ymax = (y1+h1>=y2+h2)? y2+h2-y1 : h1;
01380   for(uint8_t y = ymin; y < ymax; y++){
01381     for(uint8_t x = xmin; x < xmax; x++){
01382       if(display.getBitmapPixel(b1, x, y) && display.getBitmapPixel(b2, x1+x-x2, y1+y-y2)){
01383         return true;
01384       }
01385     }
01386   }
01387   return false;
01388 }
01389 
01390 
01391 //** EOF **//
01392 
01393 
01394 
01395 
01396 
01397 
01398 
01399 
01400