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

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!
Revision:
23:f88837b8f914
Parent:
22:e826f80d8582
--- a/POKITTO_HW/HWLCD.cpp	Fri Dec 29 02:55:34 2017 +0000
+++ b/POKITTO_HW/HWLCD.cpp	Fri Dec 29 05:17:10 2017 +0000
@@ -37,6 +37,11 @@
 #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
+
 #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
 
@@ -327,6 +332,7 @@
     }
 }
 
+
 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);
@@ -449,7 +455,11 @@
 uint16_t scanline[4][176]; // read 4 half-nibbles = 4 pixels at a time
 uint8_t *d, yoffset=0;
 
+#ifdef PROJ_USE_FPS_COUNTER
+xptr = 8;
+#else
 xptr = 0;
+#endif
 setDRAMptr(xptr,yoffset);
 
 
@@ -474,51 +484,367 @@
 
     d+=220/4; // jump to read byte directly below in screenbuffer
     }
-    s=0;
-    /** draw scanlines **/
-    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;
-    }
-    for (s=0;s<176;) {
-        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;
-    }
-    for (s=0;s<176;) {
-        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 PROJ_USE_FPS_COUNTER
+    if (x>=8 ) {
+#else
+    {
+
+#endif
+
+        /** draw scanlines **/
+        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;
+        }
+        for (s=0;s<176;) {
+            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;
+        }
+        for (s=0;s<176;) {
+            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;
+        }
     }
   }
 }
 
+// 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 and sprites to LCD.
+
+If useDirectMode=true only sprites are updated TO LCD and the dirty rect is drawn behind the sprite current and previous
+location.
+If useDirectMode=false both the full screen buffer and sprites are updated to LCD.
+
+Limitations of sprites:
+- Sprite is enabled if sprites.bitmapData is not NULL
+- All enabled sprites must be at the beginning of the sprites array.
+*/
+void Pokitto::lcdRefreshMode1Spr(uint8_t * scrbuf, uint16_t* paletteptr, SpriteInfo* sprites, bool useDirectMode) {
+
+    // In direct mode, return now if there are no sprites
+    if (useDirectMode && (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
+
+    // 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_USE_FPS_COUNTER
+    if (!useDirectMode) setDRAMptr(8, 0);
+    #else
+    if (!useDirectMode) setDRAMptr(0, 0);
+    #endif
+
+    // TODO OPTIMIZE: if a sprite is totally out-of-screen, it could be marked here already. Even in this case we might
+    // need to clear the previous sprite location with screen buffer pixels.
+
+    // Go through each vertical group of 4 scanlines.
+    for (x=0; x<220; 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 = 0;  // Min y to draw for the scanline
+        uint8_t scanlineMaxY = 175;  // Max y to draw for the scanline
+
+        // If not drawing the screen buffer, reset min and max to uninitialized values
+        if (useDirectMode ) {
+            scanlineMinY = 255;
+            scanlineMaxY = 0;
+        }
+        if (sprites != NULL) {
+
+            // Check all the sprites for this scanline.
+            for (int sprindex = 0; sprindex < spriteCount; sprindex++) {
+
+                int16_t sprx = sprites[sprindex].x;
+                int16_t spry = sprites[sprindex].y;
+                uint8_t sprw = sprites[sprindex].w;
+                uint8_t sprh = sprites[sprindex].h;
+                int16_t sprOldX = sprites[sprindex].oldx;
+                int16_t sprOldY = sprites[sprindex].oldy;
+
+                // 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);
+
+                // 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) {
+
+                    // If not drawing the whole screen buffer, detect the dirty rect y-span by combining the old and
+                    // current sprite position, and combine all the sprites.
+                    if (useDirectMode) {
+
+                        // Dirty rect
+                        int16_t sprDirtyYMin = min(spry, sprOldY);
+                        sprDirtyYMin = max(sprDirtyYMin, 0);
+                        int16_t sprDirtyYMax = max(spry, sprOldY);
+                        int16_t sprDirtyYMaxEnd = sprDirtyYMax + sprh - 1;
+                        sprDirtyYMaxEnd = min(sprDirtyYMaxEnd, 175);
+
+                        // Get the scanline min and max y values for drawing
+                        if (sprDirtyYMin < scanlineMinY)
+                            scanlineMinY = sprDirtyYMin;
+                        if (sprDirtyYMaxEnd > scanlineMaxY)
+                            scanlineMaxY = sprDirtyYMaxEnd;
+                    }
+
+                    // Check if the sprite should be active for this vertical scanline group.
+                    if (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
+            }
+        }
+
+        // The height must dividable by 8. That is needed because later we copy 8 pixels at a time to the LCD.
+        if (useDirectMode && 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 < 176)
+                    scanlineMaxY += addW;
+                else {
+                    scanlineMinY = 0;
+                    scanlineMaxY = 175;
+                }
+            }
+        }
+
+        // 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];
+
+            // 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 scanline to LCD
+#ifdef PROJ_USE_FPS_COUNTER
+        if (x>=8 && scanlineMaxY - scanlineMinY +1 > 0) {
+#else
+        if (scanlineMaxY - scanlineMinY +1 > 0) {
+#endif
+            if (useDirectMode) 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;
+            }
+
+            if (useDirectMode) 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;
+            }
+
+            if (useDirectMode) 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;
+            }
+
+            if (useDirectMode) 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
@@ -1206,8 +1532,80 @@
     #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::blitWord(uint16_t c) {
     setup_data_16(c);CLR_WR;SET_WR;
 }
 
 
+