Library(Beta) for Gameduino 2

Dependencies:   arduino

Dependents:   aa_gd_jw

Revision:
0:9c211972beb2
diff -r 000000000000 -r 9c211972beb2 GD2.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GD2.cpp	Fri Apr 11 07:24:23 2014 +0000
@@ -0,0 +1,825 @@
+#include "stdarg.h"
+#include "GD2.h"
+#include "Utils.h"
+
+//AnalogIn accel[3] = {P0_23, P0_24, P0_25};
+byte touch_transform0[] = { 0xEA, 0x81, 0x00, 0x00, 0xAE, 0xFE, 0xFF, 0xFF, 0x49, 0xCC, 0xEC, 0xFF, 0x28, 0x00, 0x00, 0x00, 0x09, 0xB3, 0xFF, 0xFF, 0x42, 0x5D, 0x18, 0x01 };
+byte touch_transform1[] = { 0xAB, 0x7E, 0xFF, 0xFF, 0x0F, 0x01, 0x00, 0x00, 0x97, 0x22, 0xEE, 0x01, 0xA3, 0x00, 0x00, 0x00, 0xB3, 0x4B, 0x00, 0x00, 0xE9, 0xC9, 0xF6, 0xFF };
+
+GDClass::GDClass(PinName mosi, PinName miso, PinName sclk, PinName graphic_cs, PinName sdcard_cs)
+    : GDTR(mosi, miso, sclk, graphic_cs)
+{
+    _sdcard_cs = sdcard_cs;
+    
+    accel[0] = new AnalogIn(P0_23);
+    accel[1] = new AnalogIn(P0_24);
+    accel[2] = new AnalogIn(P0_25);
+}
+
+void GDClass::begin(uint8_t options)
+{
+    GDTR.begin();
+
+    // Generate a blank screen
+    cmd_dlstart();
+    Clear();
+    swap();
+    finish();
+
+    GDTR.wr(REG_PCLK_POL, 1);
+    
+    GDTR.wr(REG_PCLK, 5);
+#if PROTO == 1
+    GDTR.wr(REG_ROTATE, 0);
+    GDTR.wr(REG_SWIZZLE, 3);
+#endif
+    GDTR.wr(REG_GPIO_DIR, 0x83);
+    GDTR.wr(REG_GPIO, 0x80);
+    
+    for (int i = 0; i < sizeof(touch_transform0); i++)
+    {
+        if(GDTR.rd(REG_ROTATE) == 0)
+            GDTR.wr(REG_TOUCH_TRANSFORM_A + i, touch_transform0[i]);
+        else
+            GDTR.wr(REG_TOUCH_TRANSFORM_A + i, touch_transform1[i]);
+    }
+    
+    if (options & GD_CALIBRATE)
+    {
+#if CALIBRATION
+    
+    self_calibrate();
+    for (int i = 0; i < 24; i++) 
+        std::printf("0x%02X, ", GDTR.rd(REG_TOUCH_TRANSFORM_A + i));
+/*
+        if (EEPROM.read(0) != 0x7c) {
+            self_calibrate();
+             for (int i = 0; i < 24; i++) 
+                DEBUGOUT(GDTR.rd(REG_TOUCH_TRANSFORM_A + i), HEX);
+            for (int i = 0; i < 24; i++)
+                EEPROM.write(1 + i, GDTR.rd(REG_TOUCH_TRANSFORM_A + i));
+            EEPROM.write(0, 0x7c);  // is written!
+        } else {
+            for (int i = 0; i < 24; i++)
+                GDTR.wr(REG_TOUCH_TRANSFORM_A + i, EEPROM.read(1 + i));
+        }
+*/
+#endif
+    }
+
+    GDTR.wr16(REG_TOUCH_RZTHRESH, 1200);
+
+    lfsr = 0x5555;
+    lcg = 0;
+
+    SD = new sdcard(GDTR.SPI(), _sdcard_cs);
+    
+#if STORAGE
+    if (options & GD_STORAGE) 
+    {
+        storage();
+    }
+#endif
+
+    if (options & GD_TRIM) 
+    {
+        tune();
+        DEBUGOUT("tune()\r\n");
+    }
+}
+
+void GDClass::flush(void)
+{
+    GDTR.flush();
+}
+
+void GDClass::swap(void) {
+    Display();
+    cmd_swap();
+    cmd_loadidentity();
+    cmd_dlstart();
+    GDTR.flush();
+}
+
+uint32_t GDClass::measure_freq(void)
+{
+    unsigned long t0 = GDTR.rd32(REG_CLOCK);
+    delayMicroseconds(15625);
+    unsigned long t1 = GDTR.rd32(REG_CLOCK);
+    return (t1 - t0) << 6;
+}
+
+void GDClass::tune(void)
+{
+    uint32_t f;
+    for (byte i = 0; (i < 31) && ((f = measure_freq()) < LOW_FREQ_BOUND); i++)
+        GDTR.wr(REG_TRIM, i);
+    GDTR.wr32(REG_FREQUENCY, f);
+}
+
+
+void GDClass::storage(void) {
+    GDTR.__end();
+    SD->begin();
+    GDTR.resume();
+}
+
+void GDClass::self_calibrate(void) {
+    cmd_dlstart();
+    Clear();
+    cmd_text(240, 100, 30, OPT_CENTERX, "please tap on the dot");
+    cmd_calibrate();
+    finish();
+    cmd_loadidentity();
+    cmd_dlstart();
+    GDTR.flush();
+}
+
+void GDClass::seed(uint16_t n) {
+    lfsr = n | 1;
+    lcg = n ^ 0x7921;
+}
+uint16_t GDClass::random() {
+    lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xB400u);
+    lcg = (lcg * 47) + 60497;
+    return (lcg ^ lfsr);
+}
+uint16_t GDClass::random(uint16_t n) {
+    return GDClass::random() % n;
+}
+
+
+// >>> [int(65535*math.sin(math.pi * 2 * i / 1024)) for i in range(257)]
+static PROGMEM int sintab[257] = {
+    0, 402, 804, 1206, 1608, 2010, 2412, 2813, 3215, 3617, 4018, 4419, 4821, 5221, 5622, 6023, 6423, 6823, 7223, 7622, 8022, 8421, 8819, 9218, 9615, 10013, 10410, 10807, 11203, 11599, 11995, 12390, 12785, 13179, 13573, 13966, 14358, 14750, 15142, 15533, 15923, 16313, 16702, 17091, 17479, 17866, 18252, 18638, 19023, 19408, 19791, 20174, 20557, 20938, 21319, 21699, 22078, 22456, 22833, 23210, 23585, 23960, 24334, 24707, 25079, 25450, 25820, 26189, 26557, 26924, 27290, 27655, 28019, 28382, 28744, 29105, 29465, 29823, 30181, 30537, 30892, 31247, 31599, 31951, 32302, 32651, 32999, 33346, 33691, 34035, 34378, 34720, 35061, 35400, 35737, 36074, 36409, 36742, 37075, 37406, 37735, 38063, 38390, 38715, 39039, 39361, 39682, 40001, 40319, 40635, 40950, 41263, 41574, 41885, 42193, 42500, 42805, 43109, 43411, 43711, 44010, 44307, 44603, 44896, 45189, 45479, 45768, 46055, 46340, 46623, 46905, 47185, 47463, 47739, 48014, 48287, 48558, 48827, 49094, 49360, 49623, 49885, 50145, 50403, 50659, 50913, 51165, 51415, 51664, 51910, 52155, 52397, 52638, 52876, 53113, 53347, 53580, 53810, 54039, 54265, 54490, 54712, 54933, 55151, 55367, 55581, 55793, 56003, 56211, 56416, 56620, 56821, 57021, 57218, 57413, 57606, 57796, 57985, 58171, 58355, 58537, 58717, 58894, 59069, 59242, 59413, 59582, 59748, 59912, 60074, 60234, 60391, 60546, 60699, 60849, 60997, 61143, 61287, 61428, 61567, 61704, 61838, 61970, 62100, 62227, 62352, 62474, 62595, 62713, 62828, 62941, 63052, 63161, 63267, 63370, 63472, 63570, 63667, 63761, 63853, 63942, 64029, 64114, 64196, 64275, 64353, 64427, 64500, 64570, 64637, 64702, 64765, 64825, 64883, 64938, 64991, 65042, 65090, 65135, 65178, 65219, 65257, 65293, 65326, 65357, 65385, 65411, 65435, 65456, 65474, 65490, 65504, 65515, 65523, 65530, 65533, 65535
+};
+
+int16_t GDClass::rsin(int16_t r, uint16_t th) {
+    th >>= 6; // angle 0-123
+    // return int(r * sin((2 * M_PI) * th / 1024.));
+    int th4 = th & 511;
+    if (th4 & 256)
+        th4 = 512 - th4; // 256->256 257->255, etc
+    uint16_t s = pgm_read_word_near(sintab + th4);
+    int16_t p = ((uint32_t)s * r) >> 16;
+    if (th & 512)
+        p = -p;
+    return p;
+}
+
+int16_t GDClass::rcos(int16_t r, uint16_t th) {
+    return rsin(r, th + 0x4000);
+}
+
+void GDClass::polar(int &x, int &y, int16_t r, uint16_t th) {
+    x = (int)(-rsin(r, th));
+    y = (int)( rcos(r, th));
+}
+
+// >>> [int(round(1024 * math.atan(i / 256.) / math.pi)) for i in range(256)]
+static PROGMEM unsigned char atan8[] = {
+    0,1,3,4,5,6,8,9,10,11,13,14,15,17,18,19,20,22,23,24,25,27,28,29,30,32,33,34,36,37,38,39,41,42,43,44,46,47,48,49,51,52,53,54,55,57,58,59,60,62,63,64,65,67,68,69,70,71,73,74,75,76,77,79,80,81,82,83,85,86,87,88,89,91,92,93,94,95,96,98,99,100,101,102,103,104,106,107,108,109,110,111,112,114,115,116,117,118,119,120,121,122,124,125,126,127,128,129,130,131,132,133,134,135,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,177,178,179,180,181,182,183,184,185,186,187,188,188,189,190,191,192,193,194,195,195,196,197,198,199,200,201,201,202,203,204,205,206,206,207,208,209,210,211,211,212,213,214,215,215,216,217,218,219,219,220,221,222,222,223,224,225,225,226,227,228,228,229,230,231,231,232,233,234,234,235,236,236,237,238,239,239,240,241,241,242,243,243,244,245,245,246,247,248,248,249,250,250,251,251,252,253,253,254,255,255
+};
+
+uint16_t GDClass::atan2(int16_t y, int16_t x)
+{
+    uint16_t a;
+    uint16_t xx = 0;
+
+    if ((x <= 0) ^ (y > 0)) {
+        int16_t t; t = x; x = y; y = t;
+        xx ^= 0x4000;
+    }
+    if (x <= 0) {
+        x = -x;
+    } else {
+        xx ^= 0x8000;
+    }
+    y = abs(y);
+    if (x > y) {
+        int16_t t; t = x; x = y; y = t;
+        xx ^= 0x3fff;
+    }
+    while ((x | y) & 0xff80) {
+        x >>= 1;
+        y >>= 1;
+    }
+    if (y == 0) {
+        a = 0;
+    } else if (x == y) {
+        a = 0x2000;
+    } else {
+        // assert(x <= y);
+        int r = ((x << 8) / y);
+        // assert(0 <= r);
+        // assert(r < 256);
+        a = pgm_read_byte(atan8 + r) << 5;
+    }
+    a ^= xx;
+    return a;
+}
+
+void GDClass::align(byte n) {
+    while ((n++) & 3)
+        GDTR.cmdbyte(0);
+}
+
+void GDClass::cH(uint16_t v) {
+    GDTR.cmdbyte(v & 0xff);
+    GDTR.cmdbyte((v >> 8) & 0xff);
+}
+
+void GDClass::ch(int16_t v) {
+    cH((uint16_t)v);
+}
+
+void GDClass::cI(uint32_t v) {
+    GDTR.cmd32(v);
+}
+
+void GDClass::cFFFFFF(byte v) {
+    union {
+        uint32_t c;
+        uint8_t b[4];
+    };
+    b[0] = v;
+    b[1] = 0xff;
+    b[2] = 0xff;
+    b[3] = 0xff;
+    GDTR.cmd32(c);
+}
+
+void GDClass::ci(int32_t v) {
+    cI((uint32_t) v);
+}
+
+void GDClass::cs(const char *s) {
+    while (*s) {
+        char c = *s++;
+        GDTR.cmdbyte(c);
+    }
+    GDTR.cmdbyte(0);
+}
+
+void GDClass::copy(const unsigned char *src, int count) {
+    byte a = count & 3;
+    while (count--) {
+        GDTR.cmdbyte(pgm_read_byte_near(src));
+        src++;
+    }
+    align(a);
+}
+
+void GDClass::copyram(byte *src, int count) {
+    byte a = count & 3;
+    GDTR.cmd_n(src, count);
+    align(a);
+}
+
+void GDClass::AlphaFunc(byte func, byte ref) {
+    cI((9UL << 24) | ((func & 7L) << 8) | ((ref & 255L) << 0));
+}
+void GDClass::Begin(byte prim) {
+    cI((31UL << 24) | prim);
+}
+void GDClass::BitmapHandle(byte handle) {
+    cI((5UL << 24) | handle);
+}
+void GDClass::BitmapLayout(byte format, uint16_t linestride, uint16_t height) {
+    // cI((7UL << 24) | ((format & 31L) << 19) | ((linestride & 1023L) << 9) | ((height & 511L) << 0));
+    union {
+        uint32_t c;
+        uint8_t b[4];
+    };
+    b[0] = height;
+    b[1] = (1 & (height >> 8)) | (linestride << 1);
+    b[2] = (7 & (linestride >> 7)) | (format << 3);
+    b[3] = 7;
+    cI(c);
+}
+void GDClass::BitmapSize(byte filter, byte wrapx, byte wrapy, uint16_t width, uint16_t height) {
+    byte fxy = (filter << 2) | (wrapx << 1) | (wrapy);
+    // cI((8UL << 24) | ((uint32_t)fxy << 18) | ((width & 511L) << 9) | ((height & 511L) << 0));
+    union {
+        uint32_t c;
+        uint8_t b[4];
+    };
+    b[0] = height;
+    b[1] = (1 & (height >> 8)) | (width << 1);
+    b[2] = (3 & (width >> 7)) | (fxy << 2);
+    b[3] = 8;
+    cI(c);
+}
+void GDClass::BitmapSource(uint32_t addr) {
+    cI((1UL << 24) | ((addr & 1048575L) << 0));
+}
+void GDClass::BitmapTransformA(int32_t a) {
+    cI((21UL << 24) | ((a & 131071L) << 0));
+}
+void GDClass::BitmapTransformB(int32_t b) {
+    cI((22UL << 24) | ((b & 131071L) << 0));
+}
+void GDClass::BitmapTransformC(int32_t c) {
+    cI((23UL << 24) | ((c & 16777215L) << 0));
+}
+void GDClass::BitmapTransformD(int32_t d) {
+    cI((24UL << 24) | ((d & 131071L) << 0));
+}
+void GDClass::BitmapTransformE(int32_t e) {
+    cI((25UL << 24) | ((e & 131071L) << 0));
+}
+void GDClass::BitmapTransformF(int32_t f) {
+    cI((26UL << 24) | ((f & 16777215L) << 0));
+}
+void GDClass::BlendFunc(byte src, byte dst) {
+    cI((11UL << 24) | ((src & 7L) << 3) | ((dst & 7L) << 0));
+}
+void GDClass::Call(uint16_t dest) {
+    cI((29UL << 24) | ((dest & 2047L) << 0));
+}
+void GDClass::Cell(byte cell) {
+    cI((6UL << 24) | ((cell & 127L) << 0));
+}
+void GDClass::ClearColorA(byte alpha) {
+    cI((15UL << 24) | ((alpha & 255L) << 0));
+}
+void GDClass::ClearColorRGB(byte red, byte green, byte blue) {
+    cI((2UL << 24) | ((red & 255L) << 16) | ((green & 255L) << 8) | ((blue & 255L) << 0));
+}
+void GDClass::ClearColorRGB(uint32_t rgb) {
+    cI((2UL << 24) | (rgb & 0xffffffL));
+}
+void GDClass::Clear(byte c, byte s, byte t) {
+    byte m = (c << 2) | (s << 1) | t;
+    cI((38UL << 24) | m);
+}
+void GDClass::Clear(void) {
+    cI((38UL << 24) | 7);
+}
+void GDClass::ClearStencil(byte s) {
+    cI((17UL << 24) | ((s & 255L) << 0));
+}
+void GDClass::ClearTag(byte s) {
+    cI((18UL << 24) | ((s & 255L) << 0));
+}
+void GDClass::ColorA(byte alpha) {
+    cI((16UL << 24) | ((alpha & 255L) << 0));
+}
+void GDClass::ColorMask(byte r, byte g, byte b, byte a) {
+    cI((32UL << 24) | ((r & 1L) << 3) | ((g & 1L) << 2) | ((b & 1L) << 1) | ((a & 1L) << 0));
+}
+void GDClass::ColorRGB(byte red, byte green, byte blue) {
+    // cI((4UL << 24) | ((red & 255L) << 16) | ((green & 255L) << 8) | ((blue & 255L) << 0));
+    union {
+        uint32_t c;
+        uint8_t b[4];
+    };
+    b[0] = blue;
+    b[1] = green;
+    b[2] = red;
+    b[3] = 4;
+    cI(c);
+}
+void GDClass::ColorRGB(uint32_t rgb) {
+    cI((4UL << 24) | (rgb & 0xffffffL));
+}
+void GDClass::Display(void) {
+    cI((0UL << 24));
+}
+void GDClass::End(void) {
+    cI((33UL << 24));
+}
+void GDClass::Jump(uint16_t dest) {
+    cI((30UL << 24) | ((dest & 2047L) << 0));
+}
+void GDClass::LineWidth(uint16_t width) {
+    cI((14UL << 24) | ((width & 4095L) << 0));
+}
+void GDClass::Macro(byte m) {
+    cI((37UL << 24) | ((m & 1L) << 0));
+}
+void GDClass::PointSize(uint16_t size) {
+    cI((13UL << 24) | ((size & 8191L) << 0));
+}
+void GDClass::RestoreContext(void) {
+    cI((35UL << 24));
+}
+void GDClass::Return(void) {
+    cI((36UL << 24));
+}
+void GDClass::SaveContext(void) {
+    cI((34UL << 24));
+}
+void GDClass::ScissorSize(uint16_t width, uint16_t height) {
+    cI((28UL << 24) | ((width & 1023L) << 10) | ((height & 1023L) << 0));
+}
+void GDClass::ScissorXY(uint16_t x, uint16_t y) {
+    cI((27UL << 24) | ((x & 511L) << 9) | ((y & 511L) << 0));
+}
+void GDClass::StencilFunc(byte func, byte ref, byte mask) {
+    cI((10UL << 24) | ((func & 7L) << 16) | ((ref & 255L) << 8) | ((mask & 255L) << 0));
+}
+void GDClass::StencilMask(byte mask) {
+    cI((19UL << 24) | ((mask & 255L) << 0));
+}
+void GDClass::StencilOp(byte sfail, byte spass) {
+    cI((12UL << 24) | ((sfail & 7L) << 3) | ((spass & 7L) << 0));
+}
+void GDClass::TagMask(byte mask) {
+    cI((20UL << 24) | ((mask & 1L) << 0));
+}
+void GDClass::Tag(byte s) {
+    cI((3UL << 24) | ((s & 255L) << 0));
+}
+void GDClass::Vertex2f(int16_t x, int16_t y) {
+    // x = int(16 * x);
+    // y = int(16 * y);
+    cI((1UL << 30) | ((x & 32767L) << 15) | ((y & 32767L) << 0));
+}
+void GDClass::Vertex2ii(uint16_t x, uint16_t y, byte handle, byte cell) {
+    // cI((2UL << 30) | ((x & 511L) << 21) | ((y & 511L) << 12) | ((handle & 31L) << 7) | ((cell & 127L) << 0));
+    union {
+        uint32_t c;
+        uint8_t b[4];
+    };
+    b[0] = cell | ((handle & 1) << 7);
+    b[1] = (handle >> 1) | (y << 4);
+    b[2] = (y >> 4) | (x << 5);
+    b[3] = (2 << 6) | (x >> 3);
+    cI(c);
+}
+
+void GDClass::fmtcmd(const char *fmt, ...) {
+    va_list ap;
+    va_start(ap, fmt);
+    byte sz = 0;  // Only the low 2 bits matter
+    const char *s;
+
+    while (*fmt)
+        switch (*fmt++) {
+        case 'i':
+        case 'I':
+            cI(va_arg(ap, uint32_t));
+            break;
+        case 'h':
+        case 'H':
+            cH(va_arg(ap, unsigned int));
+            sz += 2;
+            break;
+        case 's':
+            s = va_arg(ap, const char*);
+            cs(s);
+            sz += strlen(s) + 1;
+            break;
+    }
+    align(sz);
+}
+
+void GDClass::cmd_append(uint32_t ptr, uint32_t num) {
+    cFFFFFF(0x1e);
+    cI(ptr);
+    cI(num);
+}
+void GDClass::cmd_bgcolor(uint32_t c) {
+    fmtcmd("II", 0xffffff09UL, c);
+}
+void GDClass::cmd_button(int16_t x, int16_t y, uint16_t w, uint16_t h, byte font, uint16_t options, const char *s) {
+    fmtcmd("IhhhhhHs", 0xffffff0dUL, x, y, w, h, font, options, s);
+}
+void GDClass::cmd_calibrate(void) {
+    cFFFFFF(0x15);
+    cFFFFFF(0xff);
+}
+void GDClass::cmd_clock(int16_t x, int16_t y, int16_t r, uint16_t options, uint16_t h, uint16_t m, uint16_t s, uint16_t ms) {
+    fmtcmd("IhhhHHHHH", 0xffffff14UL, x, y, r, options, h, m, s, ms);
+}
+void GDClass::cmd_coldstart(void) {
+    cFFFFFF(0x32);
+}
+void GDClass::cmd_dial(int16_t x, int16_t y, int16_t r, uint16_t options, uint16_t val) {
+    fmtcmd("IhhhHH", 0xffffff2dUL, x, y, r, options, val);
+}
+void GDClass::cmd_dlstart(void) {
+    cFFFFFF(0x00);
+}
+void GDClass::cmd_fgcolor(uint32_t c) {
+    fmtcmd("II", 0xffffff0aUL, c);
+}
+void GDClass::cmd_gauge(int16_t x, int16_t y, int16_t r, uint16_t options, uint16_t major, uint16_t minor, uint16_t val, uint16_t range) {
+    fmtcmd("IhhhHHHHH", 0xffffff13UL, x, y, r, options, major, minor, val, range);
+}
+void GDClass::cmd_getmatrix(void) {
+    fmtcmd("Iiiiiii", 0xffffff33UL, 0, 0, 0, 0, 0, 0);
+}
+void GDClass::cmd_getprops(uint32_t &ptr, uint32_t &w, uint32_t &h) {
+    cFFFFFF(0x25);
+    ptr = GDTR.getwp();
+    cI(0);
+    w = GDTR.getwp();
+    cI(0);
+    h = GDTR.getwp();
+    cI(0);
+}
+void GDClass::cmd_getptr(void) {
+    fmtcmd("II", 0xffffff23UL, 0);
+}
+void GDClass::cmd_gradcolor(uint32_t c) {
+    fmtcmd("II", 0xffffff34UL, c);
+}
+void GDClass::cmd_gradient(int16_t x0, int16_t y0, uint32_t rgb0, int16_t x1, int16_t y1, uint32_t rgb1) {
+    fmtcmd("IhhIhhI", 0xffffff0bUL, x0, y0, rgb0, x1, y1, rgb1);
+}
+void GDClass::cmd_inflate(uint32_t ptr) {
+    cFFFFFF(0x22);
+    cI(ptr);
+}
+void GDClass::cmd_interrupt(uint32_t ms) {
+    fmtcmd("II", 0xffffff02UL, ms);
+}
+void GDClass::cmd_keys(int16_t x, int16_t y, int16_t w, int16_t h, byte font, uint16_t options, const char*s) {
+    fmtcmd("IhhhhhHs", 0xffffff0eUL, x, y, w, h, font, options, s);
+}
+void GDClass::cmd_loadidentity(void) {
+    cFFFFFF(0x26);
+}
+void GDClass::cmd_loadimage(uint32_t ptr, int32_t options) {
+    fmtcmd("III", 0xffffff24UL, ptr, options);
+}
+void GDClass::cmd_memcpy(uint32_t dest, uint32_t src, uint32_t num) {
+    fmtcmd("IIII", 0xffffff1dUL, dest, src, num);
+}
+void GDClass::cmd_memset(uint32_t ptr, byte value, uint32_t num) {
+    cFFFFFF(0x1b);
+    cI(ptr);
+    cI((uint32_t)value);
+    cI(num);
+}
+uint32_t GDClass::cmd_memcrc(uint32_t ptr, uint32_t num) {
+    cFFFFFF(0x18);
+    cI(ptr);
+    cI(num);
+    uint32_t r = GDTR.getwp();
+    cI(0xFFFFFFFF);
+    return r;
+}
+void GDClass::cmd_memwrite(uint32_t ptr, uint32_t num) {
+    fmtcmd("III", 0xffffff1aUL, ptr, num);
+}
+void GDClass::cmd_regwrite(uint32_t ptr, uint32_t val) {
+    cFFFFFF(0x1a);
+    cI(ptr);
+    cI(4UL);
+    cI(val);
+}
+void GDClass::cmd_number(int16_t x, int16_t y, byte font, uint16_t options, uint32_t n) {
+    // fmtcmd("IhhhHi", 0xffffff2eUL, x, y, font, options, n);
+    cFFFFFF(0x2e);
+    ch(x);
+    ch(y);
+    ch(font);
+    cH(options);
+    ci(n);
+}
+void GDClass::cmd_progress(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t range) {
+    fmtcmd("IhhhhHHH", 0xffffff0fUL, x, y, w, h, options, val, range);
+}
+void GDClass::cmd_regread(uint32_t ptr) {
+    fmtcmd("III", 0xffffff19UL, ptr, 0);
+}
+void GDClass::cmd_rotate(int32_t a) {
+    cFFFFFF(0x29);
+    ci(a);
+}
+void GDClass::cmd_scale(int32_t sx, int32_t sy) {
+    cFFFFFF(0x28);
+    ci(sx);
+    ci(sy);
+}
+void GDClass::cmd_screensaver(void) {
+    cFFFFFF(0x2f);
+}
+void GDClass::cmd_scrollbar(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t size, uint16_t range) {
+    fmtcmd("IhhhhHHHH", 0xffffff11UL, x, y, w, h, options, val, size, range);
+}
+void GDClass::cmd_setfont(byte font, uint32_t ptr) {
+    fmtcmd("III", 0xffffff2bUL, font, ptr);
+}
+void GDClass::cmd_setmatrix(void) {
+    cFFFFFF(0x2a);
+}
+void GDClass::cmd_sketch(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t ptr, uint16_t format) {
+    cFFFFFF(0x30);
+    ch(x);
+    ch(y);
+    cH(w);
+    cH(h);
+    cI(ptr);
+    cI(format);
+}
+void GDClass::cmd_slider(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t options, uint16_t val, uint16_t range) {
+    fmtcmd("IhhhhHHH", 0xffffff10UL, x, y, w, h, options, val, range);
+}
+void GDClass::cmd_snapshot(uint32_t ptr) {
+    fmtcmd("II", 0xffffff1fUL, ptr);
+}
+void GDClass::cmd_spinner(int16_t x, int16_t y, byte style, byte scale) {
+    cFFFFFF(0x16);
+    ch(x);
+    ch(y);
+    cH(style);
+    cH(scale);
+}
+void GDClass::cmd_stop(void) {
+    cFFFFFF(0x17);
+}
+void GDClass::cmd_swap(void) {
+    cFFFFFF(0x01);
+}
+void GDClass::cmd_text(int16_t x, int16_t y, byte font, uint16_t options, const char *s) {
+    // fmtcmd("IhhhHs", 0xffffff0cUL, x, y, font, options, s);
+    cFFFFFF(0x0c);
+    ch(x);
+    ch(y);
+    ch(font);
+    cH(options);
+    cs(s);
+    align(strlen(s) + 1);
+}
+void GDClass::cmd_toggle(int16_t x, int16_t y, int16_t w, byte font, uint16_t options, uint16_t state, const char *s) {
+    fmtcmd("IhhhhHHs", 0xffffff12UL, x, y, w, font, options, state, s);
+}
+void GDClass::cmd_track(int16_t x, int16_t y, uint16_t w, uint16_t h, byte tag) {
+    fmtcmd("Ihhhhh", 0xffffff2cUL, x, y, w, h, tag);
+}
+void GDClass::cmd_translate(int32_t tx, int32_t ty) {
+    cFFFFFF(0x27);
+    ci(tx);
+    ci(ty);
+}
+
+byte GDClass::rd(uint32_t addr) 
+{
+    return GDTR.rd(addr);
+}
+void GDClass::wr(uint32_t addr, uint8_t v) 
+{
+    GDTR.wr(addr, v);
+}
+uint16_t GDClass::rd16(uint32_t addr) 
+{
+    return GDTR.rd16(addr);
+}
+void GDClass::wr16(uint32_t addr, uint16_t v) 
+{
+    GDTR.wr16(addr, v);
+}
+uint32_t GDClass::rd32(uint32_t addr) 
+{
+    return GDTR.rd32(addr);
+}
+void GDClass::wr32(uint32_t addr, uint32_t v) 
+{
+    GDTR.wr32(addr, v);
+}
+void GDClass::wr_n(uint32_t addr, byte *src, uint32_t n) 
+{
+    GDTR.wr_n(addr, src, n);
+}
+
+void GDClass::cmdbyte(uint8_t b) 
+{
+    GDTR.cmdbyte(b);
+}
+void GDClass::cmd32(uint32_t b) {
+    GDTR.cmd32(b);
+}
+void GDClass::finish(void) {
+    GDTR.finish();
+}
+void GDClass::get_accel(int &x, int &y, int &z) 
+{
+    static float f[3];
+    
+    for (byte i = 0; i < 3; i++) 
+    {
+        float a = accel[i]->read() * 1000;
+        // Serial.print(a, DEC); Serial.print(" ");
+        int s = (-160 * ((int)(a - 376)) >> 6);
+        f[i] = (((int)(3 * f[i])) >> 2) + (s >> 2);
+    }
+    // DEBUGOUT();
+    x = f[2];
+    y = f[1];
+    z = f[0];
+}
+void GDClass::get_inputs(void) {
+    GDTR.finish();
+    byte *bi = (byte*)&inputs;
+    GDTR.rd_n(bi, REG_TRACKER, 4);
+    GDTR.rd_n(bi + 4, REG_TOUCH_RZ, 13);
+    GDTR.rd_n(bi + 17, REG_TAG, 1);
+#if DUMP_INPUTS
+    for (size_t i = 0; i < sizeof(inputs); i++) {
+        DEBUGOUT("%x", bi[i]);
+        DEBUGOUT(" ");
+    }
+#endif
+}
+void GDClass::bulkrd(uint32_t a) {
+    GDTR.bulk(a);
+}
+void GDClass::resume(void) {
+    GDTR.resume();
+}
+void GDClass::__end(void) {
+    GDTR.__end();
+}
+void GDClass::play(uint8_t instrument, uint8_t note) {
+    wr16(REG_SOUND, (note << 8) | instrument);
+    wr(REG_PLAY, 1);
+}
+void GDClass::sample(uint32_t start, uint32_t len, uint16_t freq, uint16_t format, int loop) 
+{
+    wr32(REG_PLAYBACK_START, start);
+    wr32(REG_PLAYBACK_LENGTH, len);
+    wr16(REG_PLAYBACK_FREQ, freq);
+    wr(REG_PLAYBACK_FORMAT, format);
+    wr(REG_PLAYBACK_LOOP, loop);
+    wr(REG_PLAYBACK_PLAY, 1);
+}
+void GDClass::reset() {
+    GDTR.__end();
+    GDTR.wr(REG_CPURESET, 1);
+    GDTR.wr(REG_CPURESET, 0);
+    GDTR.resume();
+}
+
+
+// Load named file from storage
+// returns 0 on failure (e.g. file not found), 1 on success
+
+byte GDClass::load(const char *filename, void (*progress)(long, long))
+{
+#if defined(RASPBERRY_PI)
+    FILE *f = fopen(filename, "rb");
+    if (!f) {
+        perror(filename);
+        exit(1);
+    }
+    byte buf[512];
+    int n;
+    while ((n = fread(buf, 1, 512, f)) > 0) {
+        GDTR.cmd_n(buf, (n + 3) & ~3);
+    }
+    fclose(f);
+    return 1;
+#else
+    __end();
+    Reader* r = new Reader(GDTR.SPI(), SD);
+    if (r->openfile(filename)) 
+    {
+        byte buf[512];
+        while (r->offset < r->size) 
+        {
+            uint16_t n = min(512, r->size - r->offset);
+            n = (n + 3) & ~3;   // force 32-bit alignment
+            r->readsector(buf);
+
+            resume();
+            if (progress)
+                (*progress)(r->offset, r->size);
+            copyram(buf, n);
+            GDTR.stop();
+        }
+        resume();
+        delete r;
+        return 1;
+    }
+    resume();
+    delete r;
+    return 0;
+#endif
+}
+
+// Generated by mk_bsod.py. Blue screen with 'ERROR' text
+static const unsigned char __bsod[31] = {
+    0, 255, 255, 255, 255, 0, 0, 2, 7, 0, 0, 38, 12, 255, 255, 255, 240,
+    0, 120, 0, 28, 0, 0, 6, 69, 82, 82, 79, 82, 33, 0
+};
+// "Cannot open file" text
+static const unsigned char __bsod_badfile[31] = {
+    12, 255, 255, 255, 240, 0, 148, 0, 28, 0, 0, 6, 67, 97, 110, 110, 111,
+    116, 32, 111, 112, 101, 110, 32, 102, 105, 108, 101, 0, 0, 0
+};
+
+void GDClass::safeload(const char *filename)
+{
+    if (!load(filename)) {
+        copy(__bsod, sizeof(__bsod));
+        copy(__bsod_badfile, sizeof(__bsod_badfile));
+        cmd_text(240, 176, 28, OPT_CENTER, filename);
+        swap();
+        for (;;)
+            ;
+    }
+}
\ No newline at end of file