Jonne Valola / PokittoLib Featured

Dependents:   YATTT sd_map_test cPong SnowDemo ... more

PokittoLib

Library for programming Pokitto hardware

How to Use

  1. Import this library to online compiler (see button "import" on the right hand side
  2. DO NOT import mbed-src anymore, a better version is now included inside PokittoLib
  3. Change My_settings.h according to your project
  4. Start coding!

Files at this revision

API Documentation at this revision

Comitter:
Pokitto
Date:
Mon May 21 18:03:14 2018 +0000
Parent:
45:bbfc6002118c
Child:
47:8f962908f6a7
Commit message:
Fixes to sound;

Changed in this revision

POKITTO_CORE/PokittoCore.cpp Show annotated file Show diff for this revision Revisions of this file
POKITTO_CORE/PokittoSound.cpp Show annotated file Show diff for this revision Revisions of this file
POKITTO_CORE/PokittoSound.h Show annotated file Show diff for this revision Revisions of this file
POKITTO_HW/HWLCD.cpp Show annotated file Show diff for this revision Revisions of this file
POKITTO_HW/HWLCD.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/POKITTO_CORE/PokittoCore.cpp	Mon May 21 18:03:14 2018 +0000
@@ -0,0 +1,1390 @@
+/**************************************************************************/
+/*!
+    @file     PokittoCore.cpp
+    @author   Jonne Valola
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2016, Jonne Valola
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+#include "Pokitto_settings.h"
+#include "PokittoCore.h"
+#include "PokittoConsole.h"
+#include "PokittoFonts.h"
+#include "PokittoTimer.h"
+#include "PokittoLogos.h"
+#include <stdlib.h>
+
+#ifndef DISABLEAVRMIN
+#define max(a,b) ((a)>(b)?(a):(b))
+#endif // DISABLEAVRMIN
+
+char selectedfile[25];
+
+//#define F
+#ifdef __ARMCC_VERSION
+typedef void (*func_t)(void);
+#endif
+
+#ifndef POK_SIM
+#include "iap.h"
+/** start the user application
+* https://community.nxp.com/thread/417695
+*
+*/
+void start_application(unsigned long app_link_location){
+    //asm(" ldr sp, [r0,#0]");
+    //asm(" ldr pc, [r0,#4]");
+    //This code is not valid for the Cortex-m0+ instruction set.
+    // The equivalent for this (as used by the KL26) is
+__disable_irq();// Start by disabling interrupts, before changing interrupt vectors
+
+// delete buttons
+//pokDeleteButtons();
+
+// completely kill button interrupts in preparation for reset
+LPC_PINT->IENR = 0;
+LPC_PINT->IENF = 0;
+
+SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk & ~(SysTick_CTRL_ENABLE_Msk); //disable systick
+LPC_SYSCON->PDRUNCFG     |=  (1 << 10);       /* Power-down USB PHY         */
+LPC_SYSCON->PDRUNCFG     |=  (1 <<  8);       /* Power-down USB PLL         */
+
+// reset clock source to IRC
+LPC_SYSCON->MAINCLKUEN    = 0x01;             /* Update MCLK Clock Source   */
+LPC_SYSCON->MAINCLKUEN    = 0x00;             /* Toggle Update Register     */
+while (LPC_SYSCON->MAINCLKUEN & 0x01);     /* Wait Until Updated         */
+// switch clock selection to IRC
+LPC_SYSCON->MAINCLKSEL    = 0;             /* Select Clock Source        */
+LPC_SYSCON->MAINCLKUEN    = 0x01;             /* Update MCLK Clock Source   */
+LPC_SYSCON->MAINCLKUEN    = 0x00;             /* Toggle Update Register     */
+while (LPC_SYSCON->MAINCLKUEN & 0x01);     /* Wait Until Updated         */
+//disable PLL clock output
+LPC_SYSCON->SYSPLLCLKUEN = 0;
+while (LPC_SYSCON->SYSPLLCLKUEN & 0x00);
+LPC_SYSCON->SYSPLLCTRL = 0;
+
+//kill peripherals
+LPC_SYSCON->MAINCLKSEL = 0;
+LPC_SYSCON->PRESETCTRL = 0; //disable all peripherals
+
+//power down PLL
+volatile uint32_t tmp;
+tmp = (LPC_SYSCON->PDRUNCFG & 0x000025FFL);
+tmp |= ((1<<7) & 0x000025FFL);
+LPC_SYSCON->PDRUNCFG = (tmp | 0x0000C800L); /* Power-down SYSPLL          */
+
+//Chip_Clock_SetMainClockSource(SYSCTL_MAINCLKSRC_IRC); //switch to IRC
+
+// clear all gpio states
+LPC_GPIO_PORT->PIN[0] = 0;
+LPC_GPIO_PORT->PIN[1] = 0;
+LPC_GPIO_PORT->PIN[2] = 0;
+
+SCB->VTOR = app_link_location;//APPL_ADDRESS; /* Change vector table address */
+#ifndef __ARMCC_VERSION
+asm(" mov r0, %[address]"::[address] "r" (app_link_location));
+asm(" ldr r1, [r0,#0]"); // get the stack pointer value from the program's reset vector
+asm(" mov sp, r1");      // copy the value to the stack pointer
+asm(" ldr r0, [r0,#4]"); // get the program counter value from the program's reset vector
+asm(" blx r0");          // jump to the' start address
+#else
+uint32_t *app_loc = (uint32_t*)app_link_location;
+__set_MSP (app_loc[0]);
+((func_t)(app_loc[1]))();
+#endif
+}
+#endif
+
+// returns a random integar between 0 and maxVal
+int random(int maxVal)
+{
+  return random( 0, maxVal);
+}
+
+// returns a random integar between minVal and maxVal
+int random(int minVal, int maxVal)
+{
+  // int rand(void); included by default from newlib
+  return rand() % (maxVal-minVal+1) + minVal;
+}
+
+using namespace Pokitto;
+
+bool Core::run_state; // this definition needed
+
+/** Components */
+Backlight Core::backlight;
+Buttons Core::buttons;
+Battery Core::battery;
+#if POK_ENABLE_SOUND > 0
+Sound Core::sound;
+#endif
+Display Core::display;
+
+//GB Related
+uint8_t Core::startMenuTimer;
+uint8_t Core::timePerFrame;
+uint32_t Core::nextFrameMillis;
+uint32_t Core::frameCount;
+const char* Core::popupText;
+uint8_t Core::popupTimeLeft;
+uint16_t Core::frameDurationMicros;
+uint32_t Core::frameStartMicros, Core::frameEndMicros;
+uint8_t Core::volbar_visible=0;
+
+uint32_t Core::fps_counter;
+bool Core::fps_counter_updated;
+uint32_t Core::fps_refreshtime;
+uint32_t Core::fps_frameCount;
+
+Core::Core() {
+
+}
+
+
+int Core::updateLoader (uint32_t version, uint32_t jumpaddress) {
+    #ifndef POK_SIM
+    uint32_t counter=0;
+    uint8_t data[256];
+    /** prepare the flash writing **/
+    float progress=0;
+    int opg=-1;
+    uint32_t fsize=0;
+    fileEnd(); //
+    fsize = fileGetPosition();
+    if (fsize>0x40000-jumpaddress) fsize = 0x40000-jumpaddress; // shouldn't happen!!
+    fileRewind();
+    display.println("PLEASE WAIT");
+    while (1) {
+        //if (counter >= fsize-0x200) {
+        //    display.println("gotcha");
+        //}
+        if (counter >= fsize) {
+                break;
+        }
+        opg=progress;
+        if (fileReadBytes(&data[0],0x100)<0x100) {
+                if (fsize-counter>0x100) {
+                        display.println("ERROR READING LOA.DER FILE");
+                        return 1; // 1 means error
+                }
+        }
+        if (CopyPageToFlash(jumpaddress+counter,data)) {
+                    display.println("FLASH WRITE ERROR");
+                    return 1;
+        } else {
+            counter += 0x100;
+            display.print(".");
+        }
+        }
+        #endif // POK_SIM
+    return 0; //success
+}
+
+void Core::showWarning() {
+    display.enableDirectPrinting(true);
+    display.directbgcolor = COLOR_BLACK;
+    display.clearLCD();
+    display.directcolor = COLOR_RED;
+    display.setFont(fntC64UIGfx);
+    display.adjustCharStep = 0;
+    display.adjustLineStep = 0;
+    display.setCursor(10*8,9);
+    display.print("WARNING!");
+    display.directcolor = COLOR_WHITE;
+    display.setCursor(5*8,4*9);
+    display.print("LOUD SOUND THROUGH");
+    display.setCursor(2*8,5*9);
+    display.print("HEADPHONES COULD DAMAGE");
+    display.setCursor(7*8,6*9);
+    display.print("YOUR HEARING.");
+    display.setCursor(5*8,8*9);
+    display.print("USE "); display.directcolor = COLOR_GREEN;
+    display.print("0-100% VOLUME");
+    display.directcolor = COLOR_WHITE;
+    display.setCursor(5*8,9*9);
+    display.print("FOR LISTENING WITH");
+    display.setCursor(8*8,10*9);
+    display.print("HEADPHONES");
+    display.setCursor(5*8,12*9);
+    display.print("USE "); display.directcolor = COLOR_RED;
+    display.print("> 100% VOLUME"); display.directcolor = COLOR_GREEN;
+    display.directcolor = COLOR_WHITE;
+    display.setCursor(1*8,13*9);
+    display.print("ONLY FOR LISTENING VIA THE");
+    display.setCursor(5*8,14*9);
+    display.print("BUILT-IN SPEAKER");
+    display.setCursor(5*8,17*9);
+    display.print("PRESS ");display.directcolor = COLOR_GREEN;
+    display.print("C ");display.directcolor = COLOR_WHITE;
+    display.print("TO ACCEPT");
+    while (!buttons.cBtn()) {
+        wait(100);
+    }
+    display.clearLCD();
+    display.enableDirectPrinting(false);
+}
+
+void Core::jumpToLoader() {
+    //display.setFont(font5x7);
+    //display.adjustCharStep=1;
+    //display.adjustLineStep=2;
+    display.fontSize=1;
+    display.directbgcolor=COLOR_BLACK;
+    display.directcolor=COLOR_GREEN;
+    display.clearLCD();
+    display.setCursor(0,0);
+    display.enableDirectPrinting(true);
+    #ifdef POK_SIM
+    display.println("LOADER IS NOT AVAILABLE ON THE SIMULATOR. PRESS A TO RETURN.");
+    #else
+    uint32_t* bootinfo;
+    uint32_t bootversion=0, sdversion=0, sdjump=0;
+    bool flashloader=false, checkforboot=true;
+    //check for loa.der on SD card
+    #if POK_ENABLE_LOADER_UPDATES > 0
+    pokInitSD();
+    if (fileOpen("LOA.DER", FILE_MODE_BINARY)==0) {
+        //LOA.DER found on SD
+        fileEnd(); // go to end
+        fileSeekRelative(-8); //rewind 8 bytes
+        uint32_t* tptr = &sdversion;
+        fileReadBytes((uint8_t*)tptr,4); //read version number of loader on SD card
+        tptr = &sdjump;
+        fileReadBytes((uint8_t*)tptr,4); //read jump address of loader on sd card
+        fileRewind();
+    }
+    #endif
+    //now start searching for bootkey
+    while (checkforboot)
+    {
+    checkforboot=false; flashloader=false;
+    bootinfo = (uint32_t*)0x3FFF4;
+    if (*bootinfo != 0xB007AB1E) bootinfo = (uint32_t*)0x3FF04; //allow couple of alternative locations
+    if (*bootinfo != 0xB007AB1E) bootinfo = (uint32_t*)0x3FE04; //allow couple of alternative locations
+    if (*bootinfo != 0xB007AB1E) bootinfo = (uint32_t*)0x3F004; //for futureproofing
+    if (*bootinfo != 0xB007AB1E) {
+        // no bootkey found at all
+        display.directcolor=COLOR_YELLOW;
+        display.println("NO LOADER INSTALLED");
+        if (sdversion==0 || sdjump < 0x38000) {
+                //file open of loader failed
+                display.println("NO VALID LOA.DER ON SD");
+                display.println("");
+                display.directcolor=COLOR_GREEN;
+                display.println("PUT LOA.DER ON SD & REBOOT");
+        } else flashloader=true;
+    } else {
+        //loader was found
+        //check if we should update the loader
+        display.directcolor=COLOR_CYAN;
+        display.print("LOADER V.");
+        display.directcolor=COLOR_WHITE;
+        display.println(*(bootinfo+1));
+        #if POK_ENABLE_LOADER_UPDATES
+        if (sdversion>(*(bootinfo+1))) flashloader=true;
+        else start_application(*(bootinfo+2)); //never returns
+        #else
+        start_application(*(bootinfo+2)); //never returns
+        #endif
+    }
+    // update loader if it was requested
+    if(flashloader) {
+            display.directcolor=COLOR_MAGENTA;
+            display.print("NEW LOADER ON SD V.");
+            display.directcolor=COLOR_WHITE;
+            display.println(sdversion);
+            display.directcolor=COLOR_GREEN;
+            display.println("UPDATE LOADER?\n(UP=YES, DOWN=CANCEL)");
+            while(1) {
+                    if (buttons.upBtn()) {
+                        if (updateLoader(sdversion,sdjump)) {
+                            display.println("PUT LOA.DER ON SD AND RETRY");
+                        } else {
+                        display.println("SUCCESS!!");
+                        checkforboot=true; //recheck
+                        }
+                    break;
+                    }
+                    if (buttons.downBtn()) return;
+            }
+    } // if flashloader
+    } // while checkforboot
+    #endif // POK_SIM
+    while (!buttons.aBtn()) {
+        buttons.pollButtons();
+        if (buttons.aBtn()) {
+            while (buttons.aBtn()) {
+                buttons.pollButtons();
+            }
+            return;
+        }
+    }
+}
+
+void Core::askLoader() {
+    display.enableDirectPrinting(true);
+    display.directbgcolor = COLOR_BLACK;
+    display.clearLCD();
+    display.directcolor = COLOR_RED;
+    display.setFont(fntC64UIGfx);
+    display.fontSize=1;
+    display.adjustCharStep = 0;
+    display.adjustLineStep = 0;
+    display.directcolor=COLOR_GREEN;
+    display.set_cursor(12*8,6*8);
+    display.print("ijkl");
+    display.set_cursor(12*8,7*8);
+    display.print("mnop");
+    display.set_cursor(12*8,8*8);
+    display.print("qrst");
+    display.set_cursor(12*8,9*8);
+    display.print("uvwx");
+    display.set_cursor(5*8,12*8);
+    display.print("PRESS");
+    display.directcolor=COLOR_WHITE;
+    display.print(" C ");
+    display.directcolor=COLOR_GREEN;
+    display.print("FOR LOADER");
+    display.directcolor=COLOR_WHITE;
+    display.fontSize=2;
+    int countd = 0;
+    #ifndef POK_SIM
+    //read countdown time from settings
+    countd = eeprom_read_byte((uint16_t*)EESETTINGS_LOADERWAIT);
+    #endif
+    if (countd==0 || countd > 5) countd=3;
+    uint16_t c2 = getTime();
+    while (countd) {
+        buttons.pollButtons();
+        display.set_cursor(13*8,15*8);
+        display.print(countd);
+        if (getTime()>c2+1000) {
+            c2=getTime();
+            countd--;
+        }
+        if (cBtn()) {while (cBtn()) buttons.pollButtons();jumpToLoader();countd=0;}
+        if (aBtn()) {while (aBtn()) buttons.pollButtons();countd=0;}
+        if (bBtn()) {while (bBtn()) buttons.pollButtons();countd=0;}
+    }
+    display.fontSize=1;
+    display.clearLCD();
+    display.enableDirectPrinting(false);
+}
+
+
+void Core::drawvolbar(int x, int y, int level, bool text) {
+    uint16_t oldcol = display.directcolor;
+    level = level >> 5;
+    if (text) display.directRectangle(0,0,50,50,COLOR_BLACK);
+    if (level<4)  display.directcolor = COLOR_GREEN;
+    if (level==4) display.directcolor = COLOR_YELLOW;
+    if (level>4) display.directcolor = COLOR_RED;
+
+    if (text) {
+            bool temp = display.isDirectPrintingEnabled();
+            display.enableDirectPrinting(true);
+            display.print(x-1,y-20,(int)sound.getVolume());
+            display.print("  ");
+            display.enableDirectPrinting(temp);
+    }
+    if (level<1) display.directcolor = COLOR_GRAY_80;
+    display.directBitmap(x,y,Pokitto_volumebar,1,1);
+    if (level<2) display.directcolor = COLOR_GRAY_80;
+    display.directBitmap(x+8,y,Pokitto_volumebar,1,1);
+    display.directBitmap(x+8,y-4,Pokitto_volumebar,1,1);
+    if (level<3) display.directcolor = COLOR_GRAY_80;
+    display.directBitmap(x+16,y,Pokitto_volumebar,1,1);
+    display.directBitmap(x+16,y-4,Pokitto_volumebar,1,1);
+    display.directBitmap(x+16,y-8,Pokitto_volumebar,1,1);
+    if (level<4) {
+            display.directcolor = COLOR_GRAY_80;
+    }
+    display.directBitmap(x+24,y,Pokitto_volumebar,1,1);
+    display.directBitmap(x+24,y-4,Pokitto_volumebar,1,1);
+    display.directBitmap(x+24,y-8,Pokitto_volumebar,1,1);
+    display.directBitmap(x+24,y-12,Pokitto_volumebar,1,1);
+
+    if (level<5) {
+            display.directcolor = COLOR_GRAY_80;
+    }
+    display.directBitmap(x+32,y,Pokitto_volumebar,1,1);
+    display.directBitmap(x+32,y-4,Pokitto_volumebar,1,1);
+    display.directBitmap(x+32,y-8,Pokitto_volumebar,1,1);
+    display.directBitmap(x+32,y-12,Pokitto_volumebar,1,1);
+    display.directBitmap(x+32,y-16,Pokitto_volumebar,1,1);
+
+    if (level<6) {
+            display.directcolor = COLOR_GRAY_80;
+    }
+    display.directBitmap(x+40,y,Pokitto_volumebar,1,1);
+    display.directBitmap(x+40,y-4,Pokitto_volumebar,1,1);
+    display.directBitmap(x+40,y-8,Pokitto_volumebar,1,1);
+    display.directBitmap(x+40,y-12,Pokitto_volumebar,1,1);
+    display.directBitmap(x+40,y-16,Pokitto_volumebar,1,1);
+    display.directBitmap(x+40,y-20,Pokitto_volumebar,1,1);
+    display.directcolor = oldcol;
+}
+
+
+#ifdef POK_SIM
+#define VINCMULT 0.9f
+#else
+#define VINCMULT 50
+#endif //POK_SIM
+void Core::setVolLimit() {
+    display.enableDirectPrinting(true);
+    display.adjustCharStep = 0;
+    //sound.setMaxVol(VOLUME_HEADPHONE_MAX);
+    int dstate=1;
+    bool wipe = true;
+    float vol = sound.getVolume(); float tvol;
+    #ifndef POK_SIM
+    vol=eeprom_read_byte((uint16_t*)EESETTINGS_VOL);
+    Pokitto::Sound::globalVolume=vol;
+    #endif
+    if (vol>VOLUME_HEADPHONE_MAX) sound.setMaxVol(VOLUME_SPEAKER_MAX);
+    else sound.setMaxVol(VOLUME_HEADPHONE_MAX);
+    #ifdef PRODUCTIONTESTING
+    vol=170;
+    sound.setMaxVol(VOLUME_SPEAKER_MAX);
+    #endif
+    for (uint8_t t=0;t<=vol;t++) {
+            sound.setVolume(t);
+    }
+    volbar_visible=0;
+    int countd=0;
+    #ifndef POK_SIM
+    //read countdown time from settings
+    countd = eeprom_read_byte((uint16_t*)EESETTINGS_VOLWAIT);
+    #endif
+    if (countd==0 || countd > 10) countd=0xFFFF;
+    #ifdef PRODUCTIONTESTING
+    countd=2;
+    #endif
+    uint16_t c2 = getTime();
+    while (core.isRunning() && dstate && countd){
+        if (getTime()>c2+1000) {
+            c2=getTime();
+            if (countd<0xFFFF) countd--;
+        }
+        switch (dstate) {
+        case 1:
+            //redraw
+            if (wipe) {
+            display.clearLCD();
+            display.directcolor = COLOR_WHITE;
+            display.setCursor(4*8,2*8);
+            display.print("SELECT VOLUME LIMIT");
+            display.setCursor(5*8,17*9);
+            display.print("PRESS ");
+            display.directcolor = COLOR_GREEN;
+            display.print("A");
+            display.directcolor = COLOR_WHITE;
+            display.print(" TO ACCEPT");
+            display.directcolor = COLOR_GREEN;
+            // draw frame below first
+            display.setCursor(0,11*8);
+            display.println(" abbbbbbbbbbbbbbbbbbbbbbbc");
+            display.println(" |                       |");
+            display.println(" |                       |");
+            display.println(" |                       |");
+            display.println(" |                       |");
+            display.println(" dbbbbbbbbbbbbbbbbbbbbbbbe");
+            } // wipe = true
+            display.setCursor(6*8,17*9);
+            if (discrete_vol<4) display.directcolor = COLOR_WHITE;
+            else display.directcolor = COLOR_RED;
+            display.directBitmap(21*8-4,6*8,Pokitto_headphones,1,2);
+            display.setCursor(3*8,6*8+6);
+            display.print("HEADPHONES");
+            display.setCursor(3*8,8*8+2);
+            if (discrete_vol>3) display.print("TOO LOUD!");
+            else display.print("OK        ");
+            display.directcolor = COLOR_GREEN;
+            display.directBitmap(21*8-4,12*8,Pokitto_speaker,1,2);
+            display.setCursor(3*8,12*8+6);
+            display.print("VOLUME MAX");
+            display.setCursor(3*8,14*8+2);
+            //tvol = (vol/float(VOLUME_HEADPHONE_MAX))*100;
+            //if (tvol > 100 && tvol < 120) tvol=100;
+            //if (sound.getVolume()-5>VOLUME_HEADPHONE_MAX) { display.directcolor=COLOR_RED;}
+            if ((sound.getVolume()>>5)>3) { display.directcolor=COLOR_RED;}
+            else display.directcolor=COLOR_GREEN;
+            if (discrete_vol==4) display.directcolor=COLOR_YELLOW;
+            if (discrete_vol==7) discrete_vol=6;
+            display.print((int)discrete_vol);
+            //display.print(int(tvol));
+            display.print("  ");
+            display.directcolor=COLOR_GREEN;
+            drawvolbar(14*8,14*8+4+3,sound.getVolume(),false);
+            //display.setCursor(1,10);
+            //display.print(vol);
+            dstate=2; break;
+        case 2:
+            #ifndef POK_SIM
+            buttons.pollButtons();
+            #endif // POK_SIM
+            buttons.update();
+            if (aBtn()) {dstate=0;break;}
+            if (buttons.pressed(BTN_RIGHT)) {
+                    countd=0xFFFF; //disable countdown
+                    /*if (vol >= VOLUME_HEADPHONE_MAX && vol < VOLUME_HEADPHONE_MAX+1 ) vol += 0.00025f*VINCMULT;
+                    else if (vol >= VOLUME_HEADPHONE_MAX) vol += 0.25f*VINCMULT;
+                    else vol += 0.05f*VINCMULT;
+                    if (vol > VOLUME_HEADPHONE_MAX + 20) {
+                            sound.setMaxVol(VOLUME_SPEAKER_MAX);
+                    }
+                    if (vol > VOLUME_SPEAKER_MAX) vol=VOLUME_SPEAKER_MAX;
+                    sound.setVolume(vol);*/
+                    sound.volumeUp();
+                    dstate=1; wipe=false;
+                    break;
+                    }
+            if (buttons.pressed(BTN_LEFT)) {
+                    countd=0xFFFF; //disable countdown
+                    /*if (vol >= VOLUME_HEADPHONE_MAX) vol -= 0.25f*VINCMULT;
+                    else vol -= 0.05f*VINCMULT;
+                    if (vol <= VOLUME_HEADPHONE_MAX) sound.setMaxVol(VOLUME_HEADPHONE_MAX);
+                    if (vol < 0) vol=0;
+                    sound.setVolume(vol);*/
+                    sound.volumeDown();
+                    dstate=1; wipe=false;
+                    break;
+                    }
+            break;
+        }
+    }
+    vol = sound.getVolume();
+    #ifndef POK_SIM
+    if (vol != eeprom_read_byte((uint16_t*)EESETTINGS_VOL)) eeprom_write_byte((uint16_t*)EESETTINGS_VOL,(uint8_t)vol);
+    #endif
+    sound.setVolume(vol);
+    //sound.volumeUp();
+    display.setCursor(0,0);
+}
+
+void Core::begin() {
+
+    init(); // original functions
+    timePerFrame = POK_FRAMEDURATION;
+	//nextFrameMillis = 0;
+	//frameCount = 0;
+	frameEndMicros = 1;
+	startMenuTimer = 255;
+	//read default settings from flash memory (set using settings.hex)
+	readSettings();
+	//init everything
+	backlight.begin();
+	backlight.set(BACKLIGHT_MAX);
+	buttons.begin();
+	buttons.update();
+	battery.begin();
+	display.begin();
+	#if POK_DISPLAYLOGO
+        #if PROJ_DEVELOPER_MODE != 1
+        showLogo();
+        #endif // PROJ_DEVELOPER_MODE
+	#endif // POK_DISPLAYLOGO
+
+	display.enableDirectPrinting(true);
+    display.directbgcolor = COLOR_BLACK;
+    display.clearLCD();
+    display.setFont(fntC64UIGfx);
+
+    display.enableDirectPrinting(true);
+    display.directbgcolor = COLOR_BLACK;
+    display.directcolor = COLOR_GREEN;
+    display.clearLCD();
+    display.setFont(fntC64UIGfx);
+    display.adjustCharStep=0;
+    display.adjustLineStep=1;
+    #ifdef JUSTLOAD
+	jumpToLoader();
+	#endif
+
+    #ifndef DISABLE_LOADER
+    #if PROJ_DEVELOPER_MODE != 1
+    askLoader();
+    #endif // PROJ_DEVELOPER_MODE
+    #endif
+
+	#if PROJ_DEVELOPER_MODE==1
+	sound.setMaxVol(VOLUME_SPEAKER_MAX);
+	sound.setVolume(VOLUME_SPEAKER_MAX);
+	#else
+	//showWarning();
+	setVolLimit();
+	display.clear();
+	display.update();
+
+	while(buttons.states[BTN_A])
+    {
+        buttons.update();
+    }
+
+	//sound.setVolume(sound.getVolume());//make sure we're at set volume before continue
+	#endif
+	display.enableDirectPrinting(false);
+	display.adjustCharStep=1;
+	display.adjustLineStep=1;
+	display.fontSize=1;
+	display.textWrap=true;
+	#if POK_GAMEBUINO_SUPPORT > 0
+	display.setFont(font5x7);
+	#else
+	display.setFont(fontC64);
+    #endif
+	#if POK_ENABLE_SOUND > 0
+        sound.begin();
+
+	//mute when B is held during start up or if battery is low
+	battery.update();
+	if(buttons.pressed(BTN_B)){
+		sound.setVolume(0);
+	}
+	else{ //play the startup sound on each channel for it to be louder
+		#if POK_GBSOUND > 0
+		#if(NUM_CHANNELS > 0)
+			sound.playPattern(startupSound, 0);
+		#endif
+		#if(NUM_CHANNELS > 1)
+			sound.playPattern(startupSound, 1);
+		#endif
+		#if(NUM_CHANNELS > 2)
+			sound.playPattern(startupSound, 2);
+		#endif
+		#if(NUM_CHANNELS > 3)
+			sound.playPattern(startupSound, 3);
+		#endif
+		#endif // POK_GBSOUND
+	}
+	#endif // POK ENABLE_SOUND
+}
+
+void Core::init() {
+    run_state = true;
+    display.enableDirectPrinting(false);
+    display.setFont(DEFAULT_FONT);
+    initClock();
+    initGPIO();
+    initButtons();
+    initRandom();
+    //initAudio();
+    //initDisplay();
+}
+
+void Core::init(uint8_t switches) {
+    run_state = true;
+    display.enableDirectPrinting(false);
+    display.setFont(DEFAULT_FONT);
+    initClock();
+    initGPIO();
+    initButtons();
+    initRandom();
+    //initAudio();
+    //initDisplay();
+}
+
+void Core::initButtons() {
+    #ifndef POK_SIM
+    Pokitto::initButtons();
+    #endif
+}
+
+bool Core::isRunning() {
+    #ifdef POK_SIM
+    run_state = simulator.isRunning();
+    #endif // POK_SIM
+    return run_state;
+}
+
+void Core::initDisplay() {
+    #if POK_DISPLAYLOGO > 0
+        showLogo();
+    #endif
+    #if POK_USE_CONSOLE > 0
+        console.AddMessage(MSOURCE_LCD,MSG_INIT_OK);
+    #endif
+}
+
+void Core::showLogo() {
+    uint32_t now;
+    uint8_t state=0; //jump directly to logo, bypass teeth
+    uint16_t counter=0, i=0;
+    uint16_t sc;
+    while (state < 255/6) {
+    now=getTime();
+    if (now>refreshtime) {
+            refreshtime=now+30;
+            switch (state) {
+            case 0:
+                /** POKITTO CLEAN **/
+                display.directbgcolor = COLOR_BLACK;
+                display.fillLCD(display.directbgcolor);
+                sc = COLOR_BLACK;
+                state++;
+                break;
+            case 1:
+                /** POKITTO FADE IN **/
+                display.directcolor = display.interpolateColor(sc, COLOR_GREEN, i);
+                display.directBitmap(POK_LCD_W/2 - (*Pokitto_logo/2),POK_LCD_H/2-(*(Pokitto_logo+1)/2),Pokitto_logo,1,1);
+                i += 28;
+                if (i>=0xFF) { state++; i=0;}
+                break;
+            case 2:
+                /** POKITTO WAIT **/
+                display.directcolor = COLOR_GREEN;
+                display.directBitmap(POK_LCD_W/2 - (*Pokitto_logo/2),POK_LCD_H/2-(*(Pokitto_logo+1)/2),Pokitto_logo,1,1);
+                i+= 0x3F;
+                if (i>0x3FF) state = 255;
+                break;
+            }
+            if(buttons.aBtn()) state=255;
+    }
+    }
+}
+
+void Core::readSettings() {
+    // ToDo
+        /*display.contrast = SCR_CONTRAST;
+		backlight.backlightMin = BACKLIGHT_MIN;
+		backlight.backlightMax = BACKLIGHT_MAX;
+		backlight.ambientLightMin = AMBIENTLIGHT_MIN;
+		backlight.ambientLightMax = AMBIENTLIGHT_MAX;
+*/
+		sound.setMaxVol(VOLUME_HEADPHONE_MAX);
+		sound.globalVolume = VOLUME_STARTUP;
+
+		startMenuTimer = START_MENU_TIMER;
+/*
+		battery.thresolds[0] = BAT_LVL_CRITIC;
+		battery.thresolds[1] = BAT_LVL_LOW;
+		battery.thresolds[2] = BAT_LVL_MED;
+		battery.thresolds[3] = BAT_LVL_FULL;*/
+}
+
+void Core::titleScreen(const char* name){
+	titleScreen(name, 0);
+}
+
+void Core::titleScreen(const uint8_t* logo){
+	titleScreen((""), logo);
+}
+
+void Core::titleScreen(){
+	titleScreen((""));
+}
+
+void Core::titleScreen(const char*  name, const uint8_t *logo){
+	display.setFont(font5x7);
+	if(startMenuTimer){
+		display.fontSize = 1;
+		display.textWrap = false;
+		display.persistence = false;
+		battery.show = false;
+		display.setColor(BLACK);
+		while(isRunning()){
+			if(update()){
+				uint8_t logoOffset = name[0]?display.fontHeight:0; //add an offset the logo when there is a name to display
+				//draw graphics
+				display.setColorDepth(1);
+				display.setColor(3);
+				//display.drawBitmap(0,0, gamebuinoLogo);
+				display.setColor(1);
+				if(logo){
+					display.drawBitmap(0, 12+logoOffset, logo);
+				}
+				display.cursorX = 0;
+				display.cursorY = 12;
+				display.print(name);
+
+				//A button
+				display.cursorX = LCDWIDTH - display.fontWidth*3 -1;
+				display.cursorY = LCDHEIGHT - display.fontHeight*3 - 3;
+				if((frameCount/16)%2)
+				  display.println(("\25 \20"));
+				else
+				  display.println(("\25\20 "));
+				//B button
+				display.cursorX = LCDWIDTH - display.fontWidth*3 - 1;
+				display.cursorY++;
+				if(sound.globalVolume)
+					display.println(("\26\23\24"));
+				else
+					display.println(("\26\23x"));
+				//C button
+				display.cursorX = LCDWIDTH - display.fontWidth*3 - 1;
+				display.cursorY++;
+				//display.println(F("\27SD"));
+
+				//toggle volume when B is pressed
+				if(buttons.pressed(BTN_B)){
+					sound.setVolume(sound.getVolume() + 1);
+					sound.playTick();
+				}
+				//leave the menu
+				if(buttons.pressed(BTN_A) || ((frameCount>=startMenuTimer)&&(startMenuTimer != 255))){
+					startMenuTimer = 255; //don't automatically skip the title screen next time it's displayed
+					sound.stopPattern(0);
+					sound.playOK();
+					break;
+				}
+				//flash the loader
+				//if(buttons.pressed(BTN_C))
+					// ToDo changeGame();
+			}
+		}
+		battery.show = true;
+	}
+}
+
+/**
+ * Update all the subsystems, like graphics, audio, events, etc.
+ * Note: the update rect is used for drawing only part of the screen buffer to LCD. Because of speed optimizations,
+ * the x, y, and width of the update rect must be dividable by 4 pixels, and the height must be dividable by 8 pixels.
+ * The update rect is currently used for 220x176, 4 colors, screen mode only.
+ * @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.
+ * @param updRectX The update rect.
+ * @param updRectY The update rect.
+ * @param updRectW The update rect.
+ * @param updRectH The update rect.
+ */
+bool Core::update(bool useDirectMode, uint8_t updRectX, uint8_t updRectY, uint8_t updRectW, uint8_t updRectH) {
+
+    #if POK_STREAMING_MUSIC
+        sound.updateStream();
+    #endif
+
+    uint32_t now = getTime();
+	if ((((nextFrameMillis - now)) > timePerFrame) && frameEndMicros) { //if time to render a new frame is reached and the frame end has ran once
+		nextFrameMillis = now + timePerFrame;
+		frameCount++;
+
+		frameEndMicros = 0;
+		backlight.update();
+		buttons.update();
+		battery.update();
+
+        // FPS counter
+		#if defined(PROJ_USE_FPS_COUNTER) ||  defined(PROJ_SHOW_FPS_COUNTER)
+        const uint32_t fpsInterval_ms = 1000*3;
+
+        fps_frameCount++;
+        if (now > fps_refreshtime) {
+            fps_counter = (1000*fps_frameCount) / (now - fps_refreshtime + fpsInterval_ms);
+            fps_refreshtime = now + fpsInterval_ms;
+            fps_frameCount = 0;
+            fps_counter_updated = true;
+        }
+        #endif
+
+		return true;
+
+	} else {
+		if (!frameEndMicros) { //runs once at the end of the frame
+			#if POK_ENABLE_SOUND > 0
+			sound.updateTrack();
+			sound.updatePattern();
+			sound.updateNote();
+			#endif
+			updatePopup();
+			displayBattery();
+
+            display.update(useDirectMode, updRectX, updRectY, updRectW, updRectH); //send the buffer to the screen
+
+            frameEndMicros = 1; //jonne
+
+		}
+		return false;
+	}
+}
+
+void Core::displayBattery(){
+#if (ENABLE_BATTERY > 0)
+	//display.setColor(BLACK, WHITE);
+	uint8_t ox,oy;
+	ox=display.cursorX;
+	oy=display.cursorY;
+	display.cursorX = LCDWIDTH-display.fontWidth+1;
+	display.cursorY = 0;
+	switch(battery.level){
+	case 0://battery critic, power down
+		sound.stopPattern();
+		backlight.set(0);
+		display.clear();
+		display.fontSize = 1;
+		display.print(("LOW BATTERY\n"));
+		display.print(battery.voltage);
+		display.print(("mV\n\nPLEASE\nTURN OFF"));
+		display.update();
+		break;
+	case 1: //empty battery
+		if((frameCount % 16) < 8) display.print('\7'); //blinking battery
+		else display.print('x');
+		break;
+	case 2://low battery
+	case 3://full battery
+	case 4://full battery
+		if(battery.show){
+			display.print(char(5+battery.level));
+		}
+		break;
+	default:
+		if(battery.show){
+			display.print('/');
+		}
+		break;
+	}
+display.cursorX = ox;
+display.cursorY = oy;
+#endif
+}
+
+char* Core::filemenu(char *ext) {
+    uint8_t oldPersistence = display.persistence;
+    display.persistence = false;
+    uint16_t oldpal0=display.palette[0];
+    uint16_t oldpal1=display.palette[1];
+    uint16_t oldpal2=display.palette[2];
+    display.palette[2]=COLOR_GREEN;
+    display.palette[1]=COLOR_WHITE;
+    display.palette[0]=COLOR_BLACK;
+    uint8_t oldbg=display.bgcolor;
+    uint8_t oldfg=display.color;
+    display.color=1;
+    display.bgcolor=0;
+
+	int16_t rowh = display.fontHeight + 2;
+    int8_t activeItem = 0;
+	int16_t currentTopItem = 0;
+	int16_t itemsPerScreen = (display.height+1) / rowh;
+	int16_t numOfItemsFound = 0;
+	boolean exit = false;
+
+	char* txt;
+
+    pokInitSD();
+
+    bool updated = true;
+	while (isRunning()) {
+		if (update(true)) {
+			if (buttons.pressed(BTN_A) || buttons.pressed(BTN_B) || buttons.pressed(BTN_C)) {
+				exit = true; //time to exit menu !
+				if (buttons.pressed(BTN_A)) {
+					sound.playOK();
+				} else {
+				    *selectedfile = 0;
+					sound.playCancel();
+				}
+				updated = true; // update screen
+			}
+			if (exit == false) {
+				if (buttons.repeat(BTN_DOWN,4)) {
+                    if( ++activeItem >= numOfItemsFound ) activeItem = numOfItemsFound - 1;
+					if( activeItem >= currentTopItem + itemsPerScreen) currentTopItem += itemsPerScreen; // next page
+					sound.playTick();
+                    updated = true; // update screen
+				}
+				if (buttons.repeat(BTN_UP,4)) {
+				    if( --activeItem < 0 ) activeItem = 0;
+ 					if( activeItem < currentTopItem) currentTopItem -= itemsPerScreen;  // previous page
+					sound.playTick();
+                    updated = true; // update screen
+				}
+
+			} else { //exit :
+				    display.bgcolor=oldbg;
+				    display.color=oldfg;
+				    display.palette[0] = oldpal0;
+				    display.palette[1] = oldpal1;
+				    display.palette[2] = oldpal2;
+				    display.persistence = oldPersistence;
+				    return selectedfile;
+			}
+
+			//draw a fancy menu
+			display.textWrap = false;
+			uint16_t fc,bc;
+			fc = display.color;
+            bc = display.bgcolor;
+            if( updated ) { // update screen?
+                #if POK_SIM
+                getFirstFile(ext);
+                #else
+                bool isFirstFile = true;
+                #endif
+                for (int i = 0; ; i++) {
+                    display.cursorX = 0;
+                    display.invisiblecolor=255;
+                    display.cursorY = (i - currentTopItem ) * rowh;
+
+                    // read the file name from SD
+                    #ifndef POK_SIM
+                    if(isFirstFile) {
+                        txt = getFirstFile(ext);
+                        isFirstFile = false;
+                    }
+                    else
+                    #endif
+
+                    txt = getNextFile(ext);
+
+                    if (txt) {
+
+                        numOfItemsFound = i+1;
+
+                        // Draw active line with diffrent backgtound and char color
+                        if (i == activeItem){
+                            display.cursorX = 3;
+                            display.color=2;
+                            display.fillRect(0, display.cursorY - 2, LCDWIDTH, rowh);
+                            display.setColor(0,2);
+                        } else display.setColor(1,0);
+
+                        // break loop if going out of screen
+                        if(i >= currentTopItem + itemsPerScreen ) {
+                            break;
+                        }
+
+                        // Display only if the file is on the current page
+                        if( i >= currentTopItem) {
+                            display.print(display.cursorX, display.cursorY, txt);
+                            if (i == activeItem)
+                                strcpy(selectedfile,txt);
+                        }
+                    } else
+                        break; // break loop as no more files found
+
+                    display.setColor(1,0);
+                } // end for
+
+                display.update();
+            }
+            updated = false;
+		} // update
+
+        display.setColor(1,0);
+	}
+	return 0;
+}
+
+char* Core::filemenu() {
+    return filemenu("");
+}
+
+int8_t Core::menu(const char* const* items, uint8_t length) {
+#if (ENABLE_GUI > 0)
+	display.persistence = false;
+	int8_t activeItem = 0;
+	int16_t currentY = display.height;
+	int16_t targetY = 0, rowh = display.fontHeight + 2;
+	boolean exit = false;
+	int8_t answer = -1;
+	while (isRunning()) {
+		if (update()) {
+			if (buttons.pressed(BTN_A) || buttons.pressed(BTN_B) || buttons.pressed(BTN_C)) {
+				exit = true; //time to exit menu !
+				targetY = - display.fontHeight * length - 2; //send the menu out of the screen
+				if (buttons.pressed(BTN_A)) {
+					answer = activeItem;
+					sound.playOK();
+				} else {
+					sound.playCancel();
+				}
+			}
+			if (exit == false) {
+				if (buttons.repeat(BTN_DOWN,4)) {
+					activeItem++;
+					sound.playTick();
+				}
+				if (buttons.repeat(BTN_UP,4)) {
+					activeItem--;
+					sound.playTick();
+				}
+				//don't go out of the menu
+				if (activeItem == length) activeItem = 0;
+				if (activeItem < 0) activeItem = length - 1;
+
+				targetY = -rowh * activeItem + (rowh+4); //center the menu on the active item
+			} else { //exit :
+				if ((currentY - targetY) <= 1)
+				return (answer);
+			}
+			//draw a fancy menu
+			currentY = (currentY + targetY) / 2;
+			display.cursorX = 0;
+			display.cursorY = currentY;
+			display.textWrap = false;
+			uint16_t fc,bc;
+			fc = display.color;
+            bc = display.bgcolor;
+			for (byte i = 0; i < length; i++) {
+				display.cursorY = currentY + rowh * i;
+				if (i == activeItem){
+					display.cursorX = 3;
+
+                    //display.fillRoundRect(0, currentY + display.fontHeight * activeItem - 2, LCDWIDTH, (display.fontHeight+3), 3);
+                    display.fillRect(0, currentY + rowh * activeItem - 2, LCDWIDTH, (rowh));
+                    display.setColor(bc,fc);
+				} else display.setColor(fc,bc);
+
+				display.println((char*)*(const unsigned int*)(items+i));
+				display.setColor(fc,bc);
+			}
+
+		}
+	}
+#else
+	return 0;
+#endif
+	return 0;
+}
+
+void Core::keyboard(char* text, uint8_t length) {
+#if (ENABLE_GUI > 0)
+	display.persistence = false;
+	//memset(text, 0, length); //clear the text
+	text[length-1] = '\0';
+	//active character in the typing area
+	int8_t activeChar = 0;
+	//selected char on the keyboard
+	int8_t activeX = 0;
+	int8_t activeY = 2;
+	//position of the keyboard on the screen
+	int8_t currentX = LCDWIDTH;
+	int8_t currentY = LCDHEIGHT;
+	int8_t targetX = 0;
+	int8_t targetY = 0;
+
+	while (1) {
+		if (update()) {
+			//move the character selector
+			if (buttons.repeat(BTN_DOWN, 4)) {
+				activeY++;
+				sound.playTick();
+			}
+			if (buttons.repeat(BTN_UP, 4)) {
+				activeY--;
+				sound.playTick();
+			}
+			if (buttons.repeat(BTN_RIGHT, 4)) {
+				activeX++;
+				sound.playTick();
+			}
+			if (buttons.repeat(BTN_LEFT, 4)) {
+				activeX--;
+				sound.playTick();
+			}
+			//don't go out of the keyboard
+			if (activeX == KEYBOARD_W) activeX = 0;
+			if (activeX < 0) activeX = KEYBOARD_W - 1;
+			if (activeY == KEYBOARD_H) activeY = 0;
+			if (activeY < 0) activeY = KEYBOARD_H - 1;
+			//set the keyboard position on screen
+			targetX = -(display.fontWidth+1) * activeX + LCDWIDTH / 2 - 3;
+			targetY = -(display.fontHeight+1) * activeY + LCDHEIGHT / 2 - 4 - display.fontHeight;
+			//smooth the keyboard displacement
+			currentX = (targetX + currentX) / 2;
+			currentY = (targetY + currentY) / 2;
+			//type character
+			if (buttons.pressed(BTN_A)) {
+				if (activeChar < (length-1)) {
+					byte thisChar = activeX + KEYBOARD_W * activeY;
+					if((thisChar == 0)||(thisChar == 10)||(thisChar == 13)) //avoid line feed and carriage return
+					continue;
+					text[activeChar] = thisChar;
+					text[activeChar+1] = '\0';
+				}
+				activeChar++;
+				sound.playOK();
+				if (activeChar > length)
+				activeChar = length;
+			}
+			//erase character
+			if (buttons.pressed(BTN_B)) {
+				activeChar--;
+				sound.playCancel();
+				if (activeChar >= 0)
+				text[activeChar] = 0;
+				else
+				activeChar = 0;
+			}
+			//leave menu
+			if (buttons.pressed(BTN_C)) {
+				sound.playOK();
+				while (1) {
+					if (update()) {
+						//display.setCursor(0,0);
+						display.println(("You entered\n"));
+						display.print(text);
+						display.println(("\n\n\n\x15:okay \x16:edit"));
+						if(buttons.pressed(BTN_A)){
+							sound.playOK();
+							return;
+						}
+						if(buttons.pressed(BTN_B)){
+							sound.playCancel();
+							break;
+						}
+					}
+				}
+			}
+			//draw the keyboard
+			for (int8_t y = 0; y < KEYBOARD_H; y++) {
+				for (int8_t x = 0; x < KEYBOARD_W; x++) {
+					display.drawChar(currentX + x * (display.fontWidth+1), currentY + y * (display.fontHeight+1), x + y * KEYBOARD_W, 1);
+				}
+			}
+			//draw instruction
+			display.cursorX = currentX-display.fontWidth*6-2;
+			display.cursorY = currentY+1*(display.fontHeight+1);
+			display.print(("\25type"));
+
+			display.cursorX = currentX-display.fontWidth*6-2;
+			display.cursorY = currentY+2*(display.fontHeight+1);
+			display.print(("\26back"));
+
+			display.cursorX = currentX-display.fontWidth*6-2;
+			display.cursorY = currentY+3*(display.fontHeight+1);
+			display.print(("\27save"));
+
+			//erase some pixels around the selected character
+			display.setColor(WHITE);
+			display.drawFastHLine(currentX + activeX * (display.fontWidth+1) - 1, currentY + activeY * (display.fontHeight+1) - 2, 7);
+			//draw the selection rectangle
+			display.setColor(BLACK);
+			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);
+			//draw keyboard outline
+			//display.drawRoundRect(currentX - 6, currentY - 6, KEYBOARD_W * (display.fontWidth+1) + 12, KEYBOARD_H * (display.fontHeight+1) + 12, 8, BLACK);
+			//text field
+			display.drawFastHLine(0, LCDHEIGHT-display.fontHeight-2, LCDWIDTH);
+			display.setColor(WHITE);
+			display.fillRect(0, LCDHEIGHT-display.fontHeight-1, LCDWIDTH, display.fontHeight+1);
+			//typed text
+			display.cursorX = 0;
+			display.cursorY = LCDHEIGHT-display.fontHeight;
+			display.setColor(BLACK);
+			display.print(text);
+			//blinking cursor
+			if (((frameCount % 8) < 4) && (activeChar < (length-1)))
+			display.drawChar(display.fontWidth * activeChar, LCDHEIGHT-display.fontHeight, '_',1);
+		}
+	}
+#endif
+}
+
+void Core::popup(const char* text, uint8_t duration){
+#if (ENABLE_GUI > 0)
+	popupText = text;
+	popupTimeLeft = duration+12;
+#endif
+}
+
+void Core::updatePopup(){
+#if (ENABLE_GUI > 0)
+	if (popupTimeLeft){
+		uint8_t yOffset = 0;
+		if(popupTimeLeft<12){
+			yOffset = 12-popupTimeLeft;
+		}
+		display.fontSize = 1;
+		display.setColor(WHITE);
+		display.fillRoundRect(0,LCDHEIGHT-display.fontHeight+yOffset-3,84,display.fontHeight+3,3);
+		display.setColor(BLACK);
+		display.drawRoundRect(0,LCDHEIGHT-display.fontHeight+yOffset-3,84,display.fontHeight+3,3);
+		display.cursorX = 4;
+		display.cursorY = LCDHEIGHT-display.fontHeight+yOffset-1;
+		display.print(popupText);
+		popupTimeLeft--;
+	}
+#endif
+}
+
+void Core::setFrameRate(uint8_t fps) {
+	timePerFrame = 1000 / fps;
+	sound.prescaler = fps / 20;
+	sound.prescaler = __avrmax(1, sound.prescaler);
+}
+
+void Core::pickRandomSeed(){
+        initRandom();
+}
+
+bool Core::collidePointRect(int16_t x1, int16_t y1 ,int16_t x2 ,int16_t y2, int16_t w, int16_t h){
+	if((x1>=x2)&&(x1<x2+w))
+	if((y1>=y2)&&(y1<y2+h))
+	return true;
+	return false;
+}
+
+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){
+  return !( x2     >=  x1+w1  ||
+            x2+w2  <=  x1     ||
+            y2     >=  y1+h1  ||
+            y2+h2  <=  y1     );
+}
+
+bool Core::collideBitmapBitmap(int16_t x1, int16_t y1, const uint8_t* b1, int16_t x2, int16_t y2, const uint8_t* b2){
+  int16_t w1 = pgm_read_byte(b1);
+  int16_t h1 = pgm_read_byte(b1 + 1);
+  int16_t w2 = pgm_read_byte(b2);
+  int16_t h2 = pgm_read_byte(b2 + 1);
+
+  if(collideRectRect(x1, y1, w1, h1, x2, y2, w2, h2) == false){
+    return false;
+  }
+
+  int16_t xmin = (x1>=x2)? 0 : x2-x1;
+  int16_t ymin = (y1>=y2)? 0 : y2-y1;
+  int16_t xmax = (x1+w1>=x2+w2)? x2+w2-x1 : w1;
+  int16_t ymax = (y1+h1>=y2+h2)? y2+h2-y1 : h1;
+  for(uint8_t y = ymin; y < ymax; y++){
+    for(uint8_t x = xmin; x < xmax; x++){
+      if(display.getBitmapPixel(b1, x, y) && display.getBitmapPixel(b2, x1+x-x2, y1+y-y2)){
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+
+//** EOF **//
+
+
+
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/POKITTO_CORE/PokittoSound.cpp	Mon May 21 18:03:14 2018 +0000
@@ -0,0 +1,942 @@
+/**************************************************************************/
+/*!
+    @file     PokittoSound.cpp
+    @author   Jonne Valola
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2016, Jonne Valola
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+/*
+ * NOTE:
+ * API of the Pokitto Sound library is partially identical to the Gamebuino Sound API.
+ * Due to the difference in architecture (ARM Cortex-M0+ in mbed environment vs. 8-bit AVR)
+ * large parts are not identical. Most functions were rewritten, with only API remaining.
+ * We want to give attribution to the original author's project:
+ *
+ * License for Gamebuino-identical code:
+ *
+ * (C) Copyright 2014 Aurélien Rodot. All rights reserved.
+ *
+ * This file is part of the Gamebuino Library (http://gamebuino.com)
+ *
+ * The Gamebuino Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "PokittoSound.h"
+#include "Pokitto_settings.h"
+#include "PokittoCore.h"
+#include "Synth.h"
+
+#ifndef POK_SIM
+#include "HWSound.h"
+#else
+#include "SimSound.h"
+#include "PokittoSimulator.h"
+#endif
+
+typedef uint8_t byte;
+
+using namespace Pokitto;
+
+/** discrete hardware volume control **/
+
+uint8_t Pokitto::discrete_vol = 0;
+uint8_t const Pokitto::discrete_vol_levels[8]      {0,32,64,96,128,160,192,224};
+uint8_t const Pokitto::discrete_vol_hw_levels[8]   {0,27,64,96,36,117,127,127};
+uint8_t const Pokitto::discrete_vol_multipliers[8] {0,127,127,127,192,192,255,255};
+
+Pokitto::Core _soundc;
+
+uint8_t Sound::prescaler;
+uint16_t Sound::globalVolume;
+uint16_t Sound::volumeMax = VOLUME_HEADPHONE_MAX;
+uint8_t Sound::headPhoneLevel=1;
+
+bool Sound::trackIsPlaying[NUM_CHANNELS];
+bool Sound::patternIsPlaying[NUM_CHANNELS];
+uint8_t Sound::outputPitch[NUM_CHANNELS];
+int8_t Sound::outputVolume[NUM_CHANNELS];
+
+uint16_t *Sound::trackData[NUM_CHANNELS];
+uint8_t Sound::trackCursor[NUM_CHANNELS];
+uint16_t **Sound::patternSet[NUM_CHANNELS];
+int8_t Sound::patternPitch[NUM_CHANNELS];
+
+// pattern data
+uint16_t *Sound::patternData[NUM_CHANNELS];
+uint16_t **Sound::instrumentSet[NUM_CHANNELS];
+bool Sound::patternLooping[NUM_CHANNELS];
+uint16_t Sound::patternCursor[NUM_CHANNELS];
+
+// note data
+uint8_t Sound::notePitch[NUM_CHANNELS];
+uint8_t Sound::noteDuration[NUM_CHANNELS];
+int8_t Sound::noteVolume[NUM_CHANNELS];
+bool Sound::notePlaying[NUM_CHANNELS];
+
+// commands data
+int8_t Sound::commandsCounter[NUM_CHANNELS];
+int8_t Sound::volumeSlideStepDuration[NUM_CHANNELS];
+int8_t Sound::volumeSlideStepSize[NUM_CHANNELS];
+uint8_t Sound::arpeggioStepDuration[NUM_CHANNELS];
+int8_t Sound::arpeggioStepSize[NUM_CHANNELS];
+uint8_t Sound::tremoloStepDuration[NUM_CHANNELS];
+int8_t Sound::tremoloStepSize[NUM_CHANNELS];
+
+// instrument data
+uint16_t *Sound::instrumentData[NUM_CHANNELS];
+uint8_t Sound::instrumentLength[NUM_CHANNELS]; //number of steps in the instrument
+uint8_t Sound::instrumentLooping[NUM_CHANNELS]; //how many steps to loop on when the last step of the instrument is reached
+uint16_t Sound::instrumentCursor[NUM_CHANNELS]; //which step is being played
+uint8_t Sound::instrumentNextChange[NUM_CHANNELS]; //how many frames before the next step
+
+//current step data
+int8_t Sound::stepVolume[NUM_CHANNELS];
+uint8_t Sound::stepPitch[NUM_CHANNELS];
+uint8_t Sound::chanVolumes[NUM_CHANNELS];
+
+#if (POK_ENABLE_SOUND < 1)
+ #define NUM_CHANNELS 0
+#endif
+
+#if(NUM_CHANNELS > 0)
+    #ifndef POK_SIM
+        uint32_t sbyte;
+    #else
+    uint32_t sbyte;
+    float pwm1;
+    #endif // POK_SIM
+
+//declare these variables globally for faster access
+uint8_t _rand = 1;
+uint8_t _chanCount[NUM_CHANNELS]; //counts until the next change of the waveform
+bool _chanState[NUM_CHANNELS]; //if the waveform is currently high or low
+uint8_t _chanHalfPeriod[NUM_CHANNELS]; //duration of half the period of the waveform
+uint8_t _chanOutputVolume[NUM_CHANNELS]; //amplitude of the outputted waveform
+uint8_t _chanOutput[NUM_CHANNELS]; //current value of the outputted waveform
+bool _chanNoise[NUM_CHANNELS]; //if a random value should be added to the waveform to generate noise
+
+#if POK_GBSOUND > 0
+const uint16_t squareWaveInstrument[]  = {0x0101, 0x03F7};
+const uint16_t noiseInstrument[]  = {0x0101, 0x03FF};
+const uint16_t* const defaultInstruments[]  = {squareWaveInstrument,noiseInstrument};
+
+const uint16_t playOKPattern[]  = {0x0005,0x138,0x168,0x0000};
+const uint16_t playCancelPattern[]  = {0x0005,0x168,0x138,0x0000};
+const uint16_t playTickP[]  = {0x0045,0x168,0x0000};
+#endif
+#if(EXTENDED_NOTE_RANGE > 0)
+//extended note range
+#define NUM_PITCH 59
+const uint8_t _halfPeriods[NUM_PITCH] = {246,232,219,207,195,184,174,164,155,146,138,130,123,116,110,104,98,92,87,82,78,73,69,65,62,58,55,52,49,46,44,41,39,37,35,33,31,29,28,26,25,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6};
+#else
+//regular note range
+#define NUM_PITCH 36
+const uint8_t _halfPeriods[NUM_PITCH] = {246,232,219,207,195,184,174,164,155,146,138,130,123,116,110,104,98,92,87,82,78,73,69,65,62,58,55,52,49,46,44,41,39,37,35,33};
+#endif
+
+#endif
+
+void Pokitto::audio_IRQ() {
+    #if POK_STREAMING_MUSIC > 0
+        #if POK_STREAMFREQ_HALVE > 0
+        streamstep = 1-streamstep;
+        #else
+        streamstep=1;
+        #endif
+
+        streamstep &= streamon; //check if stream is on
+
+        if(streamvol && streamstep) {
+            uint8_t output = (*currentPtr++);
+            sbyte = output;
+        } else {
+            sbyte = 0; // duty cycle
+        }
+
+        if (currentPtr >= endPtr)
+        {
+        currentBuffer++;
+        if (currentBuffer==4) currentBuffer=0;
+        currentPtr = buffers[currentBuffer];
+        endPtr = currentPtr + BUFFER_SIZE;
+        }
+
+    #endif // POK_STREAMING_MUSIC
+    #if (NUM_CHANNELS > 0)
+	Sound::generateOutput();
+    #endif
+}
+
+void Sound::volumeUp() {
+    Pokitto::discrete_vol++;
+    if (discrete_vol>7) discrete_vol=7;
+    globalVolume = discrete_vol_levels[discrete_vol];
+    setVolume(globalVolume);
+    //if (globalVolume>VOLUME_HEADPHONE_MAX) setVolume(getVolume()+VOLUME_STEP*2);
+    //else setVolume(getVolume()+VOLUME_STEP);
+}
+
+void Sound::volumeDown() {
+    if (discrete_vol) Pokitto::discrete_vol--;
+    globalVolume = discrete_vol_levels[discrete_vol];
+    setVolume(globalVolume);
+    //if (globalVolume>VOLUME_HEADPHONE_MAX) setVolume(getVolume()-VOLUME_STEP*4);
+    //else setVolume(getVolume()-VOLUME_STEP);
+}
+
+
+void Sound::setMaxVol(int16_t v) {
+    if (v < 0) v = 0; //prevent nasty wraparound
+    if (v > VOLUME_SPEAKER_MAX) {
+            v = VOLUME_SPEAKER_MAX;
+    }
+    volumeMax = v;
+    setVolume(globalVolume);
+}
+
+uint16_t Sound::getMaxVol() {
+    return volumeMax;
+}
+
+void Sound::updateStream() {
+     #if POK_STREAMING_MUSIC
+    if (oldBuffer != currentBuffer) {
+        if (currentBuffer==0) fileReadBytes(&buffers[3][0],BUFFER_SIZE);
+        else if (currentBuffer==1) fileReadBytes(&buffers[0][0],BUFFER_SIZE);
+        else if (currentBuffer==2) fileReadBytes(&buffers[1][0],BUFFER_SIZE);
+        else fileReadBytes(&buffers[2][0],BUFFER_SIZE);
+        oldBuffer = currentBuffer;
+        streamcounter += BUFFER_SIZE;
+    }
+
+    #ifndef POK_SIM
+    if ( streamcounter > fs.fsize - (BUFFER_SIZE)) {
+    #else
+    if ( streamcounter > getFileLength() - (BUFFER_SIZE)) {
+    #endif
+        streamcounter=0;
+        #if POK_STREAM_LOOP > 0
+        fileRewind();
+        #else
+            #ifndef POK_SIM
+                streamon=0;
+            #endif // POK_SIM
+        #endif // POK_STREAM_LOOP
+    }
+    #endif
+}
+
+void Sound::begin() {
+#if POK_ENABLE_SOUND > 0
+soundInit();
+ampEnable(true);
+#endif
+#if (NUM_CHANNELS > 0)
+#if POK_ENABLE_SOUND > 0
+#if POK_GBSOUND > 0
+	prescaler = 1;
+	for(byte i=0; i<NUM_CHANNELS; i++){
+		chanVolumes[i] = VOLUME_CHANNEL_MAX;
+		changeInstrumentSet(defaultInstruments, i); //load default instruments. #0:square wave, #1: noise
+		command(CMD_INSTRUMENT, 0, 0, i); //set the default instrument to square wave
+	}
+#endif // POK_GBSOUND
+#endif //POK_ENABLE_SOUND
+#endif
+}
+
+void Sound::playTrack(const uint16_t* track, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	stopTrack(channel);
+	trackCursor[channel] = 0;
+	trackData[channel] = (uint16_t*)track;
+	trackIsPlaying[channel] = true;
+#endif
+}
+
+void Sound::stopTrack(uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	trackIsPlaying[channel] = false;
+	stopPattern(channel);
+#endif
+}
+
+void Sound::stopTrack(){
+#if(NUM_CHANNELS > 0)
+	for(uint8_t i=0; i<NUM_CHANNELS; i++){
+		stopTrack(i);
+	}
+#endif
+}
+
+void Sound::updateTrack(uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	if(trackIsPlaying[channel] && !patternIsPlaying[channel]){
+		uint16_t data = pgm_read_word(trackData[channel] + trackCursor[channel]);
+		if(data == 0xFFFF){ //en of the track
+			trackIsPlaying[channel] = false;
+			return;
+		}
+		uint8_t patternID = data & 0xFF;
+		data >>= 8;
+		patternPitch[channel] = data;
+		playPattern((const uint16_t*)pgm_read_word(&(patternSet[channel][patternID])), channel);
+		trackCursor[channel] ++;
+	}
+#endif
+}
+
+void Sound::updateTrack(){
+#if(NUM_CHANNELS > 0)
+	for (uint8_t i=0; i<NUM_CHANNELS; i++){
+		updateTrack(i);
+	}
+#endif
+}
+
+void Sound::changePatternSet(const uint16_t* const* patterns, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	patternSet[channel] = (uint16_t**)patterns;
+#endif
+}
+
+void Sound::playPattern(const uint16_t* pattern, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	stopPattern(channel);
+	patternData[channel] = (uint16_t*)pattern;
+	patternCursor[channel] = 0;
+	patternIsPlaying[channel] = true;
+	noteVolume[channel] = 9;
+	//reinit commands
+	volumeSlideStepDuration[channel] = 0;
+	arpeggioStepDuration[channel] = 0;
+	tremoloStepDuration[channel] = 0;
+#endif
+}
+
+void Sound::updatePattern(){
+#if(NUM_CHANNELS > 0)
+	for (uint8_t i=0; i<NUM_CHANNELS; i++){
+		updatePattern(i);
+	}
+#endif
+}
+
+void Sound::changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	instrumentSet[channel] = (uint16_t**)instruments;
+#endif
+}
+
+void Sound::updatePattern(uint8_t i){
+#if(NUM_CHANNELS > 0)
+	if(i>=NUM_CHANNELS)
+		return;
+	if(patternIsPlaying[i]){
+		if(noteDuration[i]==0){//if the end of the previous note is reached
+
+			uint16_t data = pgm_read_word(patternCursor[i] + patternData[i]);
+
+			if(data == 0){ //end of the pattern reached
+				if(patternLooping[i] == true){
+					patternCursor[i] = 0;
+					data = pgm_read_word(patternCursor[i] + patternData[i]);
+				}
+				else{
+					patternIsPlaying[i] = false;
+					if(trackIsPlaying[i]){ //if this pattern is part of a track, get the next pattern
+						updateTrack(i);
+						data = pgm_read_word(patternCursor[i] + patternData[i]);
+					} else {
+						stopNote(i);
+						//Serial.print("pattern end\n");
+						return;
+					}
+				}
+			}
+
+			while (data & 0x0001){ //read all commands and instrument changes
+				data >>= 2;
+				//Serial.print("\ncmd\t");
+				uint8_t cmd = data & 0x0F;
+				data >>= 4;
+				uint8_t X = data & 0x1F;
+				data >>= 5;
+				int8_t Y = data - 16;
+				command(cmd,X,Y,i);
+				patternCursor[i]++;
+				data = pgm_read_word(patternCursor[i] + patternData[i]);
+			}
+			data >>= 2;
+
+			uint8_t pitch = data & 0x003F;
+			data >>= 6;
+
+			uint8_t duration = data;
+			if(pitch != 63){
+			}
+
+			playNote(pitch, duration, i);
+
+			patternCursor[i]++;
+		}
+	}
+#endif
+}
+
+void Sound::stopPattern(uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	stopNote(channel);
+	patternIsPlaying[channel] = false;
+#endif
+}
+
+void Sound::stopPattern(){
+#if(NUM_CHANNELS > 0)
+	for(uint8_t i=0; i<NUM_CHANNELS; i++){
+		stopPattern(i);
+	}
+#endif
+}
+
+void Sound::command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t i){
+#if(NUM_CHANNELS > 0)
+	if(i>=NUM_CHANNELS)
+		return;
+	switch(cmd){
+	case CMD_VOLUME: //volume
+	    X = constrain((int8_t)X, 0, 10);
+		noteVolume[i] = X;
+		break;
+	case CMD_INSTRUMENT: //instrument
+		instrumentData[i] = (uint16_t*)pgm_read_word(&(instrumentSet[i][X]));
+		instrumentLength[i] = pgm_read_word(&(instrumentData[i][0])) & 0x00FF; //8 LSB
+		instrumentLength[i] *= prescaler;
+		instrumentLooping[i] = min2((pgm_read_word(&(instrumentData[i][0])) >> 8), instrumentLength[i]); //8 MSB - check that the loop is shorter than the instrument length
+		instrumentLooping[i] *= prescaler;
+		break;
+	case CMD_SLIDE: //volume slide
+		volumeSlideStepDuration[i] = X * prescaler;
+		volumeSlideStepSize[i] = Y;
+		break;
+	case CMD_ARPEGGIO:
+		arpeggioStepDuration[i] = X * prescaler;
+		arpeggioStepSize[i] = Y;
+		break;
+	case CMD_TREMOLO:
+		tremoloStepDuration[i] = X * prescaler;
+		tremoloStepSize[i] = Y;
+		break;
+	default:
+		break;
+	}
+#endif
+}
+
+void Sound::playNote(uint8_t pitch, uint8_t duration, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	//set note
+	notePitch[channel] = pitch;
+	noteDuration[channel] = duration * prescaler;
+	//reinit vars
+	instrumentNextChange[channel] = 0;
+	instrumentCursor[channel] = 0;
+	notePlaying[channel] = true;
+	_chanState[channel] = true;
+	commandsCounter[channel] = 0;
+#endif
+}
+
+void Sound::stopNote(uint8_t channel) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	notePlaying[channel] = false;
+	//counters
+	noteDuration[channel] = 0;
+	instrumentCursor[channel] = 0;
+	commandsCounter[channel] = 0;
+	//output
+	_chanOutput[channel] = 0;
+	_chanOutputVolume[channel] = 0;
+	_chanState[channel] = false;
+	updateOutput();
+#endif
+}
+
+void Sound::stopNote() {
+#if(NUM_CHANNELS > 0)
+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
+		stopNote(channel);
+	}
+#endif
+}
+
+void Sound::updateNote() {
+#if(NUM_CHANNELS > 0)
+	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
+		updateNote(i);
+	}
+#endif
+}
+
+void Sound::updateNote(uint8_t i) {
+#if(NUM_CHANNELS > 0)
+	if(i>=NUM_CHANNELS)
+		return;
+	if (notePlaying[i]) {
+
+		if(noteDuration[i] == 0){
+			stopNote(i);
+			//Serial.println("note end");
+			return;
+		} else {
+			noteDuration[i]--;
+		}
+
+		if (instrumentNextChange[i] == 0) {
+
+			//read the step data from the progmem and decode it
+			uint16_t thisStep = pgm_read_word(&(instrumentData[i][1 + instrumentCursor[i]]));
+
+			stepVolume[i] = thisStep & 0x0007;
+			thisStep >>= 3;
+
+			uint8_t stepNoise = thisStep & 0x0001;
+			thisStep >>= 1;
+
+			uint8_t stepDuration = thisStep & 0x003F;
+			thisStep >>= 6;
+
+			stepPitch[i] = thisStep;
+
+			//apply the step settings
+			instrumentNextChange[i] = stepDuration * prescaler;
+
+			_chanNoise[i] = stepNoise;
+
+
+			instrumentCursor[i]++;
+
+			if (instrumentCursor[i] >= instrumentLength[i]) {
+				if (instrumentLooping[i]) {
+					instrumentCursor[i] = instrumentLength[i] - instrumentLooping[i];
+				} else {
+					stopNote(i);
+				}
+			}
+		}
+		instrumentNextChange[i]--;
+
+		commandsCounter[i]++;
+
+		//UPDATE VALUES
+		//pitch
+		outputPitch[i] = notePitch[i] + stepPitch[i] + patternPitch[i];
+		if(arpeggioStepDuration[i]){
+		  outputPitch[i] += commandsCounter[i] / arpeggioStepDuration[i] * arpeggioStepSize[i];
+		}
+		outputPitch[i] = (outputPitch[i] + NUM_PITCH) % NUM_PITCH; //wrap
+		//volume
+		outputVolume[i] = noteVolume[i];
+		if(volumeSlideStepDuration[i]){
+		  outputVolume[i] += commandsCounter[i] / volumeSlideStepDuration[i] * volumeSlideStepSize[i];
+		}
+		if(tremoloStepDuration[i]){
+			outputVolume[i] += ((commandsCounter[i]/tremoloStepDuration[i]) % 2) * tremoloStepSize[i];
+		}
+		outputVolume[i] = constrain(outputVolume[i], 0, 9);
+		if(notePitch[i] == 63){
+			outputVolume[i] = 0;
+		}
+		// jonnehw noInterrupts();
+		_chanHalfPeriod[i] = pgm_read_byte(_halfPeriods + outputPitch[i]);
+		_chanOutput[i] = _chanOutputVolume[i] = outputVolume[i] * (globalVolume>>GLOBVOL_SHIFT) * chanVolumes[i] * stepVolume[i];
+		//Serial.println(outputVolume[i]);
+		// jonnehw interrupts();
+	}
+#endif
+}
+
+void Sound::setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	_chanHalfPeriod[channel] = halfPeriod;
+	_chanState[channel] = false;
+	_chanCount[channel] = 0;
+	updateOutput();
+#endif
+}
+
+
+void Sound::generateOutput() {
+#if(NUM_CHANNELS > 0)
+	bool outputChanged = false;
+	//no for loop here, for the performance sake (this function runs 15 000 times per second...)
+	//CHANNEL 0
+	if (_chanOutputVolume[0]) {
+		_chanCount[0]++;
+		if (_chanCount[0] >= _chanHalfPeriod[0]) {
+			outputChanged = true;
+			_chanState[0] = !_chanState[0];
+			_chanCount[0] = 0;
+			if (_chanNoise[0]) {
+				_rand = 67 * _rand + 71;
+				_chanOutput[0] = _rand % _chanOutputVolume[0];
+			}
+		}
+	}
+
+
+	//CHANNEL 1
+	#if (NUM_CHANNELS > 1)
+	if (_chanOutputVolume[1]) {
+		_chanCount[1]++;
+		if (_chanCount[1] >= _chanHalfPeriod[1]) {
+			outputChanged = true;
+			_chanState[1] = !_chanState[1];
+			_chanCount[1] = 0;
+			if (_chanNoise[1]) {
+				_rand = 67 * _rand + 71;
+				_chanOutput[1] = _rand % _chanOutputVolume[1];
+			}
+		}
+	}
+	#endif
+
+	//CHANNEL 2
+	#if (NUM_CHANNELS > 2)
+	if (_chanOutputVolume[2]) {
+		_chanCount[2]++;
+		if (_chanCount[2] >= _chanHalfPeriod[2]) {
+			outputChanged = true;
+			_chanState[2] = !_chanState[2];
+			_chanCount[2] = 0;
+			if (_chanNoise[2]) {
+				_rand = 67 * _rand + 71;
+				_chanOutput[2] = _rand % _chanOutputVolume[2];
+			}
+		}
+	}
+	#endif
+
+	//CHANNEL 3
+	#if (NUM_CHANNELS > 3)
+	if (_chanOutputVolume[3]) {
+		_chanCount[3]++;
+		if (_chanCount[3] >= _chanHalfPeriod[3]) {
+			outputChanged = true;
+			_chanState[3] = !_chanState[3];
+			_chanCount[3] = 0;
+			if (_chanNoise[3]) {
+				_rand = 67 * _rand + 71;
+				_chanOutput[3] = _rand % _chanOutputVolume[3];
+			}
+		}
+	}
+	#endif
+
+    #if POK_STREAMING_MUSIC
+        if (streamstep) {
+            outputChanged=true;
+        }
+    #endif
+
+	if (outputChanged) {
+		updateOutput();
+	}
+#endif
+}
+
+void Sound::updateOutput() {
+#if(NUM_CHANNELS > 0)
+	uint32_t output = 0;
+
+	//CHANNEL 0
+	if (_chanState[0]) {
+		output += _chanOutput[0];
+	}
+
+	//CHANNEL 1
+	#if (NUM_CHANNELS > 1)
+	if (_chanState[1]) {
+		output += _chanOutput[1];
+	}
+	#endif
+
+	//CHANNEL 2
+	#if (NUM_CHANNELS > 2)
+	if (_chanState[2]) {
+		output += _chanOutput[2];
+	}
+	#endif
+
+	//CHANNEL 3
+	#if (NUM_CHANNELS > 3)
+	if (_chanState[3]) {
+		output += _chanOutput[3];
+	}
+	#endif
+
+    #ifndef POK_SIM
+    #if POK_ENABLE_SOUND
+    /** HARDWARE **/
+
+        #if POK_STREAMING_MUSIC
+            if (streamstep) {
+                //pwmout_write(&audiopwm,(float)(sbyte>>headPhoneLevel)/(float)255);
+                sbyte *= discrete_vol_multipliers[discrete_vol];
+                sbyte >>= 8;
+                pwmout_write(&audiopwm,(float)(sbyte)/(float)255);
+            }
+        #endif
+            output *= discrete_vol_multipliers[discrete_vol];
+            output >>= 8;
+            dac_write((uint8_t)output); //direct hardware mixing baby !
+    soundbyte = output;
+    #endif //POK_ENABLE_SOUND
+    #else
+    /** SIMULATOR **/
+        #if POK_STREAMING_MUSIC
+            if (streamstep) {
+                uint16_t o = output + sbyte;
+                output = (o/2);//>>headPhoneLevel;
+            }
+        #endif
+        soundbyte = output;//<<headPhoneLevel;
+    #endif // POK_SIM
+#endif
+}
+
+void Sound::setPatternLooping(bool loop, uint8_t channel) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	patternLooping[channel] = loop;
+#endif
+}
+
+void Sound::playOK(){
+    #if POK_GBSOUND
+#if(NUM_CHANNELS > 0)
+	playPattern(playOKPattern,0);
+#endif
+#endif // POK_GBSOUND
+}
+
+void Sound::playCancel(){
+#if POK_GBSOUND
+#if(NUM_CHANNELS > 0)
+	playPattern(playCancelPattern,0);
+#endif
+#endif
+}
+
+void Sound::playTick(){
+#if POK_GBSOUND
+#if(NUM_CHANNELS > 0)
+	playPattern(playTickP,0);
+#endif
+#endif // POK_GBSOUND
+}
+
+void Sound::setVolume(int16_t volume) {
+//#if NUM_CHANNELS > 0
+	if (volume<0) volume = 0;
+	//if (volume>volumeMax) volume = volumeMax;
+	globalVolume = volume;
+	#if POK_ENABLE_SOUND > 0
+	discrete_vol = (volume>>5);
+	#ifndef POK_SIM
+	setHWvolume(discrete_vol_hw_levels[discrete_vol]); //boost volume if headphonelevel
+	#endif
+	#endif
+	#if POK_SHOW_VOLUME > 0
+	_soundc.volbar_visible = VOLUMEBAR_TIMEOUT;
+	#endif
+//#endif
+}
+
+uint16_t Sound::getVolume() {
+//#if NUM_CHANNELS > 0
+	return globalVolume;
+//#else
+//	return 0;
+//#endif
+}
+
+void Sound::setVolume(int8_t volume, uint8_t channel) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	volume = (volume > VOLUME_CHANNEL_MAX) ? VOLUME_CHANNEL_MAX : volume;
+	volume = (volume < 0) ? 0 : volume;
+	chanVolumes[channel] = volume;
+#endif
+}
+
+uint8_t Sound::getVolume(uint8_t channel) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return 255;
+	return (chanVolumes[channel]);
+#else
+	return 0;
+#endif
+}
+
+void Sound::playTone(uint8_t os, int frq, uint8_t amp, uint8_t wav,uint8_t arpmode)
+{
+    if (wav>5) wav=0;
+    if (arpmode>MAX_ARPMODE) arpmode=MAX_ARPMODE;
+    if (os==1) setOSC(&osc1,1,wav,1,0,0,frq,amp,0,0,0,0,0,0,arpmode,0,0);
+    else if (os==2) setOSC(&osc2,1,wav,1,0,0,frq,amp,0,0,0,0,0,0,arpmode,0,0);
+    else if (os==3) setOSC(&osc3,1,wav,1,0,0,frq,amp,0,0,0,0,0,0,arpmode,0,0);
+}
+
+void Sound::playTone(uint8_t os, uint16_t frq, uint8_t volume, uint32_t duration)
+{
+    if (os==1) setOSC(&osc1,1,WSQUARE,frq,volume,duration);
+    else if (os==2) setOSC(&osc2,1,WTRI,frq,volume,duration);
+    else if (os==3) setOSC(&osc3,1,WTRI,frq,volume,duration);
+}
+
+uint8_t Sound::ampIsOn()
+{
+    #ifdef POK_SIM
+    return core.ampIsOn();
+    #else
+    #if POK_ENABLE_SOUND > 0
+    return Pokitto::ampIsOn();
+    #endif
+    #endif // POK_SIM
+    return 0;
+}
+
+void Sound::ampEnable(uint8_t v) {
+    #ifdef POK_SIM
+    core.ampEnable(v);
+    #else
+    #if POK_ENABLE_SOUND > 0
+    Pokitto::ampEnable(v);
+    #endif
+    #endif // POK_SIM
+
+}
+
+int Sound::playMusicStream(char* filename)
+{
+    return playMusicStream(filename,FILE_MODE_READONLY | FILE_MODE_BINARY);
+}
+
+int Sound::playMusicStream()
+{
+    #if POK_STREAMING_MUSIC >0
+    if (currentPtr) {
+            pokPlayStream();
+            return 1;
+    } else return 0; //no stream
+    #endif // POK_STREAMING_MUSIC
+}
+
+void Sound::pauseMusicStream() {
+    #if POK_ENABLE_SOUND > 0
+    pokPauseStream();
+    #endif
+}
+
+int Sound::playMusicStream(char* filename, uint8_t options)
+{
+    #if POK_STREAMING_MUSIC
+        uint8_t result;
+        result = pokInitSD();
+        if (!isThisFileOpen(filename)) {
+            fileClose(); // close any open files
+            result = fileOpen(filename,FILE_MODE_READONLY | FILE_MODE_BINARY);
+        }
+
+        if (result) {
+                currentPtr = 0; // mark that no stream is available
+                return 0; // opening music file failed
+        }
+
+        fileReadBytes(&buffers[0][0],BUFFER_SIZE);
+        fileReadBytes(&buffers[1][0],BUFFER_SIZE);
+        fileReadBytes(&buffers[2][0],BUFFER_SIZE);
+        fileReadBytes(&buffers[3][0],BUFFER_SIZE);
+        currentBuffer = 0;
+        currentPtr = buffers[currentBuffer];
+        endPtr = currentPtr + BUFFER_SIZE;
+
+        //streaming = STR_PLAYING|options;
+
+        if (!options) pokPlayStream(); // activate stream
+    #endif //POK_STREAMING_MUSIC
+    return 1; // opening music file succeeded
+}
+
+uint32_t Sound::getMusicStreamElapsedSec() {
+    return streamcounter/POK_AUD_FREQ;
+}
+
+uint32_t Sound::getMusicStreamElapsedMilliSec() {
+    return streamcounter/(POK_AUD_FREQ/1000);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/POKITTO_CORE/PokittoSound.h	Mon May 21 18:03:14 2018 +0000
@@ -0,0 +1,229 @@
+/**************************************************************************/
+/*!
+    @file     PokittoSound.h
+    @author   Jonne Valola
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2016, Jonne Valola
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+
+#ifndef POKITTOSOUND_H
+#define POKITTOSOUND_H
+
+#include <stdint.h>
+#include "Pokitto_settings.h"
+#include "GBcompatibility.h"
+#include "PokittoFakeavr.h"
+#include "PokittoGlobs.h"
+
+extern void pokPauseStream();
+extern void pokPlayStream();
+extern uint8_t pokStreamPaused();
+
+//volume levels
+#define GLOBVOL_SHIFT 5 //shift global volume to allow for finer increments
+
+
+#ifndef MAX_VOL_TEST
+    #define VOLUME_SPEAKER_MAX 255 //((8<<GLOBVOL_SHIFT)-1)
+    #define VOLUME_HEADPHONE_MAX 127
+    #define VOLUME_STARTUP VOLUME_HEADPHONE_MAX
+#else
+    #define VOLUME_SPEAKER_MAX 255
+    #define VOLUME_HEADPHONE_MAX VOLUME_SPEAKER_MAX
+    #define VOLUME_STARTUP VOLUME_SPEAKER_MAX
+#endif // MAXVOLTEST
+
+#ifdef POK_SIM
+#define VOLUME_STEP 1
+#else
+#define VOLUME_STEP 8
+#endif
+
+
+//commands
+#define CMD_VOLUME 0
+#define CMD_INSTRUMENT 1
+#define CMD_SLIDE 2
+#define CMD_ARPEGGIO 3
+#define CMD_TREMOLO 4
+
+#define STR_PLAYING 0x1
+#define STR_VISUALIZER  0x2
+#define STR_LOOP    0x4
+#define STR_PAUSED  0x8
+
+namespace Pokitto {
+
+/** Sound class.
+ *  The Sound class is an API-compatible version of the Gamebuino Sound API by Aurelien Rodot.
+ *  Because it is a class consisting of only static members, there is only 1 instance running,
+ *  no matter how many Sound classes are declared (see example below). This means sound can
+ *  be used through a simple Sound class object or as a member of the Core class.
+ *
+ */
+
+/** discrete_vol* are needed for more accurate volume control levels on hardware **/
+extern uint8_t discrete_vol;
+extern const uint8_t discrete_vol_levels[];
+extern const uint8_t discrete_vol_hw_levels[];
+extern const uint8_t discrete_vol_multipliers[];
+
+extern void audio_IRQ();  // audio interrupt
+
+class Sound {
+private:
+    static uint16_t volumeMax;
+
+public:
+	static void begin();
+
+	// Headphonemode
+	static uint8_t headPhoneLevel; // a workaround to disappearing sound at low volume
+	static void setMaxVol(int16_t);
+    static uint16_t getMaxVol();
+    static void volumeUp();
+    static void volumeDown();
+
+	// Original functions
+	static void updateStream();
+    static void playTone(uint8_t os, int frq, uint8_t amp, uint8_t wav,uint8_t arpmode);
+    static void playTone(uint8_t os, uint16_t freq, uint8_t volume, uint32_t duration);
+    static uint8_t ampIsOn();
+    static void ampEnable(uint8_t);
+    static int playMusicStream(char* filename, uint8_t options);
+    static int playMusicStream(char* filename);
+    static int playMusicStream();
+    static void pauseMusicStream();
+    static uint32_t getMusicStreamElapsedSec();
+    static uint32_t getMusicStreamElapsedMilliSec();
+
+	// GB compatibility functions
+	static void playTrack(const uint16_t* track, uint8_t channel);
+	static void updateTrack(uint8_t channel);
+	static void updateTrack();
+	static void stopTrack(uint8_t channel);
+	static void stopTrack();
+	static void changePatternSet(const uint16_t* const* patterns, uint8_t channel);
+	static bool trackIsPlaying[NUM_CHANNELS];
+
+	static void playPattern(const uint16_t* pattern, uint8_t channel);
+	static void changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel);
+	static void updatePattern(uint8_t i);
+	static void updatePattern();
+	static void setPatternLooping(bool loop, uint8_t channel);
+	static void stopPattern(uint8_t channel);
+	static void stopPattern();
+	static bool patternIsPlaying[NUM_CHANNELS];
+
+	static void command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t i);
+	static void playNote(uint8_t pitch, uint8_t duration, uint8_t channel);
+	static void updateNote();
+	static void updateNote(uint8_t i);
+	static void stopNote(uint8_t channel);
+	static void stopNote();
+
+	static uint8_t outputPitch[NUM_CHANNELS];
+	static int8_t outputVolume[NUM_CHANNELS];
+
+	static void setMasterVolume(uint8_t);
+	static uint8_t GetMasterVolume();
+	static void setVolume(int16_t volume);
+	static uint16_t getVolume();
+	static void setVolume(int8_t volume, uint8_t channel);
+	static uint8_t getVolume(uint8_t channel);
+
+	static void playOK();
+	static void playCancel();
+	static void playTick();
+
+	static uint8_t prescaler;
+
+	static void setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod);
+
+	static void generateOutput(); //!\\ DO NOT USE
+	static uint16_t globalVolume;
+
+
+#if (NUM_CHANNELS > 0)
+	//tracks data
+	static uint16_t *trackData[NUM_CHANNELS];
+	static uint8_t trackCursor[NUM_CHANNELS];
+	static uint16_t **patternSet[NUM_CHANNELS];
+	static int8_t patternPitch[NUM_CHANNELS];
+
+	// pattern data
+	static uint16_t *patternData[NUM_CHANNELS];
+	static uint16_t **instrumentSet[NUM_CHANNELS];
+	static bool patternLooping[NUM_CHANNELS];
+	static uint16_t patternCursor[NUM_CHANNELS];
+
+	// note data
+	static uint8_t notePitch[NUM_CHANNELS];
+	static uint8_t noteDuration[NUM_CHANNELS];
+	static int8_t noteVolume[NUM_CHANNELS];
+	static bool notePlaying[NUM_CHANNELS];
+
+	// commands data
+	static int8_t commandsCounter[NUM_CHANNELS];
+	static int8_t volumeSlideStepDuration[NUM_CHANNELS];
+	static int8_t volumeSlideStepSize[NUM_CHANNELS];
+	static uint8_t arpeggioStepDuration[NUM_CHANNELS];
+	static int8_t arpeggioStepSize[NUM_CHANNELS];
+	static uint8_t tremoloStepDuration[NUM_CHANNELS];
+	static int8_t tremoloStepSize[NUM_CHANNELS];
+
+
+	// instrument data
+	static uint16_t *instrumentData[NUM_CHANNELS];
+	static uint8_t instrumentLength[NUM_CHANNELS]; //number of steps in the instrument
+	static uint8_t instrumentLooping[NUM_CHANNELS]; //how many steps to loop on when the last step of the instrument is reached
+	static uint16_t instrumentCursor[NUM_CHANNELS]; //which step is being played
+	static uint8_t instrumentNextChange[NUM_CHANNELS]; //how many frames before the next step
+
+	//current step data
+	static int8_t stepVolume[NUM_CHANNELS];
+	static uint8_t stepPitch[NUM_CHANNELS];
+
+	static uint8_t chanVolumes[NUM_CHANNELS];
+#endif
+	static void updateOutput();
+};
+
+}
+
+#endif // POKITTOSOUND_H
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/POKITTO_HW/HWLCD.cpp	Mon May 21 18:03:14 2018 +0000
@@ -0,0 +1,1921 @@
+/**************************************************************************/
+/*!
+    @file     HWLCD.cpp
+    @author   Jonne Valola
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2016, Jonne Valola
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+#include "HWLCD.h" //HWLCD.h" #include "HWLCD.h"
+#include "Pokitto_settings.h"
+
+#ifndef DISABLEAVRMIN
+#define max(a,b) ((a)>(b)?(a):(b))
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif // DISABLEAVRMIN
+
+#ifdef DISABLEAVRMIN
+#include <algorithm>
+using std::min;
+using std::max;
+#endif // DISABLEAVRMIN
+
+#define AB_JUMP 1024 // jump one 1-bit Arduboy screen forward to get next color bit
+#define GB_JUMP 504 // jump one 1-bit Gamebuino screen forward to get next color bit
+
+using namespace Pokitto;
+
+uint16_t prevdata=0; // if data does not change, do not adjust LCD bus lines
+
+#if POK_BOARDREV == 2
+    pwmout_t backlightpwm;
+#endif
+
+
+/**************************************************************************/
+/*!
+    @brief  set up the 16-bit bus
+*/
+/**************************************************************************/
+
+static inline void setup_data_16(uint16_t data)
+{
+    //uint32_t p2=0;
+
+    //if (data != prevdata) {
+    //
+    //prevdata=data;
+
+    /** D0...D16 = P2_3 ... P2_18 **/
+    //p2 = data << 3;
+
+    //__disable_irq();    // Disable Interrupts
+    SET_MASK_P2;
+    LPC_GPIO_PORT->MPIN[2] = (data<<3); // write bits to port
+    CLR_MASK_P2;
+    //__enable_irq();     // Enable Interrupts
+    //}
+}
+
+
+/**************************************************************************/
+/*!
+    @brief  Write a command to the lcd, 16-bit bus
+*/
+/**************************************************************************/
+inline void write_command_16(uint16_t data)
+{
+   CLR_CS; // select lcd
+   CLR_CD; // clear CD = command
+   SET_RD; // RD high, do not read
+   setup_data_16(data); // function that inputs the data into the relevant bus lines
+   CLR_WR_SLOW;  // WR low
+   SET_WR;  // WR low, then high = write strobe
+   SET_CS; // de-select lcd
+}
+
+/**************************************************************************/
+/*!
+    @brief  Write data to the lcd, 16-bit bus
+*/
+/**************************************************************************/
+inline void write_data_16(uint16_t data)
+{
+   CLR_CS;
+   SET_CD;
+   SET_RD;
+   setup_data_16(data);
+   CLR_WR;
+   SET_WR;
+   SET_CS;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Pump data to the lcd, 16-bit bus, public function
+*/
+/**************************************************************************/
+void Pokitto::pumpDRAMdata(uint16_t* data,uint16_t counter)
+{
+   while (counter--) {
+   CLR_CS;
+   SET_CD;
+   SET_RD;
+   setup_data_16(*data++);
+   CLR_WR;
+   SET_WR;
+   SET_CS;
+   }
+}
+
+
+/**************************************************************************/
+/*!
+    @brief  Point to a (x,y) location in the LCD DRAM
+*/
+/**************************************************************************/
+static inline void setDRAMptr(uint8_t xptr, uint8_t yoffset)
+{
+    write_command(0x20);  // Vertical DRAM Address
+    write_data(yoffset);
+    write_command(0x21);  // Horizontal DRAM Address
+    write_data(xptr);  //
+    write_command(0x22); // write data to DRAM
+    CLR_CS_SET_CD_RD_WR;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Point to a (x,y) location in the LCD DRAM, public function
+*/
+/**************************************************************************/
+void Pokitto::setDRAMpoint(uint8_t xptr, uint8_t yoffset)
+{
+    write_command(0x20);  // Vertical DRAM Address
+    write_data(yoffset);
+    write_command(0x21);  // Horizontal DRAM Address
+    write_data(xptr);  //
+    write_command(0x22); // write data to DRAM
+    CLR_CS_SET_CD_RD_WR;
+}
+
+void Pokitto::initBacklight() {
+    #if POK_BOARDREV == 2
+    pwmout_init(&backlightpwm,POK_BACKLIGHT_PIN);
+    pwmout_period_us(&backlightpwm,5);
+    pwmout_write(&backlightpwm,POK_BACKLIGHT_INITIALVALUE);
+    #endif
+}
+
+void Pokitto::setBacklight(float value) {
+    if (value>0.999f) value = 0.999f;
+    pwmout_write(&backlightpwm,value);
+}
+
+void Pokitto::lcdInit() {
+   initBacklight();
+
+   SET_RESET;
+   wait_ms(10);
+   CLR_RESET;
+   wait_ms(10);
+   SET_RESET;
+   wait_ms(10);
+  //************* Start Initial Sequence **********//
+    write_command(0x01); // driver output control, this also affects direction
+    write_data(0x11C); // originally: 0x11C 100011100 SS,NL4,NL3,NL2
+                        // NL4...0 is the number of scan lines to drive the screen !!!
+                        // so 11100 is 1c = 220 lines, correct
+                        // test 1: 0x1C 11100 SS=0,NL4,NL3,NL2 -> no effect
+                        // test 2: 0x31C 1100011100 GS=1,SS=1,NL4,NL3,NL2 -> no effect
+                        // test 3: 0x51C 10100011100 SM=1,GS=0,SS=1,NL4,NL3,NL2 -> no effect
+                        // test 4: 0x71C SM=1,GS=1,SS=1,NL4,NL3,NL2
+                        // test 5: 0x
+                        // seems to have no effect... is this perhaps only for RGB mode ?
+
+    write_command(0x02); // LCD driving control
+    write_data(0x0100); // INV = 1
+
+    write_command(0x03); // Entry mode... lets try if this affects the direction
+    write_data(0x1030); // originally 0x1030 1000000110000 BGR,ID1,ID0
+                        // test 1: 0x1038 1000000111000 BGR,ID1,ID0,AM=1 ->drawing DRAM horizontally
+                        // test 4: am=1, id0=0, id1=0, 1000000001000,0x1008 -> same as above, but flipped on long
+                        // test 2: am=0, id0=0, 1000000100000, 0x1020 -> flipped on long axis
+                        // test 3: am=0, id1=0, 1000000010000, 0x1010 -> picture flowed over back to screen
+
+
+    write_command(0x08); // Display control 2
+    write_data(0x0808); // 100000001000 FP2,BP2
+
+    write_command(0x0C); // RGB display interface
+    write_data(0x0000); // all off
+
+    write_command(0x0F); // Frame marker position
+    write_data(0x0001); // OSC_EN
+
+    write_command(0x20);  // Horizontal DRAM Address
+    write_data(0x0000);  // 0
+
+    write_command(0x21);  // Vertical DRAM Address
+    write_data(0x0000); // 0
+
+ //*************Power On sequence ****************//
+    write_command(0x10);
+    write_data(0x0000);
+
+    write_command(0x11);
+    write_data(0x1000);
+    wait_ms(10);
+//------------------------ Set GRAM area --------------------------------//
+    write_command(0x30); // Gate scan position
+    write_data(0x0000); // if GS=0, 00h=G1, else 00h=G220
+
+    write_command(0x31); // Vertical scroll control
+    write_data(0x00DB); // scroll start line 11011011 = 219
+
+    write_command(0x32); // Vertical scroll control
+    write_data(0x0000); // scroll end line 0
+
+    write_command(0x33); // Vertical scroll control
+    write_data(0x0000); // 0=vertical scroll disabled
+
+    write_command(0x34); // Partial screen driving control
+    write_data(0x00DB); // db = full screen (end)
+
+    write_command(0x35); // partial screen
+    write_data(0x0000); // 0 = start
+
+    write_command(0x36); // Horizontal and vertical RAM position
+    write_data(0x00AF); //end address 175
+
+    write_command(0x37);
+    write_data(0x0000); // start address 0
+
+    write_command(0x38);
+    write_data(0x00DB); //end address 219
+
+    write_command(0x39); // start address 0
+    write_data(0x0000);
+    wait_ms(10);
+    write_command(0xff); // start gamma register control
+    write_data(0x0003);
+
+// ----------- Adjust the Gamma  Curve ----------//
+    write_command(0x50);
+    write_data(0x0203);
+
+    write_command(0x051);
+    write_data(0x0A09);
+
+    write_command(0x52);
+    write_data(0x0005);
+
+    write_command(0x53);
+    write_data(0x1021);
+
+    write_command(0x54);
+    write_data(0x0602);
+
+    write_command(0x55);
+    write_data(0x0003);
+
+    write_command(0x56);
+    write_data(0x0703);
+
+    write_command(0x57);
+    write_data(0x0507);
+
+    write_command(0x58);
+    write_data(0x1021);
+
+    write_command(0x59);
+    write_data(0x0703);
+
+    write_command(0xB0);
+    write_data(0x2501);
+
+    write_command(0xFF);
+    write_data(0x0000);
+
+    write_command(0x07);
+    write_data(0x1017);
+    wait_ms(200);
+    write_command(0x22);
+
+    lcdClear();
+}
+
+void Pokitto::lcdSleep(void){
+   write_command(0xFF);
+   write_data(0x0000);
+
+   write_command(0x07);
+   write_data(0x0000);
+   wait_ms(50);
+   write_command(0x10);// Enter Standby mode
+   write_data(0x0003);
+   wait_ms(200);
+
+}
+
+void Pokitto::lcdWakeUp (void){
+
+   wait_ms(200);
+   write_command(0xFF);
+   write_data(0x0000);
+
+   write_command(0x10);// Exit Sleep/ Standby mode
+   write_data(0x0000);
+   wait_ms(50);
+   write_command(0x07);
+   write_data(0x0117);
+   wait_ms(200);
+  }
+
+void Pokitto::lcdFillSurface(uint16_t c) {
+    uint32_t i;
+    write_command(0x20);  // Horizontal DRAM Address
+    write_data(0x0000);  // 0
+    write_command(0x21);  // Vertical DRAM Address
+    write_data(0);
+    write_command(0x22); // write data to DRAM
+    setup_data_16(c);
+    CLR_CS_SET_CD_RD_WR;
+    for(i=0;i<220*176;i++)
+    {
+    CLR_WR;
+    SET_WR;
+    }
+}
+
+void Pokitto::lcdClear() {
+    uint32_t i;
+    write_command(0x20);  // Horizontal DRAM Address
+    write_data(0x0000);  // 0
+    write_command(0x21);  // Vertical DRAM Address
+    write_data(0);
+    write_command(0x22); // write data to DRAM
+    setup_data_16(0x0000);
+    CLR_CS_SET_CD_RD_WR;
+    for(i=0;i<220*176;i++)
+    {
+        CLR_WR;
+        SET_WR;
+    }
+}
+
+void Pokitto::lcdPixel(int16_t x, int16_t y, uint16_t color) {
+    if ((x < 0) || (x >= POK_LCD_W) || (y < 0) || (y >= POK_LCD_H))
+	return;
+	write_command(0x20);  // Horizontal DRAM Address
+    write_data(y);  // 0
+    write_command(0x21);  // Vertical DRAM Address
+    write_data(x);
+    write_command(0x22); // write data to DRAM
+    CLR_CS_SET_CD_RD_WR;
+    setup_data_16(color);
+    CLR_WR;SET_WR;
+}
+
+void Pokitto::setWindow(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
+	write_command(0x37); write_data(x1);
+	write_command(0x36); write_data(x2);
+	write_command(0x39); write_data(y1);
+	write_command(0x38); write_data(y2);
+	write_command(0x20); write_data(x1);
+	write_command(0x21); write_data(y1);
+}
+
+void Pokitto::lcdTile(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t* gfx){
+	int width=x1-x0;
+	int height=y1-y0;
+	if (x0 > POK_LCD_W) return;
+	if (y0 > POK_LCD_H) return;
+	if (x0 < 0) x0=0;
+	if (y0 < 0) y0=0;
+
+	setWindow(y0, x0, y1-1, x1-1);
+    write_command(0x22);
+
+    for (int x=0; x<=width*height-1;x++) {
+        write_data(gfx[x]);
+    }
+	setWindow(0, 0, 175, 219);
+}
+
+
+void Pokitto::lcdRectangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) {
+	int16_t temp;
+	if (x0>x1) {temp=x0;x0=x1;x1=temp;}
+	if (y0>y1) {temp=y0;y0=y1;y1=temp;}
+	if (x0 > POK_LCD_W) return;
+	if (y0 > POK_LCD_H) return;
+	if (x1 > POK_LCD_W) x1=POK_LCD_W;
+	if (y1 > POK_LCD_H) y1=POK_LCD_H;
+	if (x0 < 0) x0=0;
+	if (y0 < 0) y0=0;
+
+	int16_t x,y;
+	for (x=x0; x<=x1;x++) {
+		write_command(0x20);  // Horizontal DRAM Address (=y on pokitto screen)
+		write_data(y0);
+		write_command(0x21);  // Vertical DRAM Address (=x on pokitto screen)
+		write_data(x);
+		write_command(0x22); // write data to DRAM
+
+		CLR_CS_SET_CD_RD_WR; // go to vram write mode
+
+
+		for (y=y0; y<y1;y++) {
+				setup_data_16(color); // setup the data (flat color = no change between pixels)
+				CLR_WR;SET_WR; //CLR_WR;SET_WR;//toggle writeline, pokitto screen writes a column up to down
+		}
+	}
+}
+
+/***
+ * Update the screen buffer of 220x176 pixels, 4 colors to LCD.
+ *
+ * The update rect is used for drawing only part of the screen buffer to LCD. Because of speed optimizations, the
+ * x, y, and width of the update rect must be dividable by 4 pixels, and the height must be dividable by 8 pixels.
+ * Note: The update rect is currently used for 220x176, 4 colors, screen mode only.
+ * @param scrbuf The screen buffer.
+ * @param updRectX The update rect.
+ * @param updRectY The update rect.
+ * @param updRectW The update rect.
+ * @param updRectH The update rect.
+ * @param paletteptr The screen palette.
+*/
+void Pokitto::lcdRefreshMode1(uint8_t * scrbuf, uint8_t updRectX, uint8_t updRectY, uint8_t updRectW, uint8_t updRectH, uint16_t* paletteptr) {
+
+    uint16_t x,y,xptr;
+    uint16_t scanline[4][176]; // read 4 half-nibbles = 4 pixels at a time
+    uint8_t *d, yoffset=0;
+
+    // If not the full screen is updated, check the validity of the update rect.
+    if ( updRectX != 0 || updRectY != 0 ||updRectW != LCDWIDTH ||updRectH != LCDHEIGHT ) {
+        uint8_t org_screenx = updRectX;
+        updRectX &= 0xfc; // Make the value dividable by 4.
+        updRectW += org_screenx - updRectX;
+        updRectW = (updRectW + 3) & 0xfc; // Make the value dividable by 4, round up.
+
+        uint8_t org_screeny = updRectY;
+        updRectY &= 0xfc; // Make the value dividable by 4.
+        updRectH += org_screeny - updRectY;
+        updRectH = (updRectH + 7) & 0xf8; // Make the value dividable by 8 (because of loop unroll optimization), round up.
+    }
+
+
+    #ifdef PROJ_SHOW_FPS_COUNTER
+    xptr = 8;
+    setDRAMptr(8, 0);
+    #else
+    xptr = 0;
+    setDRAMptr(0, 0);
+    #endif
+
+    for (x=updRectX; x<updRectX+updRectW; x+=4) {
+        d = scrbuf+(x>>2);// point to beginning of line in data
+
+        /** find colours in one scanline **/
+        uint8_t s=0;
+        d += (updRectY * 220/4);
+        for (y=updRectY; y<updRectY+updRectH; y++) {
+            uint8_t tdata = *d;
+            uint8_t t4 = tdata & 0x03; tdata >>= 2;// lowest half-nibble
+            uint8_t t3 = tdata & 0x03; tdata >>= 2;// second lowest half-nibble
+            uint8_t t2 = tdata & 0x03; tdata >>= 2;// second highest half-nibble
+            uint8_t t = tdata & 0x03;// highest half-nibble
+
+            /** put nibble values in the scanlines **/
+            scanline[0][y] = paletteptr[t];
+            scanline[1][y] = paletteptr[t2];
+            scanline[2][y] = paletteptr[t3];
+            scanline[3][y] = paletteptr[t4];
+
+            d += 220/4; // jump to read byte directly below in screenbuffer
+        }
+
+        #ifdef PROJ_SHOW_FPS_COUNTER
+        if (x>=8 ) {
+        #else
+        {
+
+        #endif
+
+            // Draw 8 vertical pixels at a time for performance reasons
+            setDRAMptr(x, updRectY);
+            for (uint8_t s=updRectY; s<updRectY+updRectH;) {
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+            }
+            setDRAMptr(x+1, updRectY);
+            for (uint8_t s=updRectY; s<updRectY+updRectH;) {
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+            }
+            setDRAMptr(x+2, updRectY);
+            for (uint8_t s=updRectY; s<updRectY+updRectH;) {
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+            }
+            setDRAMptr(x+3, updRectY);
+            for (uint8_t s=updRectY; s<updRectY+updRectH;) {
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+            }
+        }
+    }
+
+    #ifdef POK_SIM
+    simulator.refreshDisplay();
+    #endif
+}
+
+// Copy sprite pixels to the scanline
+#define SPRITE_2BPP_INNER_LOOP(n)\
+\
+    /* If the sprite is enabled and contained in this vertical scanline, copy 4 pixels. */\
+    if (sprScanlineAddr[(n)] &&\
+        y >= sprites[(n)].y && y < sprites[(n)].y + sprites[(n)].h ) {\
+\
+        int16_t sprx = sprites[(n)].x;\
+        uint16_t s_data16b = 0;  /* sprite data, 2 bytes */\
+\
+        /* Get pixel block, 4 or 8 pixels horizontally. Use the predefined bitshift mode. */\
+        /* Note:it is cheapest to compare to 0 first. */\
+        if (sprScanlineBitshiftMode[(n)] == BITSHIFT_MODE_MIDDLE_BYTE) {\
+            s_data16b = *(sprScanlineAddr[(n)]);\
+            uint16_t leftByte = *(sprScanlineAddr[(n)]-1);\
+            s_data16b = (leftByte << 8) | s_data16b;\
+        }\
+        else if (sprScanlineBitshiftMode[(n)] == BITSHIFT_MODE_FIRST_BYTE) {\
+            s_data16b = *(sprScanlineAddr[(n)]);\
+        }\
+        else { /* BITSHIFT_MODE_LAST_BYTE */\
+            uint16_t leftByte = *(sprScanlineAddr[(n)]-1);\
+            s_data16b = (leftByte << 8) | s_data16b;\
+        }\
+\
+        /* Shift sprite pixels according to sprite x. After shifting we have only 4 pixels. */\
+        uint8_t shiftRight = (sprx&0x3) << 1;\
+        s_data16b = (s_data16b >> shiftRight);\
+\
+        /* Get individual pixels */\
+        uint8_t s_t4 = s_data16b & 0x03; s_data16b >>= 2; /* lowest half-nibble */\
+        uint8_t s_t3 = s_data16b & 0x03; s_data16b >>= 2; /* second lowest half-nibble */\
+        uint8_t s_t2 = s_data16b & 0x03; s_data16b >>= 2; /* second highest half-nibble */\
+        uint8_t s_t1 = s_data16b & 0x03;                  /* highest half-nibble */\
+\
+        /* Store pixels as 16-bit colors from the palette */\
+        if (s_t4 != transparentColor) p4 = sprites[(n)].palette[s_t4];\
+        if (s_t3 != transparentColor) p3 = sprites[(n)].palette[s_t3];\
+        if (s_t2 != transparentColor) p2 = sprites[(n)].palette[s_t2];\
+        if (s_t1 != transparentColor) p = sprites[(n)].palette[s_t1];\
+\
+        /* Advance scanline address */\
+        sprScanlineAddr[(n)] += (sprites[(n)].w >> 2);\
+    }
+
+// Loop unrolling macros
+#define UNROLLED_LOOP_1() SPRITE_2BPP_INNER_LOOP(0)
+#define UNROLLED_LOOP_2() UNROLLED_LOOP_1() SPRITE_2BPP_INNER_LOOP(1)
+#define UNROLLED_LOOP_3() UNROLLED_LOOP_2() SPRITE_2BPP_INNER_LOOP(2)
+#define UNROLLED_LOOP_4() UNROLLED_LOOP_3() SPRITE_2BPP_INNER_LOOP(3)
+#define UNROLLED_LOOP_5() UNROLLED_LOOP_4() SPRITE_2BPP_INNER_LOOP(4)
+#define UNROLLED_LOOP_6() UNROLLED_LOOP_5() SPRITE_2BPP_INNER_LOOP(5)
+#define UNROLLED_LOOP_7() UNROLLED_LOOP_6() SPRITE_2BPP_INNER_LOOP(6)
+#define UNROLLED_LOOP_8() UNROLLED_LOOP_7() SPRITE_2BPP_INNER_LOOP(7)
+#define UNROLLED_LOOP_9() UNROLLED_LOOP_8() SPRITE_2BPP_INNER_LOOP(8)
+#define UNROLLED_LOOP_10() UNROLLED_LOOP_9() SPRITE_2BPP_INNER_LOOP(9)
+#define UNROLLED_LOOP_11() UNROLLED_LOOP_10() SPRITE_2BPP_INNER_LOOP(10)
+#define UNROLLED_LOOP_12() UNROLLED_LOOP_11() SPRITE_2BPP_INNER_LOOP(11)
+#define UNROLLED_LOOP_13() UNROLLED_LOOP_12() SPRITE_2BPP_INNER_LOOP(12)
+#define UNROLLED_LOOP_14() UNROLLED_LOOP_13() SPRITE_2BPP_INNER_LOOP(13)
+#define UNROLLED_LOOP_15() UNROLLED_LOOP_14() SPRITE_2BPP_INNER_LOOP(14)
+#define UNROLLED_LOOP_16() UNROLLED_LOOP_15() SPRITE_2BPP_INNER_LOOP(15)
+#define UNROLLED_LOOP_N_(n) UNROLLED_LOOP_##n()
+#define UNROLLED_LOOP_N(n) UNROLLED_LOOP_N_(n)
+
+/***
+ * Update the screen buffer of 220x176 pixels, 4 colors and free size 4 color sprites to LCD.
+ *
+ * The update rect is used for drawing only part of the screen buffer to LCD. Because of speed optimizations, the
+ * x, y, and width of the update rect must be dividable by 4 pixels, and the height must be dividable by 8 pixels.
+ * Note: The update rect is currently used for 220x176, 4 colors, screen mode only.
+ * If drawSpritesOnly=true, only sprites are fully updated to LCD. However, the dirty rect of the screen buffer is
+ * drawn behind the sprite current and previous location.
+ * Note: Sprite is enabled if sprite.bitmapData is not NULL. Also all enabled sprites must be at the beginning of
+ * the sprites array. No gaps are allowed in the array.
+ * @param scrbuf The screen buffer.
+ * @param updRectX The update rect.
+ * @param updRectY The update rect.
+ * @param updRectW The update rect.
+ * @param updRectH The update rect.
+ * @param paletteptr The screen palette.
+ * @param sprites The sprite array.
+ * @param drawSpritesOnly True, if only sprites are drawn. False, if both sprites and the screen buffer are drawn.
+*/
+void Pokitto::lcdRefreshMode1Spr(
+    uint8_t * scrbuf, uint8_t updRectX, uint8_t updRectY, uint8_t updRectW, uint8_t updRectH, uint16_t* paletteptr,
+    SpriteInfo* sprites, bool drawSpritesOnly) {
+
+    // In direct mode draw only sprites and their dirty rects. Return now if there are no sprites
+    if (drawSpritesOnly && (sprites == NULL || sprites[0].bitmapData == NULL))
+        return;
+
+    uint16_t x,y;
+    uint16_t scanline[4][176]; // read 4 half-nibbles (= 4 pixels) at a time
+    const uint8_t transparentColor = 0;  // fixed palette index 0 for transparency
+
+    // If not the full screen is updated, check the validity of the update rect.
+    if ( updRectX != 0 || updRectY != 0 ||updRectW != LCDWIDTH ||updRectH != LCDHEIGHT ) {
+        uint8_t org_screenx = updRectX;
+        updRectX &= 0xfc; // Make the value dividable by 4.
+        updRectW += org_screenx - updRectX;
+        updRectW = (updRectW + 3) & 0xfc; // Make the value dividable by 4, round up.
+
+        uint8_t org_screeny = updRectY;
+        updRectY &= 0xfc; // Make the value dividable by 4.
+        updRectH += org_screeny - updRectY;
+        updRectH = (updRectH + 7) & 0xf8; // Make the value dividable by 8 (because of loop unroll optimization), round up.
+    }
+
+    // Calculate the current  amount of sprites
+    // Note: Sprites must be taken into use from index 0 upwards, because the first sprite with bitmapData==NULL is considered as the last sprite
+    uint8_t spriteCount = 0;
+    if (sprites != NULL)
+        for (;sprites[spriteCount].bitmapData != NULL && spriteCount < SPRITE_COUNT; spriteCount++);
+
+    // If drawing the screen buffer, set the start pos to LCD commands only here.
+    #ifdef PROJ_SHOW_FPS_COUNTER
+    if (!drawSpritesOnly) setDRAMptr(8, 0);
+    #else
+    if (!drawSpritesOnly) setDRAMptr(0, 0);
+    #endif
+
+    //*** GO THROUGH EACH VERTICAL GROUP OF 4 SCANLINES.***
+
+    for (x=0; x<LCDWIDTH; x+=4) {
+
+        uint8_t *screenBufScanlineAddr = scrbuf + (x>>2);// point to beginning of line in data
+
+        /*Prepare scanline start address for sprites that are visible in this vertical scanline. Sprite width cannot exceed the screen width*/
+        uint8_t *sprScanlineAddr[SPRITE_COUNT];  // Sprite start address for the scanline
+        uint8_t sprScanlineBitshiftMode[SPRITE_COUNT];  // Sprite bitshift mode for the scanline
+        const uint8_t BITSHIFT_MODE_MIDDLE_BYTE = 0;
+        const uint8_t BITSHIFT_MODE_FIRST_BYTE = 1;
+        const uint8_t BITSHIFT_MODE_LAST_BYTE = 2;
+        uint8_t scanlineMinY = 255; // Init to uninitialized value. Do not draw by default.
+        uint8_t scanlineMaxY = 0; // Init to uninitialized value. Do not draw by default.
+
+        //*** CALCULATE DIRTY RECTS AND RESOLVE WHICH SPRITES BELONG TO THIS SCANLINE GROUP ***
+
+        if (sprites != NULL) {
+
+            // Check all the sprites for this scanline. That is used for handling the given update rect
+            // Note that the last round is when (sprindex == spriteCount). That is used to add the screen buffer
+            // update rect to the dirty rect.
+            for (int sprindex = 0; sprindex <= spriteCount; sprindex++) {
+
+                int16_t sprx, spry, sprOldX, sprOldY;
+                uint8_t sprw, sprh;
+                bool isCurrentSpriteOutOfScreen = false;
+                bool isOldSpriteOutOfScreen = false;
+
+                if (sprindex < spriteCount) {
+
+                    sprx = sprites[sprindex].x;
+                    spry = sprites[sprindex].y;
+                    sprw = sprites[sprindex].w;
+                    sprh = sprites[sprindex].h;
+                    sprOldX = sprites[sprindex].oldx;
+                    sprOldY = sprites[sprindex].oldy;
+               }
+
+                // Handle the screen buffer update rect after all sprites
+                else if(!drawSpritesOnly){
+
+                    sprx = updRectX;
+                    spry = updRectY;
+                    sprw = updRectW;
+                    sprh = updRectH;
+                    sprOldX = updRectX;
+                    sprOldY = updRectY;
+                    isCurrentSpriteOutOfScreen = false;
+                    isOldSpriteOutOfScreen = false;
+                }
+
+                // Check for out-of-screen
+                if (sprx >= LCDWIDTH || spry >= LCDHEIGHT)
+                    isCurrentSpriteOutOfScreen = true;
+                if (sprOldX >= LCDWIDTH || sprOldY >= LCDHEIGHT)
+                    isOldSpriteOutOfScreen = true;
+
+                // Skip if current and old sprites are out-of-screen
+                if (isCurrentSpriteOutOfScreen && isOldSpriteOutOfScreen)
+                    continue;
+
+                // Detect the dirty rect x-span by combining the previous and current sprite position.
+                int16_t sprDirtyXMin = min(sprx, sprOldX);
+                int16_t sprDirtyXMax = max(sprx, sprOldX);
+                if (isCurrentSpriteOutOfScreen)
+                    sprDirtyXMax = sprOldX;
+                if (isOldSpriteOutOfScreen)
+                    sprDirtyXMax = sprx;
+
+                // Is current x inside the sprite combined dirty rect ?
+                int16_t sprDirtyXMaxEnd = sprDirtyXMax + sprw - 1 + 4; // Add 4 pixels to dirty rect width (needed?)
+                if (sprDirtyXMin <= x+3 && x <= sprDirtyXMaxEnd) {
+
+                    // *** COMBINE DIRTY RECTS FOR THIS SCANLINE GROUP ***
+
+                    // Dirty rect
+                    int sprDirtyYMin = std::min(spry, sprOldY);
+                    sprDirtyYMin = std::max((int)sprDirtyYMin, 0);
+                    int sprDirtyYMax = std::max(spry, sprOldY);
+                    if (isCurrentSpriteOutOfScreen)
+                        sprDirtyYMax = sprOldY;
+                    if (isOldSpriteOutOfScreen)
+                        sprDirtyYMax = spry;
+                    int sprDirtyYMaxEnd = sprDirtyYMax + sprh - 1;
+                    sprDirtyYMaxEnd = std::min(sprDirtyYMaxEnd, LCDHEIGHT - 1);  // Should use LCDHEIGHT instead of screenH? Same with other screen* ?
+
+                    // Get the scanline min and max y values for drawing
+                    if (sprDirtyYMin < scanlineMinY)
+                        scanlineMinY = sprDirtyYMin;
+                    if (sprDirtyYMaxEnd > scanlineMaxY)
+                        scanlineMaxY = sprDirtyYMaxEnd;
+
+                   // *** PREPARE SPRITE FOR DRAWING ***
+
+                   // Check if the sprite should be active for this vertical scanline group.
+                    if (sprindex < spriteCount &&  // not for update rect
+                        !isCurrentSpriteOutOfScreen && //out-of-screen
+                        sprx <= x+3 && x < sprx + sprw) { // note: cover group of 4 pixels of the scanline (x+3)
+
+                        // Find the byte number in the sprite data
+                        int16_t byteNum = ((x+3) - sprx)>>2;
+
+                        // Get the start addres of the spite data in this scanline.
+                        sprScanlineAddr[sprindex] = const_cast<uint8_t*>(sprites[sprindex].bitmapData + byteNum);
+
+                        // If the sprite goes over the top, it must be clipped from the top.
+                        if(spry < 0)
+                            sprScanlineAddr[sprindex] += (-spry) * (sprw >> 2);
+
+                        // Select the bitshift mode for the blit algorithm
+                        if (byteNum == 0)
+                            sprScanlineBitshiftMode[sprindex] = BITSHIFT_MODE_FIRST_BYTE;
+                        else if (byteNum >= (sprw >> 2))
+                            sprScanlineBitshiftMode[sprindex] = BITSHIFT_MODE_LAST_BYTE;
+                        else
+                            sprScanlineBitshiftMode[sprindex] = BITSHIFT_MODE_MIDDLE_BYTE;
+                    }
+                    else
+                        sprScanlineAddr[sprindex] = NULL;  // Deactive sprite for this scanline
+                }
+                else
+                    sprScanlineAddr[sprindex] = NULL;  // Deactive sprite for this scanline
+            }
+        }
+
+        // *** ADJUST THE SCANLINE GROUP HEIGHT ***
+
+        // The height must dividable by 8. That is needed because later we copy 8 pixels at a time to the LCD.
+        if (scanlineMaxY - scanlineMinY + 1 > 0) {
+            uint8_t scanlineH = scanlineMaxY - scanlineMinY + 1;
+            uint8_t addW = 8 - (scanlineH & 0x7);
+
+            // if height is not dividable by 8, make it be.
+            if (addW != 0) {
+                if (scanlineMinY > addW )
+                    scanlineMinY -= addW;
+                else if( scanlineMaxY + addW < updRectY+updRectH)
+                    scanlineMaxY += addW;
+                else {
+                    // Draw full height scanline
+                    scanlineMinY = updRectY;
+                    scanlineMaxY = updRectY+updRectH-1;
+                }
+            }
+        }
+
+        // *** COMBINE THE SCANLINE GROUP OF THE SCREEN BUFFER AND ALL SPRITES ***
+
+        // Find colours in this group of 4 scanlines
+        screenBufScanlineAddr += (scanlineMinY * 220/4);
+        for (y=scanlineMinY; y<=scanlineMaxY; y++)
+        {
+            // get the screen buffer data first
+            uint8_t tdata = *screenBufScanlineAddr;
+            uint8_t t4 = tdata & 0x03; tdata >>= 2;// lowest half-nibble
+            uint8_t t3 = tdata & 0x03; tdata >>= 2;// second lowest half-nibble
+            uint8_t t2 = tdata & 0x03; tdata >>= 2;// second highest half-nibble
+            uint8_t t = tdata & 0x03;// highest half-nibble
+
+            // Convert to 16-bit colors in palette
+            uint16_t p = paletteptr[t];
+            uint16_t p2 = paletteptr[t2];
+            uint16_t p3 = paletteptr[t3];
+            uint16_t p4 = paletteptr[t4];
+
+            #if 0
+            // Dirty rect visual test
+            p = COLOR_BLUE >> (Core::frameCount % 5);
+            p2 = COLOR_BLUE >> (Core::frameCount % 5);
+            p3 = COLOR_BLUE >> (Core::frameCount % 5);
+            p4 = COLOR_BLUE >> (Core::frameCount % 5);
+            #endif
+
+            // Add active sprite pixels
+            if (sprites != NULL) {
+
+                // Use loop unrolling for speed optimization
+                UNROLLED_LOOP_N(SPRITE_COUNT)
+            }
+
+            // put the result nibble values in the scanline
+            scanline[0][y] = p;
+            scanline[1][y] = p2;
+            scanline[2][y] = p3;
+            scanline[3][y] = p4;
+
+            screenBufScanlineAddr += 220>>2; // jump to read byte directly below in screenbuffer
+        }
+
+        // *** DRAW THE SCANLINE GROUP TO LCD
+
+#ifdef PROJ_SHOW_FPS_COUNTER
+        if (x>=8 && scanlineMaxY - scanlineMinY +1 > 0) {
+#else
+        if (scanlineMaxY - scanlineMinY +1 > 0) {
+#endif
+            // Draw 8 vertical pixels at a time for performance reasons
+
+            setDRAMptr(x, scanlineMinY);
+            for (uint8_t s=scanlineMinY;s<=scanlineMaxY;) {
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+            }
+
+            setDRAMptr(x+1, scanlineMinY);
+            for (uint8_t s=scanlineMinY;s<=scanlineMaxY;) {
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+            }
+
+            setDRAMptr(x+2, scanlineMinY);
+            for (uint8_t s=scanlineMinY;s<=scanlineMaxY;) {
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+            }
+
+            setDRAMptr(x+3, scanlineMinY);
+            for (uint8_t s=scanlineMinY;s<=scanlineMaxY;) {
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+                setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+            }
+        }
+    }
+
+    // Update old x and y for the sprites
+    if (sprites != NULL) {
+        for (int sprindex = 0; sprindex < spriteCount; sprindex++) {
+            sprites[sprindex].oldx = sprites[sprindex].x;
+            sprites[sprindex].oldy = sprites[sprindex].y;
+        }
+    }
+
+    #ifdef POK_SIM
+    simulator.refreshDisplay();
+    #endif
+}
+
+void Pokitto::lcdRefreshMode2(uint8_t * scrbuf, uint16_t* paletteptr) {
+uint16_t x,y;
+uint16_t scanline[2][88]; // read two nibbles = pixels at a time
+uint8_t *d;
+
+write_command(0x20);  // Horizontal DRAM Address
+write_data(0);  // 0
+write_command(0x21);  // Vertical DRAM Address
+write_data(0);
+write_command(0x22); // write data to DRAM
+CLR_CS_SET_CD_RD_WR;
+
+for(x=0;x<110;x+=2)
+  {
+    d = scrbuf+(x>>1);// point to beginning of line in data
+    /** find colours in one scanline **/
+    uint8_t s=0;
+    for(y=0;y<88;y++)
+    {
+    uint8_t t = *d >> 4; // higher nibble
+    uint8_t t2 = *d & 0xF; // lower nibble
+    /** higher nibble = left pixel in pixel pair **/
+    scanline[0][s] = paletteptr[t];
+    scanline[1][s++] = paletteptr[t2];
+    /** testing only **/
+    //scanline[0][s] = 0xFFFF*(s&1);
+    //scanline[1][s] = 0xFFFF*(!(s&1));
+    //s++;
+    /** until here **/
+    d+=110/2; // jump to read byte directly below in screenbuffer
+    }
+    s=0;
+    /** draw scanlines **/
+    /** leftmost scanline twice**/
+
+    #ifdef PROJ_SHOW_FPS_COUNTER
+    if (x<4) continue;
+    setDRAMptr(x<<1, 0);
+    #endif
+
+    for (s=0;s<88;) {
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+    }
+
+    for (s=0;s<88;) {
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+    }
+    /** rightmost scanline twice**/
+    //setDRAMptr(xptr++,yoffset);
+    for (s=0;s<88;) {
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+    }
+
+    for (s=0;s<88;) {
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+    }
+  }
+}
+
+void Pokitto::lcdRefreshMode3(uint8_t * scrbuf, uint16_t* paletteptr) {
+uint16_t x,y;
+uint16_t scanline[2][176]; // read two nibbles = pixels at a time
+uint8_t *d;
+
+write_command(0x20);  // Horizontal DRAM Address
+write_data(0);  // 0
+write_command(0x21);  // Vertical DRAM Address
+write_data(0);
+write_command(0x22); // write data to DRAM
+CLR_CS_SET_CD_RD_WR;
+
+for(x=0;x<220;x+=2)
+  {
+    d = scrbuf+(x>>1);// point to beginning of line in data
+    /** find colours in one scanline **/
+    uint8_t s=0;
+    for(y=0;y<176;y++)
+    {
+    uint8_t t = *d >> 4; // higher nibble
+    uint8_t t2 = *d & 0xF; // lower nibble
+    /** higher nibble = left pixel in pixel pair **/
+    scanline[0][s] = paletteptr[t];
+    scanline[1][s++] = paletteptr[t2];
+    /** testing only **/
+    //scanline[0][s] = 0xFFFF*(s&1);
+    //scanline[1][s] = 0xFFFF*(!(s&1));
+    //s++;
+    /** until here **/
+    d+=220/2; // jump to read byte directly below in screenbuffer
+    }
+    s=0;
+    /** draw scanlines **/
+    /** leftmost scanline**/
+
+    #ifdef PROJ_SHOW_FPS_COUNTER
+    if (x<8) continue;
+    setDRAMptr(x, 0);
+    #endif
+
+    for (s=0;s<176;) {
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+    }
+
+    /** rightmost scanline**/
+    //setDRAMptr(xptr++,yoffset);
+    for (s=0;s<176;) {
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+    }
+  }
+}
+
+void Pokitto::lcdRefreshGB(uint8_t * scrbuf, uint16_t* paletteptr) {
+uint16_t x,y;
+uint16_t scanline[48];
+uint8_t * d;
+
+#if POK_STRETCH
+//uint16_t xptr = 8;
+#else
+//xptr = 26;
+#endif
+
+write_command(0x20);  // Horizontal DRAM Address
+write_data(0);  // 0
+write_command(0x21);  // Vertical DRAM Address
+write_data(0);
+write_command(0x22); // write data to DRAM
+CLR_CS_SET_CD_RD_WR;
+
+/** draw border **/
+    for (int s=0;s<5*176;) {
+            setup_data_16(COLOR_BLACK);CLR_WR;SET_WR;s++;
+    }
+
+for(x=0;x<84;x++)
+  {
+
+        d = scrbuf + x;// point to beginning of line in data
+
+        /** find colours in one scanline **/
+        uint8_t s=0;
+        for(y=0;y<6;y++)
+            {
+            uint8_t t = *d;
+            #if POK_COLORDEPTH > 1
+            uint8_t t2 = *(d+504);
+            #endif
+            #if POK_COLORDEPTH > 2
+            uint8_t t3 = *(d+504+504);
+            #endif
+            #if POK_COLORDEPTH > 3
+            uint8_t t4 = *(d+504+504+504);
+            #endif
+            uint8_t paletteindex = 0;
+
+            /** bit 1 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x1);
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x1)) | ((t2 & 0x01)<<1);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = (t & 0x1) | ((t2 & 0x1)<<1) | ((t3 & 0x1)<<2);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = (t & 0x1) | ((t2 & 0x1)<<1) | ((t3 & 0x1)<<2) | ((t4 & 0x1)<<3);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 2 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x2)>>1;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x2)>>1) | ((t2 & 0x02));
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x2)>>1) | ((t2 & 0x2)) | ((t3 & 0x2)<<1);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x2)>>1) | ((t2 & 0x2)) | ((t3 & 0x2)<<1) | ((t4 & 0x2)<<2);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 3 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x4)>>2;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 4)>>2) | ((t2 & 0x04)>>1);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x4)>>2) | ((t2 & 0x4)>>1) | (t3 & 0x4);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x4)>>2) | ((t2 & 0x4)>>1) | (t3 & 0x4) | ((t4 & 0x4)<<1);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 4 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x8)>>3;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x8)>>3) | ((t2 & 0x08)>>2);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x8)>>3) | ((t2 & 0x8)>>2) | ((t3 & 0x8)>>1);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x8)>>3) | ((t2 & 0x8)>>2) | ((t3 & 0x8)>>1) | (t4 & 0x8);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 5 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x10)>>4;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x10)>>4) | ((t2 & 0x10)>>3);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x10)>>4) | ((t2 & 0x10)>>3) | ((t3 & 0x10)>>2);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x10)>>4) | ((t2 & 0x10)>>3) | ((t3 & 0x10)>>2) | ((t4 & 0x10)>>1);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 6 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x20)>>5;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x20)>>5) | ((t2 & 0x20)>>4);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x20)>>5) | ((t2 & 0x20)>>4) | ((t3 & 0x20)>>3);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x20)>>5) | ((t2 & 0x20)>>4) | ((t3 & 0x20)>>3) | ((t4 & 0x20)>>2);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 7 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x40)>>6;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x40)>>6) | ((t2 & 0x40)>>5);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x40)>>6) | ((t2 & 0x40)>>5) | ((t3 & 0x40)>>4) ;
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x40)>>6) | ((t2 & 0x40)>>5) | ((t3 & 0x40)>>4) | ((t4 & 0x40)>>3);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 8 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x80)>>7;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x80)>>7) | ((t2 & 0x80)>>6);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x80)>>7) | ((t2 & 0x80)>>6) | ((t3 & 0x80)>>5);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x80)>>7) | ((t2 & 0x80)>>6) | ((t3 & 0x80)>>5) | ((t4 & 0x80)>>4);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            d+=84; // jump to byte directly below
+            }
+
+
+        /*write_command(0x20);  // Horizontal DRAM Address
+        write_data(0x10);  // 0
+        write_command(0x21);  // Vertical DRAM Address
+        write_data(xptr++);
+        write_command(0x22); // write data to DRAM
+        CLR_CS_SET_CD_RD_WR;*/
+        /** draw border **/
+        setup_data_16(COLOR_BLACK);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;        CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+
+        s=0;
+
+        /** draw scanlines **/
+        for (s=0;s<48;) {
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+        }
+        /** draw border **/
+        setup_data_16(COLOR_BLACK);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;        CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+
+
+        /*write_command(0x20);  // Horizontal DRAM Address
+        write_data(0x10);  // 0
+        write_command(0x21);  // Vertical DRAM Address
+        write_data(xptr++);
+        write_command(0x22); // write data to DRAM
+        CLR_CS_SET_CD_RD_WR;*/
+        /** draw border **/
+        setup_data_16(COLOR_BLACK);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;        CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+
+        for (s=0;s<48;) {
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+        }
+
+        /** draw border **/
+        setup_data_16(COLOR_BLACK);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;        CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+
+
+        #if POK_STRETCH
+        //if (x>16 && x<68)
+        if (x&2)// && x&2)
+        {
+            /*write_command(0x20);  // Horizontal DRAM Address
+            write_data(0x10);  // 0
+            write_command(0x21);  // Vertical DRAM Address
+            write_data(xptr++);
+            write_command(0x22); // write data to DRAM
+            CLR_CS_SET_CD_RD_WR;*/
+            /** draw border **/
+        setup_data_16(COLOR_BLACK);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;        CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+
+
+            for (s=0;s<48;) {
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+            }
+
+            /** draw border **/
+        setup_data_16(COLOR_BLACK);CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;        CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;CLR_WR;SET_WR;
+
+        }
+        #endif
+    }
+    /** draw border **/
+    for (int s=0;s<5*176;) {
+            setup_data_16(COLOR_BLACK);CLR_WR;SET_WR;s++;
+    }
+}
+
+
+void Pokitto::lcdRefreshAB(uint8_t * scrbuf, uint16_t* paletteptr) {
+uint16_t x,y;
+uint16_t scanline[64];
+uint8_t *d;
+//lcdClear();
+#if POK_STRETCH
+uint16_t xptr = 14;
+uint8_t yoffset = 24;
+#else
+uint16_t xptr = 0;
+uint8_t yoffset = 0;
+#endif
+
+for(x=0;x<128;x++)
+  {
+    write_command(0x20);  // Horizontal DRAM Address
+    write_data(yoffset);  // 0
+    write_command(0x21);  // Vertical DRAM Address
+    write_data(xptr++);
+    write_command(0x22); // write data to DRAM
+    CLR_CS_SET_CD_RD_WR;
+    //setDRAMptr(xptr++,yoffset);
+
+        d = scrbuf + x;// point to beginning of line in data
+
+        /** find colours in one scanline **/
+        uint8_t s=0;
+        for(y=0;y<8;y++)
+            {
+            uint8_t t = *d;
+            #if POK_COLORDEPTH > 1
+            uint8_t t2 = *(d+AB_JUMP);
+            #endif // POK_COLORDEPTH
+            #if POK_COLORDEPTH > 2
+            uint8_t t3 = *(d+AB_JUMP+AB_JUMP);
+            #endif // POK_COLORDEPTH
+            #if POK_COLORDEPTH > 3
+            uint8_t t4 = *(d+AB_JUMP+AB_JUMP+AB_JUMP);
+            #endif // POK_COLORDEPTH
+            uint8_t paletteindex = 0;
+
+            /** bit 1 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x1);
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x1)) | ((t2 & 0x01)<<1);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = (t & 0x1) | ((t2 & 0x1)<<1) | ((t3 & 0x1)<<2);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = (t & 0x1) | ((t2 & 0x1)<<1) | ((t3 & 0x1)<<2) | ((t4 & 0x1)<<3);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 2 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x2)>>1;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x2)>>1) | ((t2 & 0x02));
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x2)>>1) | ((t2 & 0x2)) | ((t3 & 0x2)<<1);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x2)>>1) | ((t2 & 0x2)) | ((t3 & 0x2)<<1) | ((t4 & 0x2)<<2);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 3 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x4)>>2;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 4)>>2) | ((t2 & 0x04)>>1);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x4)>>2) | ((t2 & 0x4)>>1) | (t3 & 0x4);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x4)>>2) | ((t2 & 0x4)>>1) | (t3 & 0x4) | ((t4 & 0x4)<<1);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 4 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x8)>>3;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x8)>>3) | ((t2 & 0x08)>>2);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x8)>>3) | ((t2 & 0x8)>>2) | ((t3 & 0x8)>>1);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x8)>>3) | ((t2 & 0x8)>>2) | ((t3 & 0x8)>>1) | (t4 & 0x8);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 5 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x10)>>4;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x10)>>4) | ((t2 & 0x10)>>3);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x10)>>4) | ((t2 & 0x10)>>3) | ((t3 & 0x10)>>2);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x10)>>4) | ((t2 & 0x10)>>3) | ((t3 & 0x10)>>2) | ((t4 & 0x10)>>1);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 6 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x20)>>5;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x20)>>5) | ((t2 & 0x20)>>4);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x20)>>5) | ((t2 & 0x20)>>4) | ((t3 & 0x20)>>3);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x20)>>5) | ((t2 & 0x20)>>4) | ((t3 & 0x20)>>3) | ((t4 & 0x20)>>2);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 7 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x40)>>6;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x40)>>6) | ((t2 & 0x40)>>5);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x40)>>6) | ((t2 & 0x40)>>5) | ((t3 & 0x40)>>4) ;
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x40)>>6) | ((t2 & 0x40)>>5) | ((t3 & 0x40)>>4) | ((t4 & 0x40)>>3);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            /** bit 8 **/
+            #if POK_COLORDEPTH == 1
+            paletteindex = (t & 0x80)>>7;
+            #elif POK_COLORDEPTH == 2
+            paletteindex = ((t & 0x80)>>7) | ((t2 & 0x80)>>6);
+            #elif POK_COLORDEPTH == 3
+            paletteindex = ((t & 0x80)>>7) | ((t2 & 0x80)>>6) | ((t3 & 0x80)>>5);
+            #elif POK_COLORDEPTH == 4
+            paletteindex = ((t & 0x80)>>7) | ((t2 & 0x80)>>6) | ((t3 & 0x80)>>5) | ((t4 & 0x80)>>4);
+            #endif
+            scanline[s++] = paletteptr[paletteindex];
+
+            d+=128; // jump to byte directly below
+            }
+
+        s=0;
+
+        /** draw scanlines **/
+        for (s=0;s<64;) {
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        }
+
+        #if POK_STRETCH
+        if (x&1) {
+        write_command(0x20);  // Horizontal DRAM Address
+        write_data(yoffset);  // 0
+        write_command(0x21);  // Vertical DRAM Address
+        write_data(xptr++);
+        write_command(0x22); // write data to DRAM
+        CLR_CS_SET_CD_RD_WR;
+        //setDRAMptr(xptr++,yoffset);
+
+        for (s=0;s<64;) {
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        }
+        }
+        #endif
+    }
+}
+
+void Pokitto::lcdRefreshModeGBC(uint8_t * scrbuf, uint16_t* paletteptr) {
+uint16_t x,y,xptr;
+uint16_t scanline[4][144]; // read 4 half-nibbles = 4 pixels at a time
+uint8_t *d, yoffset=0;
+
+xptr = 0;
+setDRAMptr(xptr,yoffset);
+
+
+for(x=0;x<160;x+=4)
+  {
+    d = scrbuf+(x>>2);// point to beginning of line in data
+    /** find colours in one scanline **/
+    uint8_t s=0;
+    for(y=0;y<144;y++)
+    {
+    uint8_t tdata = *d;
+    uint8_t t4 = tdata & 0x03; tdata >>= 2;// lowest half-nibble
+    uint8_t t3 = tdata & 0x03; tdata >>= 2;// second lowest half-nibble
+    uint8_t t2 = tdata & 0x03; tdata >>= 2;// second highest half-nibble
+    uint8_t t = tdata & 0x03;// highest half-nibble
+
+    /** put nibble values in the scanlines **/
+
+    scanline[0][s] = paletteptr[t];
+    scanline[1][s] = paletteptr[t2];
+    scanline[2][s] = paletteptr[t3];
+    scanline[3][s++] = paletteptr[t4];
+
+     d+=160/4; // jump to read byte directly below in screenbuffer
+    }
+
+    s=0;
+    /** draw scanlines **/
+    for (s=0;s<144;) {
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+    }
+    setDRAMptr(++xptr,yoffset);
+    for (s=0;s<144;) {
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+    }
+    setDRAMptr(++xptr,yoffset);
+    for (s=0;s<144;) {
+        setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[2][s++]);CLR_WR;SET_WR;
+    }
+    setDRAMptr(++xptr,yoffset);
+    for (s=0;s<144;) {
+        setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[3][s++]);CLR_WR;SET_WR;
+    }
+    setDRAMptr(++xptr,yoffset);
+  }
+}
+
+
+void Pokitto::lcdRefreshT1(uint8_t* tilebuf, uint8_t* tilecolorbuf, uint8_t* tileset, uint16_t* paletteptr) {
+#ifdef POK_TILEMODE
+uint16_t x,y,data,xptr;
+uint16_t scanline[176];
+uint8_t yoffset=0, tilebyte, tileindex, tilex=0, tiley=0,xcount;
+
+
+if (!tileset) return;
+
+#if LCDWIDTH < POK_LCD_W
+xptr = (POK_LCD_W-LCDWIDTH)/2;
+#else
+xptr = 0;
+#endif
+#if LCDHEIGHT < POK_LCD_H
+yoffset = (POK_LCD_H-LCDHEIGHT)/2;
+#else
+yoffset = 0;
+#endif
+
+for(x=0, xcount=0 ;x<LCDWIDTH;x++,xcount++)  // loop through vertical columns
+  {
+    setDRAMptr(xptr++,yoffset); //point to VRAM
+
+        /** find colours in one scanline **/
+        uint8_t s=0, tiley=0;
+        //tileindex = tilebuf[tilex*POK_TILES_Y];
+        if (xcount==POK_TILE_W) {
+            tilex++;
+            xcount=0;
+        }
+
+        for(y=0;y<LCDHEIGHT;)
+        {
+            uint8_t tileval = tilebuf[tilex+tiley*POK_TILES_X]; //get tile number
+            uint16_t index = tileval*POK_TILE_W+xcount;
+            uint8_t tilebyte = tileset[index]; //get bitmap data
+            for (uint8_t ycount=0, bitcount=0; ycount<POK_TILE_H; ycount++, y++, bitcount++) {
+                if (bitcount==8) {
+                    bitcount=0;
+                    index += 176; //jump to byte below in the tileset bitmap
+                    tilebyte = tileset[index]; //get bitmap data
+                }
+                //tilebyte = tile[(tileindex>>4)+*POK_TILE_W]; //tilemaps are 16x16
+                //uint8_t paletteindex = ((tilebyte>>(bitcount&0x7)) & 0x1);
+                if (!tileval) scanline[s++] = COLOR_MAGENTA*((tilebyte>>bitcount)&0x1);//paletteptr[paletteindex];
+                else scanline[s++] = paletteptr[((tilebyte>>bitcount)&0x1)*tileval];//paletteptr[paletteindex];
+            }
+            tiley++; //move to next tile
+        }
+        s=0;
+
+        /** draw scanlines **/
+        for (s=0;s<LCDHEIGHT;) {
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+        }
+    }
+    #endif
+}
+
+
+void Pokitto::lcdRefreshMode13(uint8_t * scrbuf, uint16_t* paletteptr, uint8_t offset){
+uint16_t x,y;
+uint16_t scanline[2][110]; // read two nibbles = pixels at a time
+uint8_t *d;
+
+write_command(0x20); write_data(0);
+write_command(0x21); write_data(0);
+write_command(0x22);
+CLR_CS_SET_CD_RD_WR;
+
+for(x=0;x<110;x+=2)
+  {
+    d = scrbuf+x;// point to beginning of line in data
+    uint8_t s=0;
+    for(y=0;y<88;y++)
+    {
+        uint8_t t = *d;
+        uint8_t t1 = *(d+1);
+        scanline[0][s] = paletteptr[(t+offset)&255];
+        scanline[1][s++] = paletteptr[(t1+offset)&255];
+        d+=110; // jump to read byte directly below in screenbuffer
+    }
+    s=0;
+    for (s=0;s<88;) {
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+    }
+    for (s=0;s<88;) {
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+    }
+    for (s=0;s<88;) {
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+    }
+    for (s=0;s<88;) {
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;CLR_WR;SET_WR;
+    }
+  }
+
+}
+
+
+
+void Pokitto::lcdRefreshMode14(uint8_t * scrbuf, uint16_t* paletteptr) {
+uint16_t x,y,data,xptr;
+uint16_t scanline[176]; uint16_t* scptr;
+uint8_t *d;
+
+write_command(0x20); write_data(0);
+write_command(0x21); write_data(0);
+write_command(0x22);
+CLR_CS_SET_CD_RD_WR;
+
+for(x=0;x<220;x++)
+  {
+        d = scrbuf+x;
+        scptr = &scanline[0];
+
+        /** find colours in one scanline **/
+        /*for(y=0;y<22;y++)
+            {
+
+            uint16_t t = *d;
+            uint16_t t2 = *(d+POK_BITFRAME);
+            uint16_t t3 = *(d+POK_BITFRAME+POK_BITFRAME);
+
+            *scptr++ = (t & 0x1)*R_MASK | (t2 & 0x1)*G_MASK | (t3 & 0x1)*B_MASK; t >>= 1;t2 >>= 1;t3 >>= 1;
+            *scptr++ = (t & 0x1)*R_MASK | (t2 & 0x1)*G_MASK | (t3 & 0x1)*B_MASK; t >>= 1;t2 >>= 1;t3 >>= 1;
+            *scptr++ = (t & 0x1)*R_MASK | (t2 & 0x1)*G_MASK | (t3 & 0x1)*B_MASK; t >>= 1;t2 >>= 1;t3 >>= 1;
+            *scptr++ = (t & 0x1)*R_MASK | (t2 & 0x1)*G_MASK | (t3 & 0x1)*B_MASK; t >>= 1;t2 >>= 1;t3 >>= 1;
+
+            *scptr++ = (t & 0x1)*R_MASK | (t2 & 0x1)*G_MASK | (t3 & 0x1)*B_MASK; t >>= 1;t2 >>= 1;t3 >>= 1;
+            *scptr++ = (t & 0x1)*R_MASK | (t2 & 0x1)*G_MASK | (t3 & 0x1)*B_MASK; t >>= 1;t2 >>= 1;t3 >>= 1;
+            *scptr++ = (t & 0x1)*R_MASK | (t2 & 0x1)*G_MASK | (t3 & 0x1)*B_MASK; t >>= 1;t2 >>= 1;t3 >>= 1;
+            *scptr++ = (t & 0x1)*R_MASK | (t2 & 0x1)*G_MASK | (t3 & 0x1)*B_MASK; t >>= 1;t2 >>= 1;t3 >>= 1;
+
+
+            d+=220; // jump to word directly below
+            }
+        */
+        /** alternative way: go through one color at a time **/
+            scptr = &scanline[0]; // set to beginning of scanline
+            for(y=0;y<22;y++, d +=220)
+            {
+            uint16_t t = *d & 0xFF;
+
+            *scptr++ = R_MASK * (t&0x1); t >>= 1;
+            *scptr++ = R_MASK * (t&0x1); t >>= 1;
+            *scptr++ = R_MASK * (t&0x1); t >>= 1;
+            *scptr++ = R_MASK * (t&0x1); t >>= 1;
+            *scptr++ = R_MASK * (t&0x1); t >>= 1;
+            *scptr++ = R_MASK * (t&0x1); t >>= 1;
+            *scptr++ = R_MASK * (t&0x1); t >>= 1;
+            *scptr++ = R_MASK * (t&0x1);
+            }
+            scptr = &scanline[0]; // set to beginning of scanline
+            d = scrbuf+x+POK_BITFRAME;
+            for(y=0;y<22;y++, d +=220)
+            {
+            uint16_t t = *d & 0xFF;
+
+            *scptr++ |= G_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= G_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= G_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= G_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= G_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= G_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= G_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= G_MASK * (t&0x1);
+            }
+            scptr = &scanline[0]; // set to beginning of scanline
+            d = scrbuf+x+POK_BITFRAME*2;
+            for(y=0;y<22;y++, d +=220)
+            {
+            uint16_t t = *d & 0xFF;
+
+            *scptr++ |= B_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= B_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= B_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= B_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= B_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= B_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= B_MASK * (t&0x1); t >>= 1;
+            *scptr++ |= B_MASK * (t&0x1);
+            }
+
+
+        #ifdef PROJ_SHOW_FPS_COUNTER
+        if (x<8) continue;
+        setDRAMptr(x, 0);
+        #endif
+
+        /** draw scanlines **/
+        for (int s=0;s<176;) {
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+            setup_data_16(scanline[s++]);CLR_WR;SET_WR;
+
+        }
+
+    }
+}
+
+//#define ADEKTOSMODE15
+
+#ifdef ADEKTOSMODE15
+void Pokitto::lcdRefreshMode15(uint16_t* pal, uint8_t* scrbuf){
+    write_command(0x03); write_data(0x1038); //realy only need to call this once
+    write_command(0x20); write_data(0);
+    write_command(0x21); write_data(0);
+
+    write_command(0x22);
+
+    #ifdef PROJ_SHOW_FPS_COUNTER
+    for (int x=0,xt=0; x<0x4BA0;x++,xt++) {
+    if (xt==110) xt=0;
+    if (xt<8) {
+        write_data(0);
+        write_data(0);
+    } else {
+        write_data(pal[(((scrbuf[x]) & 0xf0) >> 4)]);
+        write_data(pal[( (scrbuf[x]) & 0x0f)]);
+    }
+
+    }
+    #else
+    for (int x=0; x<0x4BA0;x++) {
+        write_data(pal[(((scrbuf[x]) & 0xf0) >> 4)]);
+        write_data(pal[( (scrbuf[x]) & 0x0f)]);
+    }
+    #endif //PROJ_SHOW_FPS_COUNTER
+}
+
+#else
+
+void Pokitto::lcdRefreshMode15(uint16_t* paletteptr, uint8_t* scrbuf){
+uint16_t x,y,xptr;
+uint16_t scanline[2][176]; // read two nibbles = pixels at a time
+uint8_t *d, yoffset=0;
+
+xptr = 0;
+//setDRAMptr(xptr,yoffset);
+
+write_command(0x20); write_data(0);
+write_command(0x21); write_data(0);
+write_command(0x22);
+CLR_CS_SET_CD_RD_WR;
+
+for(x=0;x<220;x+=2)
+  {
+    d = scrbuf+(x>>1);// point to beginning of line in data
+    // find colours in one scanline
+    uint8_t s=0;
+    for(y=0;y<176;y++)
+    {
+    uint8_t t = *d >> 4; // higher nibble
+    uint8_t t2 = *d & 0xF; // lower nibble
+    // higher nibble = left pixel in pixel pair
+    scanline[0][s] = paletteptr[t];
+    scanline[1][s++] = paletteptr[t2];
+
+    d+=220/2; // jump to read byte directly below in screenbuffer
+    }
+    s=0;
+    // draw scanlines
+
+    #ifdef PROJ_SHOW_FPS_COUNTER
+    if (x<8) continue;
+    setDRAMptr(x, 0);
+    #endif
+
+    for (s=0;s<176;) {
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[0][s++]);CLR_WR;SET_WR;
+    }
+
+    for (s=0;s<176;) {
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+        setup_data_16(scanline[1][s++]);CLR_WR;SET_WR;
+    }
+  }
+}
+#endif //ADEKTOSMODE15
+
+void Pokitto::blitWord(uint16_t c) {
+    setup_data_16(c);CLR_WR;SET_WR;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/POKITTO_HW/HWLCD.h	Mon May 21 18:03:14 2018 +0000
@@ -0,0 +1,218 @@
+/**************************************************************************/
+/*!
+    @file     HWLCD.h
+    @author   Jonne Valola
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2016, Jonne Valola
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+#ifndef __HWLCD_H__
+#define __HWLCD_H__
+
+#include "mbed.h"
+#include "gpio_api.h"
+#include "pinmap.h"
+
+#define write_command write_command_16
+#define write_data write_data_16
+
+namespace Pokitto {
+
+struct SpriteInfo {
+    const uint8_t* bitmapData;
+    int16_t x;
+    int16_t y;
+    int16_t oldx;
+    int16_t oldy;
+    uint8_t w;
+    uint8_t h;
+    uint16_t palette[4];
+};
+
+extern void setDRAMpoint(uint8_t, uint8_t);
+extern void pumpDRAMdata(uint16_t*, uint16_t);
+extern void initBacklight();
+extern void setBacklight(float);
+extern void lcdFillSurface(uint16_t);
+extern void lcdPixel(int16_t x, int16_t y, uint16_t c);
+extern void setWindow(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
+extern void lcdTile(int16_t x0, int16_t y0, int16_t width, int16_t height, uint16_t* gfx);
+extern void lcdRectangle(int16_t x, int16_t y,int16_t x2, int16_t y2, uint16_t color);
+extern void lcdInit();
+extern void lcdSleep();
+extern void lcdWakeUp();
+extern void lcdRefresh(uint8_t *, uint16_t*);
+extern void lcdRefreshAB(uint8_t *, uint16_t*);
+extern void lcdRefreshMode14(uint8_t *, uint16_t*);
+extern void lcdRefreshGB(uint8_t *, uint16_t*);
+extern void lcdRefreshMode1(uint8_t* scrbuf, uint8_t updRectX, uint8_t updRectY, uint8_t updRectW, uint8_t updRectH, uint16_t* paletteptr);
+extern void lcdRefreshMode1Spr(uint8_t * scrbuf, uint8_t screenx, uint8_t screeny, uint8_t screenw, uint8_t screenh, uint16_t* paletteptr, Pokitto::SpriteInfo* sprites, bool drawSpritesOnly);
+extern void lcdRefreshMode2(uint8_t *, uint16_t*);
+extern void lcdRefreshMode3(uint8_t *, uint16_t*);
+extern void lcdRefreshModeGBC(uint8_t *, uint16_t*);
+extern void lcdRefreshMode13(uint8_t *, uint16_t*, uint8_t);
+ 
+extern void lcdRefreshMode15(uint16_t*, uint8_t*);
+ 
+
+/** Update LCD from 1-bit tile mode */
+extern void lcdRefreshT1(uint8_t*, uint8_t*, uint8_t*, uint16_t*);
+extern void lcdClear();
+extern void lcdFill(uint16_t);
+/** Blit one word of data*/
+extern void blitWord(uint16_t);
+
+/**************************************************************************/
+/**                          PINS AND PORTS                              **/
+/**************************************************************************/
+
+#if POK_BOARDREV == 1
+/** 2-layer board version 1.3 **/
+ #define LCD_CD_PORT           0
+ #define LCD_CD_PIN            2
+ #define LCD_WR_PORT           1
+ #define LCD_WR_PIN            23
+ #define LCD_RD_PORT           1
+ #define LCD_RD_PIN            24
+ #define LCD_RES_PORT          1
+ #define LCD_RES_PIN           28
+#else
+/** 4-layer board version 2.1 **/
+ #define LCD_CD_PORT           0
+ #define LCD_CD_PIN            2
+ #define LCD_WR_PORT           1
+ #define LCD_WR_PIN            12
+ #define LCD_RD_PORT           1
+ #define LCD_RD_PIN            24
+ #define LCD_RES_PORT          1
+ #define LCD_RES_PIN           0
+#endif
+
+/**************************************************************************/
+/**                          LCD CONTROL MACROS                          **/
+/**************************************************************************/
+
+#define CLR_RESET LPC_GPIO_PORT->CLR[LCD_RES_PORT] = 1 << LCD_RES_PIN; //RST = (0); // Clear pin
+#define SET_RESET LPC_GPIO_PORT->SET[LCD_RES_PORT] = 1 << LCD_RES_PIN; // RST = (1); // Set pin
+
+#define CLR_CD { LPC_GPIO_PORT->CLR[LCD_CD_PORT] = 1 << LCD_CD_PIN; } // RS = (0); // Clear pin
+#define SET_CD { LPC_GPIO_PORT->SET[LCD_CD_PORT] = 1 << LCD_CD_PIN; }// RS = (1); // Set pin
+
+#define CLR_WR { LPC_GPIO_PORT->CLR[LCD_WR_PORT] = 1 << LCD_WR_PIN;__asm("nop");}//__asm("nop");}//WR = (0); // Clear pin
+#define CLR_WR_SLOW { LPC_GPIO_PORT->CLR[LCD_WR_PORT] = 1 << LCD_WR_PIN;__asm("nop");__asm("nop");}//WR = (0); // Clear pin
+#define SET_WR LPC_GPIO_PORT->SET[LCD_WR_PORT] = 1 << LCD_WR_PIN; //WR = (1); // Set pin
+
+#define CLR_RD LPC_GPIO_PORT->CLR[LCD_RD_PORT] = 1 << LCD_RD_PIN; //RD = (0); // Clear pin
+#define SET_RD LPC_GPIO_PORT->SET[LCD_RD_PORT] = 1 << LCD_RD_PIN; //RD = (1); // Set pin
+
+#define SET_CS  //CS tied to ground
+#define CLR_CS
+
+#define CLR_CS_CD_SET_RD_WR {CLR_CD; SET_RD; SET_WR;}
+#define CLR_CS_SET_CD_RD_WR {SET_CD; SET_RD; SET_WR;}
+#define SET_CD_RD_WR {SET_CD; SET_RD; SET_WR;}
+#define SET_WR_CS SET_WR;
+
+#define SET_MASK_P2 LPC_GPIO_PORT->MASK[2] = ~(0x7FFF8); //mask P2_3 ...P2_18
+#define CLR_MASK_P2 LPC_GPIO_PORT->MASK[2] = 0; // all on
+
+
+
+/**************************************************************************/
+/**                          SETUP GPIO & DATA                           **/
+/**************************************************************************/
+
+static void setup_gpio()
+{
+    /** control lines **/
+    LPC_GPIO_PORT->DIR[LCD_CD_PORT] |= (1  << LCD_CD_PIN );
+    LPC_GPIO_PORT->DIR[LCD_WR_PORT] |= (1  << LCD_WR_PIN );
+    LPC_GPIO_PORT->DIR[LCD_RD_PORT] |= (1  << LCD_RD_PIN );
+    LPC_GPIO_PORT->DIR[LCD_RES_PORT] |= (1  << LCD_RES_PIN );
+    /** data lines **/
+    LPC_GPIO_PORT->DIR[2] |= (0xFFFF  << 3);  // P2_3...P2_18 as output
+
+    pin_mode(P2_3,PullNone); // turn off pull-up
+    pin_mode(P2_4,PullNone); // turn off pull-up
+    pin_mode(P2_5,PullNone); // turn off pull-up
+    pin_mode(P2_6,PullNone); // turn off pull-up
+
+    pin_mode(P2_7,PullNone); // turn off pull-up
+    pin_mode(P2_8,PullNone); // turn off pull-up
+    pin_mode(P2_9,PullNone); // turn off pull-up
+    pin_mode(P2_10,PullNone); // turn off pull-up
+
+    pin_mode(P2_11,PullNone); // turn off pull-up
+    pin_mode(P2_12,PullNone); // turn off pull-up
+    pin_mode(P2_13,PullNone); // turn off pull-up
+    pin_mode(P2_14,PullNone); // turn off pull-up
+
+    pin_mode(P2_15,PullNone); // turn off pull-up
+    pin_mode(P2_16,PullNone); // turn off pull-up
+    pin_mode(P2_17,PullNone); // turn off pull-up
+    pin_mode(P2_18,PullNone); // turn off pull-up
+}
+
+
+
+
+#define HI_BYTE(d) (LPC_GPIO->MPIN[1]= (d<<13)) //((d>>8)<<21))
+#define LO_BYTE(d) (LPC_GPIO->MPIN[1]= (d<<21)) //because of mask makes no difference
+
+// Macros to set data bus direction to input/output
+#define LCD_GPIO2DATA_SETINPUT  GPIO_GPIO2DIR &= ~LCD_DATA_MASK
+#define LCD_GPIO2DATA_SETOUTPUT GPIO_GPIO2DIR |= LCD_DATA_MASK
+
+
+// Basic Color definitions
+#define	COLOR_BLACK                         (uint16_t)(0x0000)
+#define	COLOR_BLUE                          (uint16_t)(0x001F)
+#define	COLOR_RED                           (uint16_t)(0xF800)
+#define	COLOR_GREEN                         (uint16_t)(0x07E0)
+#define COLOR_CYAN                          (uint16_t)(0x07FF)
+#define COLOR_MAGENTA                       (uint16_t)(0xF81F)
+#define COLOR_YELLOW                        (uint16_t)(0xFFE0)
+#define COLOR_WHITE                         (uint16_t)(0xFFFF)
+
+// Grayscale Values
+#define COLOR_GRAY_15                       (uint16_t)(0x0861)    //  15  15  15
+#define COLOR_GRAY_30                       (uint16_t)(0x18E3)    //  30  30  30
+#define COLOR_GRAY_50                       (uint16_t)(0x3186)    //  50  50  50
+#define COLOR_GRAY_80                       (uint16_t)(0x528A)    //  80  80  80
+#define COLOR_GRAY_128                      (uint16_t)(0x8410)    // 128 128 128
+#define COLOR_GRAY_200                      (uint16_t)(0xCE59)    // 200 200 200
+#define COLOR_GRAY_225                      (uint16_t)(0xE71C)    // 225 225 225
+
+
+} // namespace pokitto
+#endif // __HWLCD_H_
+
+