/* TinyCard v4 - see http://www.technoblogy.com/show?54EI David Johnson-Davies - www.technoblogy.com - 14th May 2026 ATtiny3224 @ 10 MHz (internal); BOD Disabled CC BY 4.0 Licensed under a Creative Commons Attribution 4.0 International license: http://creativecommons.org/licenses/by/4.0/ */ #define SDCARD // Comment out if no SD card socket #include #include #ifdef SDCARD #include #endif const unsigned long Timeout = (unsigned long)5*60*1000; // Timeout in millis = 5 minutes // Arduino pin numbers const int Enable = PIN_PB3; // PB3 - enable low const int Backlight = PIN_PB2; // PB2 Backlight - can do analogue // TFT display const int TFT_MOSI = PIN_PA1; // PA1 const int TFT_SCLK = PIN_PA3; // PA3 const int TFT_CS = PIN_PA4; // PA4 TFT display SPI chip select pin const int TFT_DC = PIN_PA7; // PA7 TFT display data/command select pin // SD card const int SD_CS = PIN_PB0; // SD Card Select = D3 const int SD_MISO = PIN_PA2; // PA2 for SD card // Display parameters - Adafruit 1.8" 160x128 display or AliExpress 1.8" 160x128 display (blue PCB) const int xsize = 160, ysize = 128, xoff = 0, yoff = 0, invert = 0, rotate = 6, bgr = 0; // Character set with hints ********************************************** const uint8_t CharMap[96+16][7] PROGMEM = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00 }, { 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, 0x00 }, { 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00, 0x00 }, { 0x23, 0x13, 0x08, 0x64, 0x62, 0x00, 0x00 }, { 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x00 }, { 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x00 }, { 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x00 }, { 0x00, 0x41, 0x22, 0x1C, 0x00, 0x00, 0x00 }, { 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00, 0x00 }, { 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00 }, { 0x00, 0x80, 0x70, 0x30, 0x00, 0x00, 0x00 }, { 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00 }, { 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00 }, { 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00 }, { 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x00 }, { 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00, 0x00 }, { 0x72, 0x49, 0x49, 0x49, 0x46, 0x00, 0x00 }, { 0x21, 0x41, 0x49, 0x4D, 0x33, 0x5a, 0x00 }, // 3 { 0x18, 0x14, 0x12, 0x7F, 0x10, 0x5E, 0x00 }, // 4 { 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, 0x00 }, { 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00, 0x00 }, { 0x41, 0x21, 0x11, 0x09, 0x07, 0x00, 0x00 }, { 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00 }, { 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00 }, { 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x40, 0x34, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00 }, { 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00 }, { 0x00, 0x41, 0x22, 0x14, 0x08, 0x00, 0x00 }, { 0x02, 0x01, 0x59, 0x09, 0x06, 0x00, 0x00 }, { 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x87, 0x00 }, // @ { 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00, 0x00 }, { 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00, 0x00 }, { 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00, 0x00 }, { 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x00 }, { 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00, 0x00 }, { 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00, 0x00 }, { 0x3E, 0x41, 0x41, 0x51, 0x73, 0x00, 0x00 }, { 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x00 }, { 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00, 0x00 }, { 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00, 0x00 }, { 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00, 0x00 }, { 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00 }, { 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x2E, 0x7E }, // M { 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x2C, 0x75 }, // N { 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x00 }, { 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00, 0x00 }, { 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00, 0x00 }, { 0x7F, 0x09, 0x19, 0x29, 0x46, 0x37, 0x00 }, // R { 0x26, 0x49, 0x49, 0x49, 0x32, 0x00, 0x00 }, { 0x03, 0x01, 0x7F, 0x01, 0x03, 0x00, 0x00 }, { 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00, 0x00 }, { 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00, 0x00 }, { 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00, 0x00 }, { 0x63, 0x14, 0x08, 0x14, 0x63, 0x00, 0x00 }, { 0x03, 0x04, 0x78, 0x04, 0x03, 0x00, 0x00 }, { 0x61, 0x51, 0x49, 0x45, 0x43, 0x00, 0x00 }, { 0x00, 0x7F, 0x41, 0x41, 0x41, 0x00, 0x00 }, { 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00 }, { 0x00, 0x41, 0x41, 0x41, 0x7F, 0x00, 0x00 }, { 0x04, 0x02, 0x01, 0x02, 0x04, 0x00, 0x00 }, { 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00 }, { 0x00, 0x03, 0x07, 0x08, 0x00, 0x00, 0x00 }, { 0x20, 0x54, 0x54, 0x78, 0x40, 0x00, 0x00 }, { 0x7F, 0x28, 0x44, 0x44, 0x38, 0x00, 0x00 }, { 0x38, 0x44, 0x44, 0x44, 0x28, 0x00, 0x00 }, { 0x38, 0x44, 0x44, 0x28, 0x7F, 0x00, 0x00 }, { 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x00 }, { 0x00, 0x08, 0x7E, 0x09, 0x02, 0x00, 0x00 }, { 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x8a, 0x00 }, // g { 0x7F, 0x08, 0x04, 0x04, 0x78, 0x27, 0x00 }, // h { 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00, 0x00 }, { 0x20, 0x40, 0x40, 0x3D, 0x00, 0x00, 0x00 }, { 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 }, { 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, 0x00 }, { 0x7C, 0x04, 0x78, 0x04, 0x78, 0x00, 0x00 }, { 0x7C, 0x08, 0x04, 0x04, 0x78, 0x00, 0x00 }, { 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00 }, { 0xFC, 0x18, 0x24, 0x24, 0x18, 0x00, 0x00 }, { 0x18, 0x24, 0x24, 0x18, 0xFC, 0x00, 0x00 }, { 0x7C, 0x08, 0x04, 0x04, 0x08, 0x27, 0x00 }, // r { 0x48, 0x54, 0x54, 0x54, 0x24, 0x00, 0x00 }, { 0x04, 0x04, 0x3F, 0x44, 0x24, 0x00, 0x00 }, { 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00, 0x00 }, { 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00, 0x00 }, { 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00, 0x00 }, { 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00 }, { 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00, 0x00 }, { 0x44, 0x64, 0x54, 0x4C, 0x44, 0x14, 0x89 }, // z { 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x41, 0x36, 0x08, 0x00, 0x00, 0x00 }, { 0x02, 0x01, 0x02, 0x04, 0x02, 0x00, 0x00 }, { 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00 }, // 127 = Cursor { 0x7F, 0x5B, 0x41, 0x5F, 0x7F, 0x00, 0x00 }, // 128 = [1] { 0x7F, 0x45, 0x55, 0x51, 0x7F, 0x00, 0x00 }, // [2] { 0x7F, 0x55, 0x55, 0x41, 0x7F, 0x00, 0x00 }, // [3] { 0x7F, 0x71, 0x77, 0x43, 0x7F, 0x00, 0x00 }, // [4] { 0x7F, 0x51, 0x55, 0x45, 0x7F, 0x00, 0x00 }, // [5] { 0x7F, 0x41, 0x55, 0x45, 0x7F, 0x00, 0x00 }, // [6] { 0x7F, 0x7D, 0x7D, 0x41, 0x7F, 0x00, 0x00 }, // [7] { 0x7F, 0x41, 0x55, 0x41, 0x7F, 0x00, 0x00 }, // [8] { 0x7F, 0x71, 0x75, 0x41, 0x7F, 0x00, 0x00 }, // 136 = [9] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x7F, 0x77, 0x77, 0x77, 0x7F, 0x00, 0x00 }, // 140 = [-] { 0x7F, 0x7D, 0x45, 0x71, 0x7F, 0x46, 0x56 }, // [?] { 0x7F, 0x77, 0x63, 0x77, 0x7F, 0x00, 0x00 }, // [+] { 0x7F, 0x6B, 0x6B, 0x6B, 0x7F, 0x00, 0x00 }, // [=] }; const uint16_t BigMap[3][12] PROGMEM = { { 0x3FFF, 0x3003, 0x3003, 0x3FFF, 0x3003, 0x3003, 0x33F3, 0x3003, 0x3003, 0x3FFF, 0, 0 }, // 137 = [10] { 0x3FFF, 0x3FFF, 0x3003, 0x3003, 0x3FFF, 0x3FFF, 0x3003, 0x3003, 0x3FFF, 0x3FFF, 0, 0 }, // [11] { 0x3FFF, 0x3003, 0x3003, 0x3FFF, 0x3033, 0x3033, 0x3333, 0x3303, 0x3303, 0x3FFF, 0, 0 }, // 139 = [12] }; const uint8_t F1code = 128; const uint8_t F10code = 137; const uint8_t F12code = 139; const uint8_t MinusCode = 140; const uint8_t QmarkCode = 141; const uint8_t PlusCode = 142; const uint8_t EqualsCode = 143; const uint8_t EOL = 13*12; // X position at end of line // TFT colour display ********************************************** uint16_t const White = 0xffff, Black = 0x0000, Grey = 0x52aa, Cyan = 0x07ff; // 33% grey uint16_t const Red = 0xf800, Yellow = 0xffe0, Green = 0x07e0, Blue = 0x001f; int const Leading = 8; int const CASET = 0x2A; // Define column address int const RASET = 0x2B; // Define row address int const RAMWR = 0x2C; // Write to display RAM int const RAMRD = 0x2E; // Read from display RAM // Globals - current plot position and colours int Xpos, Ypos; int Foreground = White; int Background = Black; int Scale = 1; // Text scale boolean Smooth = true; boolean Hints = true; // Send a byte to the display void SendByte (uint8_t d) { SPI.transfer(d); } // Send a command to the display void SendCommand (uint8_t c) { digitalWrite(TFT_DC, LOW); SendByte(c); digitalWrite(TFT_DC, HIGH); } // Send a command followed by two data words void SendCommand2 (uint8_t c, uint16_t d1, uint16_t d2) { digitalWrite(TFT_DC, LOW); SendByte(c); digitalWrite(TFT_DC, HIGH); SendByte(d1>>8); SendByte(d1); SendByte(d2>>8); SendByte(d2); } void InitDisplay () { pinMode(TFT_DC, OUTPUT); pinMode(TFT_CS, OUTPUT); digitalWrite(TFT_DC, HIGH); // Data digitalWrite(TFT_CS, LOW); SPI.begin(); SendCommand(0x01); // Software reset delay(250); // delay 250 ms SendCommand(0x36); SendByte(rotate<<5 | bgr<<3); // Set orientation, rgb/bgr SendCommand(0x3A); SendByte(0x55); // Set color mode - 16-bit SendCommand(0x20+invert); // Invert SendCommand(0x11); // Out of sleep mode delay(150); digitalWrite(TFT_CS, HIGH); } void DisplayOn () { digitalWrite(TFT_CS, LOW); SendCommand(0x29); // Display on delay(150); digitalWrite(TFT_CS, HIGH); } void ClearDisplay () { digitalWrite(TFT_CS, LOW); SendCommand2(CASET, yoff, yoff + ysize - 1); SendCommand2(RASET, xoff, xoff + xsize - 1); SendCommand(0x3A); SendByte(0x03); // 12-bit colour - it's faster SendCommand(RAMWR); for (int i=0; i<3840; i++) { SPI.transfer(0); SPI.transfer(0); SPI.transfer(0); SPI.transfer(0); SPI.transfer(0); SPI.transfer(0); SPI.transfer(0); SPI.transfer(0); } SendCommand(0x3A); SendByte(0x05); // Back to 16-bit colour digitalWrite(TFT_CS, HIGH); } unsigned int Colour (int r, int g, int b) { return (r & 0xf8)<<8 | (g & 0xfc)<<3 | b>>3; } // Move current plot position to x,y void MoveTo (int x, int y) { Xpos = x; Ypos = y; } // Plot point at x,y void PlotPoint (int x, int y) { digitalWrite(TFT_CS, LOW); SendCommand2(CASET, yoff+y, yoff+y); SendCommand2(RASET, xoff+x, xoff+x); SendCommand(RAMWR); SendByte(Foreground>>8); SendByte(Foreground & 0xff); digitalWrite(TFT_CS, HIGH); } // Draw a line to x,y void DrawTo (int x, int y) { digitalWrite(TFT_CS, LOW); int sx, sy, e2, err; int dx = abs(x - Xpos); int dy = abs(y - Ypos); if (Xpos < x) sx = 1; else sx = -1; if (Ypos < y) sy = 1; else sy = -1; err = dx - dy; for (;;) { PlotPoint(Xpos, Ypos); if (Xpos==x && Ypos==y) return; e2 = err<<1; if (e2 > -dy) { err = err - dy; Xpos = Xpos + sx; } if (e2 < dx) { err = err + dx; Ypos = Ypos + sy; } } digitalWrite(TFT_CS, HIGH); } void FillRect (int w, int h) { digitalWrite(TFT_CS, LOW); SendCommand2(CASET, Ypos+yoff, Ypos+yoff+h-1); SendCommand2(RASET, Xpos+xoff, Xpos+xoff+w-1); SendCommand(RAMWR); for (int i=0; i>8); SendByte(Foreground & 0xff); } } digitalWrite(TFT_CS, HIGH); } void DrawRect (int w, int h) { int x1 = Xpos, y1 = Ypos; DrawTo(x1+w-1, y1); DrawTo(x1+w-1, y1+h-1); DrawTo(x1, y1+h-1); DrawTo(x1, y1); } // Converts bit pattern abcdefgh into a0b0c0d0e0f0g0h uint16_t Stretch (uint16_t x) { x = (x & 0xF0)<<4 | (x & 0x0F); x = (x<<2 | x) & 0x3333; x = (x<<1 | x) & 0x5555; return x<<1 | x; } void WriteColumn (uint8_t bits) { uint16_t colour; for (uint8_t i=0; i<8; i++) { if (bits>>(7-i) & 1) colour = Foreground; else colour = Background; SPI.transfer(colour>>8); SPI.transfer(colour & 0xFF); } } void WriteColumns (uint16_t left, uint16_t right) { WriteColumn(left>>8); WriteColumn(left); WriteColumn(right>>8); WriteColumn(right); } void PlotChar (uint8_t c) { digitalWrite(TFT_CS, LOW); SendCommand2(CASET, yoff+Ypos, yoff+Ypos+8*Scale-1); SendCommand2(RASET, xoff+Xpos, xoff+Xpos+6*Scale-1); SendCommand(RAMWR); if (c < F10code || c > F12code) { uint8_t col0 = pgm_read_byte(&CharMap[c-32][0]); uint16_t col0L, col0R, col1L, col1R; col0L = Stretch(col0); col0R = col0L; for (uint8_t col = 1 ; col < 5; col++) { uint8_t col1 = pgm_read_byte(&CharMap[c-32][col]); col1L = Stretch(col1); col1R = col1L; if (Scale == 1) WriteColumn(col0); // Smoothing else { if (Smooth) { for (int i=6; i>=0; i--) { for (int j=1; j<3; j++) { if (((col0>>i & 0b11) == (3-j)) && ((col1>>i & 0b11) == j)) { col0R = col0R | 1<<((i*2)+j); col1L = col1L | 1<<((i*2)+3-j); } } } } WriteColumns(col0L, col0R); col0L = col1L; col0R = col1R; } col0 = col1; } if (Scale == 1) WriteColumn(col0); else { WriteColumns(col0L, col0R); if (Hints) { uint8_t hint = pgm_read_byte(&CharMap[c-32][5]); if (hint) PlotPoint((hint>>4)+Xpos, (hint & 0x0f)+Ypos); hint = pgm_read_byte(&CharMap[c-32][6]); if (hint) PlotPoint((hint>>4)+Xpos, (hint & 0x0f)+Ypos); } } } else { uint16_t colour; for (int xx=0; xx<12; xx++) { int bits = pgm_read_word(&BigMap[c-137][xx]); for (int yy=0; yy<16; yy++) { if (bits>>(15-yy) & 1) colour = Foreground; else colour = Background; SPI.transfer(colour>>8); SPI.transfer(colour & 0xFF); } } } Xpos = Xpos + 6*Scale; digitalWrite(TFT_CS, HIGH); } // Plot text from a char array starting at the current plot position void PlotChars (char *s) { while (*s) PlotChar(*s++); } // Plot text starting at the current plot position void PlotText(PGM_P p) { while (1) { char c = pgm_read_byte(p++); if (c == 0) return; PlotChar(c); } } void PlotInt (int n) { bool lead = false; if (n < 0) { PlotChar('-'); n = - n; } for (int d=10000; d>0; d = d/10) { char j = (n/d) % 10; if (j!=0 || lead || d==1) { PlotChar(j + '0'); lead = true; } } } void PlotHex4 (uint16_t i) { uint16_t p = 0x1000; for (uint16_t d=p; d>0; d=d/16) { uint16_t j = i/d; PlotChar((j<10) ? j+'0' : j+'W'); i = i - j*d; } } void TestChart () { MoveTo(0,0); DrawTo(xsize-1, 0); DrawTo(xsize-1, ysize-1); DrawTo(0, ysize-1); DrawTo(0, 0); Scale = 8; MoveTo((xsize-40)/2, (ysize-64)/2); PlotChar('F'); Scale = 1; } uint8_t Centre (uint8_t x) { return (xsize - x*12)/2; } void MessageDialog (PGM_P p) { int len = strlen_P(p), width = (len+1)*12, margin = (xsize - width)/2; MoveTo(margin, ysize-76); Foreground = Black; FillRect(width, 42); Foreground = White; DrawRect(width, 42); Foreground = Cyan; MoveTo(margin+6, ysize-64); PlotText(p); Foreground = White; delay(1000); } // Read screen (only AliExpress display) ********************************************** uint16_t GetPixel (uint16_t x, uint16_t y) { uint32_t pixel = 0; digitalWrite(TFT_CS, LOW); SendCommand2(CASET, yoff+y, yoff+y); SendCommand2(RASET, xoff+x, xoff+x); SendCommand(RAMRD); SPI0.CTRLA &= ~(SPI_ENABLE_bm); // Disable SPI peripheral pinMode(TFT_MOSI, INPUT); for (uint8_t i=0; i<9; i++) { digitalWrite(TFT_SCLK, HIGH); digitalWrite(TFT_SCLK, LOW); } for (uint8_t i=0; i<24; i++) { digitalWrite(TFT_SCLK, HIGH); pixel = pixel<<1 | digitalRead(TFT_MOSI); digitalWrite(TFT_SCLK, LOW); } pinMode(TFT_MOSI, OUTPUT); digitalWrite(TFT_CS, HIGH); SPI0.CTRLA |= SPI_ENABLE_bm; // Enable SPI peripheral return ((pixel & 0xf80000)>>8 | (pixel & 0xfc00)>>5 | (pixel & 0xf8)>>3); } // Keyboard ********************************************** const int ClockMask = PIN6_bm; // PA6 = D+ const int DataMask = PIN5_bm; // PA5 = D- const int KeymapSize = 0x84; volatile uint16_t KeyBuf = 0; // Control codes const int F1key = 14; const int F12key = F1key+11; // 25 const int Escape = 27; const int Tab = 9; const int NumLock = 28; const int ScrollLock = 29; const int CapsLock = 30; void SetupKeyboard() { PORTA.DIRCLR = DataMask | ClockMask; // Inputs PORTA.PIN5CTRL = PORT_PULLUPEN_bm; // Pullup on PA5 PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // Pullup on PA6 } const uint8_t Keymap[] PROGMEM = // Without shift " \026 \022\020\016\017\031 \027\025\023\021\011` q1 zsaw2 " // 0x00 to 0x1F " cxde43 vftr5 nbhgy6 mju78 " // 0x20 to 0x3F " ,kio09 ./l;p- \' [= \036 \015] \\ " // 0x40 to 0x5F " \010 1 47 0.2568\033\034\030+3-*9\035 \024" // 0x60 to 0x83 // With shift " \026 \022\020\016\017\031 \027\025\023\021\011~ Q! ZSAW@ " // 0x00 to 0x1F " CXDE$# VFTR% NBHGY^ MJU&* " // 0x20 to 0x3F " ?L:P_ \" {+ \036 \015} | " // 0x40 to 0x5F " \010 1 47 0.2568\033\034\030+3-*9\035 \024"; // 0x60 to 0x83 volatile uint16_t ScanCode = 0, ScanBit = 1; ISR(PORTA_PORT_vect) { static bool Break = 0, Modifier = 0, Shift = 0, Alt = 0, Ctrl = 0; PORTA.INTFLAGS = ClockMask; // Clear CLOCK interrupt flag if (PORTA.IN & DataMask) ScanCode = ScanCode | ScanBit; ScanBit = ScanBit << 1; if (ScanBit != 0x800) return; // ScanCode incomplete // Process scan code if ((ScanCode & 0x401) != 0x400) return; // Invalid start/stop bit uint16_t s = (ScanCode & 0x1FE) >> 1; ScanCode = 0, ScanBit = 1; if (s == 0xAA) return; // BAT completion code if (s == 0xF0) { Break = 1; return; } if (s == 0xE0) { Modifier = 1; return; } if (Modifier && Break) { if (s == 0x11) Alt = 0; // Right Alt released if (s == 0x14) Ctrl = 0; // Right Ctrl released Break = 0; Modifier = 0; return; } else if (Modifier) { if (s == 0x11) Alt = 1; // Right Alt pressed if (s == 0x14) Ctrl = 1; // Right Ctrl pressed Modifier = 0; return; } else if (Break) { if ((s == 0x12) || (s == 0x59)) Shift = 0; // Left or Right Shift released if (s == 0x11) Alt = 0; // Left Alt released if (s == 0x14) Ctrl = 0; // Left Ctrl released Break = 0; return; } if ((s == 0x12) || (s == 0x59)) { Shift = 1; return; } // Left or Right Shift pressed if (s == 0x11) { Alt = 1; return; } // Left Alt pressed if (s == 0x14) { Ctrl = 1; return; } // Left Ctrl pressed uint8_t c = pgm_read_byte(&Keymap[s + KeymapSize*Shift]); if (c == 32 && s != 0x29) return; KeyBuf = (Ctrl&1)<<15 | (Alt&1)<<14 | c; return; } // TinyCard memory structure ************************************* const int MaxMessage = 1248; // Maximum total message size const int Cards = 12; // Selected with F1 to F12 const int BufferOffset = 2*(Cards+1) + 4; const int PackSize = BufferOffset + MaxMessage; const int LineWidth = 13; // Characters per display line uint8_t Clipboard[104]; uint8_t ClipEndPtr = 0; struct { union { struct { uint8_t PackName; // Currently selected pack uint8_t Card; // Currently selected card uint16_t EndPtr; // Points after last character uint16_t StartPtr[Cards+1]; uint8_t Buffer[MaxMessage]; }; uint8_t Copybuf[PackSize]; }; } Pack; enum modes { Edit, Play, Update }; volatile uint8_t Mode = Edit; const int Packs = 8; uint8_t BuiltinPack[] = { // Number Game 0x01, 0x00, 0x27, 0x01, 0x00, 0x00, 0x38, 0x00, 0x4c, 0x00, 0x61, 0x00, 0x80, 0x00, 0x92, 0x00, 0xab, 0x00, 0xc0, 0x00, 0xce, 0x00, 0xde, 0x00, 0x0e, 0x01, 0x27, 0x01, 0x27, 0x01, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x47, 0x61, 0x6d, 0x65, 0x20, 0x54, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x77, 0x68, 0x6f, 0x6c, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x31, 0x20, 0x74, 0x6f, 0x20, 0x32, 0x30, 0x2e, 0x20, 0x81, 0x4e, 0x65, 0x78, 0x74, 0x2e, 0x49, 0x73, 0x20, 0x69, 0x74, 0x20, 0x82, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x6f, 0x72, 0x20, 0x86, 0x6f, 0x64, 0x64, 0x3f, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x79, 0x20, 0x62, 0x79, 0x20, 0x39, 0x2e, 0x20, 0x83, 0x4e, 0x65, 0x78, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x69, 0x67, 0x69, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x67, 0x65, 0x74, 0x68, 0x65, 0x72, 0x2e, 0x20, 0x84, 0x4e, 0x65, 0x78, 0x74, 0x2e, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x32, 0x2e, 0x20, 0x85, 0x4e, 0x65, 0x78, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x37, 0x21, 0x20, 0x80, 0x54, 0x72, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x79, 0x20, 0x62, 0x79, 0x20, 0x32, 0x2e, 0x20, 0x87, 0x4e, 0x65, 0x78, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x20, 0x31, 0x30, 0x2e, 0x20, 0x88, 0x4e, 0x65, 0x78, 0x74, 0x2e, 0x48, 0x61, 0x6c, 0x76, 0x65, 0x20, 0x69, 0x74, 0x2e, 0x20, 0x89, 0x4e, 0x65, 0x78, 0x74, 0x2e, 0x53, 0x75, 0x62, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x74, 0x68, 0x6f, 0x75, 0x67, 0x68, 0x74, 0x20, 0x6f, 0x66, 0x2e, 0x20, 0x8a, 0x4e, 0x65, 0x78, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x35, 0x21, 0x20, 0x80, 0x54, 0x72, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, // Terminator 0xff, // The Island 0x02, 0x02, 0xe8, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x9b, 0x00, 0xe8, 0x00, 0x24, 0x01, 0x7b, 0x01, 0xc3, 0x01, 0x19, 0x02, 0x19, 0x02, 0x19, 0x02, 0x19, 0x02, 0x19, 0x02, 0x19, 0x02, 0x54, 0x68, 0x65, 0x20, 0x49, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x0d, 0x57, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x73, 0x6c, 0x61, 0x6e, 0x64, 0x2e, 0x20, 0x47, 0x6f, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x81, 0x66, 0x6f, 0x72, 0x65, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x82, 0x62, 0x65, 0x61, 0x63, 0x68, 0x3f, 0x41, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x73, 0x74, 0x72, 0x75, 0x67, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x65, 0x73, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x67, 0x65, 0x74, 0x20, 0x62, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x73, 0x6e, 0x61, 0x6b, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x65, 0x2e, 0x20, 0x80, 0x54, 0x72, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x59, 0x6f, 0x75, 0x20, 0x77, 0x61, 0x6c, 0x6b, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x65, 0x61, 0x63, 0x68, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x61, 0x63, 0x68, 0x20, 0x61, 0x20, 0x72, 0x69, 0x76, 0x65, 0x72, 0x2e, 0x20, 0x83, 0x53, 0x77, 0x69, 0x6d, 0x20, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x84, 0x77, 0x61, 0x6c, 0x6b, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x3f, 0x41, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x73, 0x77, 0x69, 0x6d, 0x20, 0x61, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x67, 0x65, 0x74, 0x20, 0x65, 0x61, 0x74, 0x65, 0x6e, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, 0x63, 0x72, 0x6f, 0x63, 0x6f, 0x64, 0x69, 0x6c, 0x65, 0x2e, 0x20, 0x80, 0x54, 0x72, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x59, 0x6f, 0x75, 0x20, 0x77, 0x61, 0x6c, 0x6b, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x69, 0x76, 0x65, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x72, 0x65, 0x61, 0x63, 0x68, 0x20, 0x61, 0x20, 0x70, 0x61, 0x6c, 0x6d, 0x20, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x20, 0x85, 0x44, 0x69, 0x67, 0x20, 0x62, 0x65, 0x73, 0x69, 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x65, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x86, 0x63, 0x6c, 0x69, 0x6d, 0x62, 0x20, 0x69, 0x74, 0x3f, 0x59, 0x6f, 0x75, 0x20, 0x64, 0x69, 0x67, 0x20, 0x62, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, 0x72, 0x65, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x74, 0x72, 0x65, 0x61, 0x73, 0x75, 0x72, 0x65, 0x20, 0x63, 0x68, 0x65, 0x73, 0x74, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x72, 0x69, 0x63, 0x68, 0x21, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x41, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x6c, 0x69, 0x6d, 0x62, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x63, 0x6f, 0x6e, 0x75, 0x74, 0x20, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x73, 0x20, 0x6c, 0x6f, 0x6f, 0x73, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x69, 0x74, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x65, 0x61, 0x64, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x64, 0x69, 0x65, 0x2e, 0x20, 0x80, 0x54, 0x72, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, // Terminator 0xff, // Animal Quiz 0x00, 0x0a, 0xcc, 0x01, 0x00, 0x00, 0x35, 0x00, 0x62, 0x00, 0x8a, 0x00, 0xb8, 0x00, 0xe5, 0x00, 0x14, 0x01, 0x3a, 0x01, 0x67, 0x01, 0x85, 0x01, 0xab, 0x01, 0xcc, 0x01, 0xfb, 0x01, 0x41, 0x6e, 0x69, 0x6d, 0x61, 0x6c, 0x20, 0x51, 0x75, 0x69, 0x7a, 0x0d, 0x49, 0x73, 0x20, 0x61, 0x20, 0x70, 0x6f, 0x72, 0x70, 0x6f, 0x69, 0x73, 0x65, 0x20, 0x61, 0x20, 0x87, 0x62, 0x69, 0x72, 0x64, 0x2c, 0x20, 0x87, 0x66, 0x69, 0x73, 0x68, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x81, 0x6d, 0x61, 0x6d, 0x6d, 0x61, 0x6c, 0x3f, 0x44, 0x6f, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x88, 0x66, 0x6f, 0x75, 0x72, 0x2c, 0x20, 0x82, 0x73, 0x69, 0x78, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x88, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x6c, 0x65, 0x67, 0x73, 0x3f, 0x49, 0x73, 0x20, 0x61, 0x20, 0x70, 0x65, 0x6e, 0x67, 0x75, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x83, 0x62, 0x69, 0x72, 0x64, 0x2c, 0x20, 0x89, 0x66, 0x69, 0x73, 0x68, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x89, 0x6d, 0x61, 0x6d, 0x6d, 0x61, 0x6c, 0x3f, 0x44, 0x6f, 0x20, 0x6d, 0x6f, 0x73, 0x74, 0x20, 0x63, 0x72, 0x61, 0x62, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x84, 0x73, 0x69, 0x78, 0x2c, 0x20, 0x84, 0x65, 0x69, 0x67, 0x68, 0x74, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x85, 0x74, 0x65, 0x6e, 0x20, 0x6c, 0x65, 0x67, 0x73, 0x3f, 0x4e, 0x6f, 0x74, 0x20, 0x62, 0x61, 0x64, 0x20, 0x2d, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x2e, 0x20, 0x80, 0x54, 0x72, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x57, 0x65, 0x6c, 0x6c, 0x20, 0x64, 0x6f, 0x6e, 0x65, 0x2c, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x21, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x53, 0x6f, 0x72, 0x72, 0x79, 0x2c, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x2e, 0x20, 0x80, 0x54, 0x72, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x73, 0x20, 0x61, 0x20, 0x63, 0x72, 0x6f, 0x63, 0x6f, 0x64, 0x69, 0x6c, 0x65, 0x20, 0x61, 0x20, 0x88, 0x72, 0x65, 0x70, 0x74, 0x69, 0x6c, 0x65, 0x2c, 0x20, 0x8a, 0x6d, 0x61, 0x6d, 0x6d, 0x61, 0x6c, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x8a, 0x66, 0x69, 0x73, 0x68, 0x3f, 0x49, 0x73, 0x20, 0x61, 0x20, 0x73, 0x68, 0x61, 0x72, 0x6b, 0x20, 0x61, 0x20, 0x8b, 0x6d, 0x61, 0x6d, 0x6d, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x89, 0x66, 0x69, 0x73, 0x68, 0x3f, 0x49, 0x73, 0x20, 0x61, 0x20, 0x62, 0x61, 0x74, 0x20, 0x61, 0x20, 0x86, 0x62, 0x69, 0x72, 0x64, 0x2c, 0x20, 0x86, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x84, 0x6d, 0x61, 0x6d, 0x6d, 0x61, 0x6c, 0x3f, 0x49, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x63, 0x68, 0x20, 0x61, 0x20, 0x8b, 0x6d, 0x61, 0x6d, 0x6d, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x8b, 0x62, 0x69, 0x72, 0x64, 0x3f, 0x44, 0x6f, 0x65, 0x73, 0x20, 0x61, 0x20, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x86, 0x66, 0x6f, 0x75, 0x72, 0x2c, 0x20, 0x86, 0x73, 0x69, 0x78, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x86, 0x65, 0x69, 0x67, 0x68, 0x74, 0x20, 0x6c, 0x65, 0x67, 0x73, 0x3f, // Terminator 0xff, // Colour Game 0x00, 0x07, 0x43, 0x01, 0x00, 0x00, 0x4d, 0x00, 0x71, 0x00, 0x96, 0x00, 0xb9, 0x00, 0xdb, 0x00, 0xfd, 0x00, 0x20, 0x01, 0x43, 0x01, 0x63, 0x01, 0x88, 0x01, 0xa9, 0x01, 0xca, 0x01, 0x43, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x47, 0x61, 0x6d, 0x65, 0x0d, 0x54, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x2e, 0x20, 0x48, 0x6f, 0x77, 0x20, 0x6d, 0x61, 0x6e, 0x79, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x69, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x3f, 0x20, 0x81, 0x3c, 0x5f, 0x35, 0x2c, 0x20, 0x82, 0x35, 0x2c, 0x20, 0x83, 0x3e, 0x5f, 0x35, 0x3f, 0x44, 0x6f, 0x65, 0x73, 0x20, 0x69, 0x74, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x33, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x3f, 0x20, 0x88, 0x59, 0x65, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x89, 0x6e, 0x6f, 0x3f, 0x44, 0x6f, 0x65, 0x73, 0x20, 0x69, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x22, 0x62, 0x22, 0x20, 0x2d, 0x20, 0x84, 0x79, 0x65, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x85, 0x6e, 0x6f, 0x3f, 0x44, 0x6f, 0x65, 0x73, 0x20, 0x69, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x22, 0x65, 0x22, 0x20, 0x2d, 0x20, 0x86, 0x79, 0x65, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x87, 0x6e, 0x6f, 0x3f, 0x49, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x22, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x22, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 0x49, 0x20, 0x67, 0x75, 0x65, 0x73, 0x73, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x22, 0x67, 0x72, 0x65, 0x65, 0x6e, 0x22, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 0x49, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x22, 0x6f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x22, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 0x49, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x22, 0x79, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x22, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 0x49, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x22, 0x72, 0x65, 0x64, 0x22, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 0x44, 0x6f, 0x65, 0x73, 0x20, 0x69, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x22, 0x70, 0x22, 0x20, 0x2d, 0x20, 0x8a, 0x79, 0x65, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x8b, 0x6e, 0x6f, 0x2e, 0x49, 0x20, 0x67, 0x75, 0x65, 0x73, 0x73, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x22, 0x70, 0x69, 0x6e, 0x6b, 0x22, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, 0x49, 0x20, 0x67, 0x75, 0x65, 0x73, 0x73, 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x22, 0x62, 0x6c, 0x75, 0x65, 0x22, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x2e, // Terminator 0xff, // Twisty Maze 0x00, 0x05, 0xcc, 0x01, 0x00, 0x00, 0x32, 0x00, 0x82, 0x00, 0xd2, 0x00, 0x22, 0x01, 0x72, 0x01, 0xcc, 0x01, 0xcc, 0x01, 0xcc, 0x01, 0xcc, 0x01, 0xcc, 0x01, 0xcc, 0x01, 0xcc, 0x01, 0x54, 0x77, 0x69, 0x73, 0x74, 0x79, 0x20, 0x4d, 0x61, 0x7a, 0x65, 0x0d, 0x46, 0x69, 0x6e, 0x64, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x77, 0x61, 0x79, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x61, 0x7a, 0x65, 0x21, 0x20, 0x81, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x2e, 0x59, 0x6f, 0x75, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x7a, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x77, 0x69, 0x73, 0x74, 0x79, 0x20, 0x6c, 0x69, 0x74, 0x74, 0x6c, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x20, 0x47, 0x6f, 0x20, 0x82, 0x6e, 0x6f, 0x72, 0x74, 0x68, 0x2c, 0x20, 0x83, 0x65, 0x61, 0x73, 0x74, 0x2c, 0x20, 0x83, 0x73, 0x6f, 0x75, 0x74, 0x68, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x82, 0x77, 0x65, 0x73, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x6d, 0x61, 0x7a, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6c, 0x69, 0x74, 0x74, 0x6c, 0x65, 0x20, 0x74, 0x77, 0x69, 0x73, 0x74, 0x79, 0x20, 0x70, 0x61, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x20, 0x47, 0x6f, 0x20, 0x84, 0x6e, 0x6f, 0x72, 0x74, 0x68, 0x2c, 0x20, 0x81, 0x65, 0x61, 0x73, 0x74, 0x2c, 0x20, 0x81, 0x73, 0x6f, 0x75, 0x74, 0x68, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x83, 0x77, 0x65, 0x73, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x74, 0x77, 0x69, 0x73, 0x74, 0x79, 0x20, 0x6c, 0x69, 0x74, 0x74, 0x6c, 0x65, 0x20, 0x6d, 0x61, 0x7a, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x61, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x20, 0x47, 0x6f, 0x20, 0x81, 0x6e, 0x6f, 0x72, 0x74, 0x68, 0x2c, 0x20, 0x84, 0x65, 0x61, 0x73, 0x74, 0x2c, 0x20, 0x82, 0x73, 0x6f, 0x75, 0x74, 0x68, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x81, 0x77, 0x65, 0x73, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x20, 0x61, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x74, 0x77, 0x69, 0x73, 0x74, 0x79, 0x20, 0x6d, 0x61, 0x7a, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6c, 0x69, 0x74, 0x74, 0x6c, 0x65, 0x20, 0x70, 0x61, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x20, 0x47, 0x6f, 0x20, 0x83, 0x6e, 0x6f, 0x72, 0x74, 0x68, 0x2c, 0x20, 0x82, 0x65, 0x61, 0x73, 0x74, 0x2c, 0x20, 0x85, 0x73, 0x6f, 0x75, 0x74, 0x68, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x84, 0x77, 0x65, 0x73, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x20, 0x62, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x61, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x73, 0x74, 0x65, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x75, 0x6e, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x27, 0x76, 0x65, 0x20, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x54, 0x77, 0x69, 0x73, 0x74, 0x79, 0x20, 0x4d, 0x61, 0x7a, 0x65, 0x21, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, // Terminator 0xff, // Go Rock Collecting 0x02, 0x0b, 0x43, 0x03, 0x00, 0x00, 0x48, 0x00, 0x89, 0x00, 0xd0, 0x00, 0x10, 0x01, 0x51, 0x01, 0x98, 0x01, 0xe1, 0x01, 0x24, 0x02, 0x6b, 0x02, 0xae, 0x02, 0xf7, 0x02, 0x43, 0x03, 0x47, 0x6f, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x0d, 0x53, 0x65, 0x65, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x6d, 0x61, 0x6e, 0x79, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x66, 0x69, 0x6e, 0x64, 0x2e, 0x20, 0x81, 0x53, 0x74, 0x61, 0x72, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x79, 0x20, 0x70, 0x61, 0x74, 0x68, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x68, 0x79, 0x73, 0x74, 0x73, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x20, 0x8e, 0x8e, 0x82, 0x50, 0x69, 0x63, 0x6b, 0x5f, 0x75, 0x70, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x79, 0x20, 0x70, 0x61, 0x74, 0x68, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x8d, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x67, 0x6f, 0x20, 0x85, 0x86, 0x4c, 0x65, 0x66, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x87, 0x88, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x61, 0x76, 0x65, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x75, 0x72, 0x71, 0x75, 0x6f, 0x69, 0x73, 0x65, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x20, 0x8e, 0x84, 0x50, 0x69, 0x63, 0x6b, 0x5f, 0x75, 0x70, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x63, 0x61, 0x76, 0x65, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x8d, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x67, 0x6f, 0x20, 0x81, 0x82, 0x4c, 0x65, 0x66, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x89, 0x8a, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x70, 0x65, 0x62, 0x62, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x61, 0x63, 0x68, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x6f, 0x62, 0x73, 0x69, 0x64, 0x69, 0x61, 0x6e, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x20, 0x8e, 0x86, 0x50, 0x69, 0x63, 0x6b, 0x5f, 0x75, 0x70, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x70, 0x65, 0x62, 0x62, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x61, 0x63, 0x68, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x8d, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x67, 0x6f, 0x20, 0x83, 0x84, 0x4c, 0x65, 0x66, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x87, 0x88, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x72, 0x69, 0x76, 0x65, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x70, 0x79, 0x72, 0x69, 0x74, 0x65, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x20, 0x8e, 0x88, 0x50, 0x69, 0x63, 0x6b, 0x5f, 0x75, 0x70, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x72, 0x69, 0x76, 0x65, 0x72, 0x20, 0x62, 0x61, 0x6e, 0x6b, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x8d, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x67, 0x6f, 0x20, 0x83, 0x84, 0x4c, 0x65, 0x66, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x89, 0x8a, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x20, 0x68, 0x69, 0x6c, 0x6c, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x70, 0x69, 0x65, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x71, 0x75, 0x61, 0x72, 0x74, 0x7a, 0x20, 0x68, 0x65, 0x72, 0x65, 0x2e, 0x20, 0x8e, 0x8a, 0x50, 0x69, 0x63, 0x6b, 0x5f, 0x75, 0x70, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x73, 0x6d, 0x61, 0x6c, 0x6c, 0x20, 0x68, 0x69, 0x6c, 0x6c, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x8d, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x67, 0x6f, 0x20, 0x81, 0x82, 0x8b, 0x4c, 0x65, 0x66, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x85, 0x86, 0x8b, 0x52, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x59, 0x6f, 0x75, 0x27, 0x76, 0x65, 0x20, 0x61, 0x72, 0x72, 0x69, 0x76, 0x65, 0x64, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x20, 0x68, 0x6f, 0x6d, 0x65, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x27, 0x76, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x8d, 0x20, 0x72, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x2d, 0x20, 0x77, 0x65, 0x6c, 0x6c, 0x20, 0x64, 0x6f, 0x6e, 0x65, 0x21, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, // Terminator 0xff, // Lucky Fifteen 0x02, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x6c, 0x00, 0xa0, 0x00, 0xd2, 0x00, 0x0a, 0x01, 0x3e, 0x01, 0x77, 0x01, 0xa8, 0x01, 0xcf, 0x01, 0xf5, 0x01, 0xf5, 0x01, 0xf5, 0x01, 0x4c, 0x75, 0x63, 0x6b, 0x79, 0x20, 0x46, 0x69, 0x66, 0x74, 0x65, 0x65, 0x6e, 0x20, 0x54, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x31, 0x20, 0x74, 0x6f, 0x20, 0x31, 0x35, 0x2e, 0x8f, 0x20, 0x81, 0x49, 0x27, 0x6d, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x21, 0x49, 0x73, 0x20, 0x69, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x3f, 0x0d, 0x32, 0x20, 0x33, 0x20, 0x36, 0x20, 0x37, 0x20, 0x31, 0x30, 0x20, 0x31, 0x31, 0x20, 0x31, 0x34, 0x20, 0x31, 0x35, 0x0d, 0x82, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x83, 0x4e, 0x6f, 0x2e, 0x8e, 0x8e, 0x49, 0x73, 0x20, 0x69, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x3f, 0x0d, 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x31, 0x32, 0x20, 0x31, 0x33, 0x20, 0x31, 0x34, 0x20, 0x31, 0x35, 0x0d, 0x84, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x85, 0x4e, 0x6f, 0x2e, 0x49, 0x73, 0x20, 0x69, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x3f, 0x0d, 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x20, 0x31, 0x32, 0x20, 0x31, 0x33, 0x20, 0x31, 0x34, 0x20, 0x31, 0x35, 0x0d, 0x84, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x85, 0x4e, 0x6f, 0x2e, 0x8e, 0x8e, 0x8e, 0x8e, 0x49, 0x73, 0x20, 0x69, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x3f, 0x0d, 0x38, 0x20, 0x39, 0x20, 0x31, 0x30, 0x20, 0x31, 0x31, 0x20, 0x31, 0x32, 0x20, 0x31, 0x33, 0x20, 0x31, 0x34, 0x20, 0x31, 0x35, 0x0d, 0x86, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x87, 0x4e, 0x6f, 0x2e, 0x49, 0x73, 0x20, 0x69, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x3f, 0x0d, 0x38, 0x20, 0x39, 0x20, 0x31, 0x30, 0x20, 0x31, 0x31, 0x20, 0x31, 0x32, 0x20, 0x31, 0x33, 0x20, 0x31, 0x34, 0x20, 0x31, 0x35, 0x0d, 0x86, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x87, 0x4e, 0x6f, 0x2e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x49, 0x73, 0x20, 0x69, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x3f, 0x0d, 0x31, 0x20, 0x33, 0x20, 0x35, 0x20, 0x37, 0x20, 0x39, 0x20, 0x31, 0x31, 0x20, 0x31, 0x33, 0x20, 0x31, 0x35, 0x0d, 0x88, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x89, 0x4e, 0x6f, 0x2e, 0x49, 0x73, 0x20, 0x69, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x3f, 0x0d, 0x31, 0x20, 0x33, 0x20, 0x35, 0x20, 0x37, 0x20, 0x39, 0x20, 0x31, 0x31, 0x20, 0x31, 0x33, 0x20, 0x31, 0x35, 0x0d, 0x88, 0x59, 0x65, 0x73, 0x2c, 0x20, 0x89, 0x4e, 0x6f, 0x2e, 0x8e, 0x49, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x6b, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x69, 0x73, 0x20, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, // Terminator 0xff, // Rock Paper Scissors 0x02, 0x0a, 0xd2, 0x02, 0x00, 0x00, 0x55, 0x00, 0x8f, 0x00, 0xd8, 0x00, 0x24, 0x01, 0x6b, 0x01, 0xa4, 0x01, 0xf5, 0x01, 0x41, 0x02, 0x8c, 0x02, 0xd2, 0x02, 0xd2, 0x02, 0xd2, 0x02, 0x52, 0x6f, 0x63, 0x6b, 0x20, 0x50, 0x61, 0x70, 0x65, 0x72, 0x20, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x0d, 0x4c, 0x65, 0x74, 0x27, 0x73, 0x20, 0x70, 0x6c, 0x61, 0x79, 0x20, 0x61, 0x20, 0x67, 0x61, 0x6d, 0x65, 0x2e, 0x20, 0x44, 0x6f, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 0x84, 0x85, 0x86, 0x52, 0x6f, 0x63, 0x6b, 0x2c, 0x20, 0x81, 0x82, 0x83, 0x50, 0x61, 0x70, 0x65, 0x72, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x87, 0x88, 0x89, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x3f, 0x49, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x50, 0x61, 0x70, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x64, 0x72, 0x61, 0x77, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x2e, 0x20, 0x50, 0x61, 0x70, 0x65, 0x72, 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x20, 0x2d, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x77, 0x69, 0x6e, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x8e, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x20, 0x63, 0x68, 0x6f, 0x73, 0x65, 0x20, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x2e, 0x20, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x20, 0x63, 0x75, 0x74, 0x20, 0x50, 0x61, 0x70, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x49, 0x20, 0x77, 0x69, 0x6e, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x8c, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x20, 0x63, 0x68, 0x6f, 0x73, 0x65, 0x20, 0x50, 0x61, 0x70, 0x65, 0x72, 0x2e, 0x20, 0x50, 0x61, 0x70, 0x65, 0x72, 0x20, 0x77, 0x72, 0x61, 0x70, 0x73, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x20, 0x2d, 0x20, 0x49, 0x20, 0x77, 0x69, 0x6e, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x8c, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x20, 0x2d, 0x20, 0x44, 0x72, 0x61, 0x77, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x2e, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x20, 0x62, 0x6c, 0x75, 0x6e, 0x74, 0x73, 0x20, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x20, 0x2d, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x77, 0x69, 0x6e, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x8e, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x20, 0x63, 0x68, 0x6f, 0x73, 0x65, 0x20, 0x50, 0x61, 0x70, 0x65, 0x72, 0x2e, 0x20, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x20, 0x63, 0x75, 0x74, 0x73, 0x20, 0x50, 0x61, 0x70, 0x65, 0x72, 0x20, 0x2d, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x77, 0x69, 0x6e, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x8e, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x2e, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x20, 0x62, 0x6c, 0x75, 0x6e, 0x74, 0x73, 0x20, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x20, 0x2d, 0x20, 0x49, 0x20, 0x77, 0x69, 0x6e, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x8c, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, 0x49, 0x27, 0x76, 0x65, 0x20, 0x67, 0x6f, 0x74, 0x20, 0x53, 0x63, 0x69, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x20, 0x2d, 0x20, 0x74, 0x68, 0x61, 0x74, 0x27, 0x73, 0x20, 0x61, 0x20, 0x64, 0x72, 0x61, 0x77, 0x21, 0x20, 0x59, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x20, 0x8d, 0x2e, 0x20, 0x80, 0x50, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x3f, // Terminator 0xff, }; char ChooseBuiltin () { MoveTo(0, ysize-108); Foreground = Black; FillRect(160, 90); Foreground = White; DrawRect(160, 90); Foreground = Cyan; MoveTo(Centre(10), ysize-48); PlotText(PSTR("Load pack:")); Foreground = White; MoveTo(Centre(Packs), ysize-72); for (int i=1; i<=Packs; i++) PlotInt(i); Foreground = Cyan; MoveTo(Centre(12), ysize-96); PlotText(PSTR("Press 1 to ")); PlotInt(Packs); for (;;) { uint16_t c = WaitForInput(); if (c >= '1' && c <= '0'+Packs) { Foreground = Red; MoveTo(Centre(Packs)+12*(c-'1'), ysize-72); PlotChar(c); Foreground = Cyan; MoveTo(8, ysize-96); PlotText(PSTR(" Loading... ")); delay(500); return c; } else if (c == ScrollLock) { // Save screen DimBmpSave(); } else if (c == 0 || c == Escape || c == ' ') return 0; } } // SD Card interface ********************************************** #ifdef SDCARD File SDfile; void SDSave (char c) { char filename[] = "*.PAK"; filename[0] = c; SD.begin(SD_CS); SDfile = SD.open(filename, O_RDWR | O_CREAT | O_TRUNC); // Overwrite if (SDfile) { JoinBuf(Pack.Card); if (SDfile.write(Pack.Copybuf, BufferOffset+Pack.StartPtr[Cards])) { SplitBuf(Pack.Card); SDfile.close(); } else MessageDialog(PSTR("Save error")); } else { MessageDialog(PSTR("File error")); } SD.end(); ShowCard(Edit); } void SDLoad (char c) { char filename[] = "*.PAK"; filename[0] = c; SD.begin(SD_CS); SDfile = SD.open(filename); if (SDfile) { SDfile.read(Pack.Copybuf, BufferOffset); SDfile.read(Pack.Copybuf+BufferOffset, BufferOffset+Pack.StartPtr[Cards]); SDfile.close(); Pack.Card = 0; SplitBuf(Pack.Card); } else { MessageDialog(PSTR("File error")); } SD.end(); ShowCard(Edit); } void SDExport () { char filename[] = "EXPORTA.TXT"; SD.begin(SD_CS); while (SD.exists(filename)) { filename[6]++; } SDfile = SD.open(filename, FILE_WRITE); if (SDfile) { MessageDialog(PSTR("Exporting...")); JoinBuf(Pack.Card); for (uint8_t card = 0; card < Cards; card++) { SDfile.print("Card "); SDfile.println(card+1); for (uint16_t i=Pack.StartPtr[card]; i= F1code && c <= EqualsCode) { SDfile.print('['); if (c >= F1code && c <= F12code) SDfile.print(c-F1code+1); else if (c == MinusCode) SDfile.print('-'); else if (c == QmarkCode) SDfile.print('?'); else if (c == PlusCode) SDfile.print('+'); else if (c == EqualsCode) SDfile.print('='); SDfile.print(']'); } else SDfile.print((char)c); } SDfile.println(); SDfile.println(); } SDfile.close(); SplitBuf(Pack.Card); } else { MessageDialog(PSTR("File error")); } SD.end(); ShowCard(Edit); } char ChooseFile (bool save) { SD.begin(SD_CS); MoveTo(0, ysize-116); Foreground = Black; FillRect(160, 106); Foreground = White; DrawRect(160, 106); Foreground = Cyan; if (save) { MoveTo(Centre(10), ysize-40); PlotText(PSTR("Save pack:")); Foreground = Grey; MoveTo(3, ysize-64); PlotText(PSTR("ABCDEFGHIJKLM")); MoveTo(3, ysize-80); PlotText(PSTR("NOPQRSTUVWXYZ")); } else { MoveTo(Centre(10), ysize-40); PlotText(PSTR("Load pack:")); } Foreground = White; char test[] = "*.PAK"; for (char c='A'; c<='Z'; c++) { test[0] = c; if (SD.exists(test)) { MoveTo(3+12*((c-'A')%13), ysize-(64+16*((c-'A')/13))); PlotChar(c); } } SD.end(); Foreground = Cyan; MoveTo(8, ysize-104); PlotText(PSTR("Press A to Z")); for (;;) { uint8_t c = toupper(WaitForInput()); if (c >= 'A' && c <= 'Z') { Foreground = Red; MoveTo(3+12*((c-'A')%13), ysize-(64+16*((c-'A')/13))); PlotChar(c); Foreground = Cyan; MoveTo(8, ysize-104); if (save) PlotText(PSTR(" Saving... ")); else PlotText(PSTR(" Loading... ")); delay(500); return c; } else if (c == ScrollLock) { // Save screen DimBmpSave(); } else if (c == 0 || c == Escape || c == ' ') { ShowCard(false); return 0; } } } #endif // TinyCard program ********************************************** volatile int Scroll = 0; const int MaxButtons = 4; const uint16_t ButtonColour[MaxButtons] = { Red, Yellow, Green, Blue }; uint16_t Button[MaxButtons]; // Button destinations uint8_t LastButton; // Number of buttons on card const int MaxBrightness = 128; // Backlight - maximum 255 int Score = 0; // Game score void SplitBuf (int card) { Pack.EndPtr = Pack.StartPtr[card+1]; int tomove = Pack.StartPtr[Cards] - Pack.StartPtr[card+1]; int distance = MaxMessage - Pack.StartPtr[Cards]; // Slide characters up by 'tomove' and fill with spaces for (int p=0; p= 9) { MoveTo(xsize-5, ysize-8); PlotChar('0'+(1+card)/10); MoveTo(xsize-5, ysize-16); PlotChar('0'+(1+card)%10); } else { MoveTo(xsize-5, ysize-8); PlotChar('0'+(1+card)%10); } Foreground = White; Scale = 2; } void HideCursor() { if (Xpos != EOL) { PlotChar(' '); Xpos = Xpos - 6*Scale; } } void ShowCursor () { if (Xpos == EOL) { // End of current line if (Ypos == 0) return; // End of card int ytemp = Ypos; MoveTo(0, Ypos-16); PlotChar(0x7F); // Start of next line MoveTo(EOL, ytemp); } else { PlotChar(0x7F); Xpos = Xpos - 6*Scale; } } void ShowHelp () { PGM_P Help[14][2] = { PSTR("Esc"), PSTR("Switch to Edit/Play"), PSTR("In Edit mode:"), PSTR(""), PSTR("F1 to F12"), PSTR("Show card 1-12"), PSTR("Alt F1 to F12"), PSTR("Enter code"), PSTR("Alt + or -"), PSTR("Score +/- 1"), PSTR("Alt ="), PSTR("Score = 0"), PSTR("Alt ?"), PSTR("Show score"), PSTR("Ctrl X"), PSTR("Cut card to clip"), PSTR("Ctrl C"), PSTR("Copy card to clip"), PSTR("Ctrl V"), PSTR("Paste from clip"), PSTR("Ctrl L"), PSTR("Load pack from file"), PSTR("Ctrl S"), PSTR("Save pack to file"), PSTR("Ctrl B"), PSTR("Load built-in pack"), PSTR("Ctrl N"), PSTR("Start a new pack") }; ClearDisplay(); Foreground = Cyan; MoveTo(54,ysize-16); PlotText(PSTR("Help")); Scale = 1; for (int i=0; i<14; i++) { if (strlen_P(Help[i][1])) Foreground = Green; else Foreground = Cyan; MoveTo(0,ysize-24-i*8); PlotText(Help[i][0]); Foreground = White; PlotChar(' '); PlotText(Help[i][1]); } Scale = 2; uint8_t c = WaitForInput(); if (c == ScrollLock) DimBmpSave(); ShowCard(Edit); } uint8_t IntWidth (int n) { uint8_t w = 1; if (n < 0) { n = -n; w++; } while (n > 9) { n = n/10; w++; } return w; } void ShowCard (uint8_t mode) { if (mode != Update) ClearDisplay(); if (mode == Edit) PlotCardNumber(Pack.Card); MoveTo(0, ysize-16); LastButton = 0; for (int i=0; i= F1code && c <= EqualsCode); bool wordbreak = (c == ' ' || c == '\r' || i-last >= LineWidth || i == Pack.EndPtr); if ((playFkey || wordbreak) && (last < i)) { // Flush word? if (i-last > spaces) { // No room for word if (mode == Update) { // Blank to end of line Foreground = Black; MoveTo((LineWidth-spaces)*12-1, Ypos); FillRect(spaces*12, 16); Foreground = White; } MoveTo(0, Ypos-16); spaces = LineWidth; // Move to start of next line } for (uint16_t j=last; j 0) { spaces--; PlotChar(' '); } // Space between words else { MoveTo(0, Ypos-16); spaces = LineWidth; // New line } } Foreground = White; if (i-last >= LineWidth) last = i; else last = i+1; // Don't insert space at break if (Button[LastButton]) LastButton++; // Move to next button } if (mode == Play && c >= F1code && c <= F12code) { if (LastButton < MaxButtons) { Foreground = ButtonColour[LastButton]; // Change colour Button[LastButton] |= 1<<(c-F1code); // Add destination for button } last = i+1; // Skip F code } else if (mode == Play && c >= MinusCode && c <= EqualsCode) { if (c == EqualsCode) Score = 0; else Score = Score + c - QmarkCode; if (c == QmarkCode) { if (spaces < IntWidth(Score)+1) { MoveTo(0, Ypos-16); spaces = LineWidth; } PlotInt(Score); spaces = spaces - IntWidth(Score) - 1; if (Pack.Buffer[i+1] == ' ') { PlotChar(' '); i++; } // No extra space } last = i+1; // Skip F code } if (c == '\r') { // Hard return MoveTo(0, Ypos-16); spaces = LineWidth; } i++; // Next character } if (Ypos >= 16 && mode == Update) { // Blank next line int xtemp = Xpos, ytemp = Ypos; Foreground = Black; MoveTo(0, Ypos-16); FillRect(LineWidth*12, 16); Foreground = White; MoveTo(xtemp, ytemp); } if (mode != Play) ShowCursor(); } void ProcessKey (uint8_t c, bool ctrl, bool alt) { // Play mode *************************************************************** if (Mode == Play) { if (c == Escape) { // Escape key goes to edit mode Mode = Edit; ShowCard(Edit); return; } if (c == ScrollLock) { // Save screen DimBmpSave(); return; } // Edit mode *************************************************************** } else if (Mode == Edit) { // These characters don't affect the cursor if (c == Escape) { // Escape key goes to edit mode Mode = Play; Score = 0; // Start with zero score. ShowCard(Play); return; } if (!alt && c >= F1key && c <= F12key) { // 17 to 26 if (Pack.Card == c - F1key) return; // Card = 0 to 9 JoinBuf(Pack.Card); Pack.Card = c - F1key; SplitBuf(Pack.Card); ShowCard(Edit); return; } if (ctrl) { // Ctrl commands if (c == 'b') { // Built-in game char c = ChooseBuiltin(); if (c) { int p=0; for (int i=0; i Pack.StartPtr[Pack.Card]) Pack.EndPtr--; } else if (Pack.EndPtr < Pack.StartPtr[Pack.Card+1] && !(Xpos == 13*12 && Ypos == 0)) { // Room? uint8_t scorepos = (c == '-') ? 1 : (c == '?') ? 2 : (c == '+') ? 3 : (c == '=') ? 4 : 0 ; if (alt && c>=F1key && c<=F12key) c = c - F1key + F1code;// Convert Alt-Fkeys to codes else if (alt && scorepos) c = scorepos + MinusCode -1; // Code for -, ?, +, and = Pack.Buffer[Pack.EndPtr++] = c; } ShowCard(Update); ShowCursor(); } } // Buttons ********************************************** const int ButtonPin = 6; // PB1 = AIN10 int ButtonRead () { return 5 - (analogRead(ButtonPin) * 5 + 512)/1024; // 1 to 5 or 0 = no press } // Save screen in BMP format ********************************************** void DimBmpSave () { #ifdef SDCARD analogWrite(Backlight, 64); bmpSave(); analogWrite(Backlight, MaxBrightness); #endif } #ifdef SDCARD // Write two bytes, least significant byte first void writeTwo (uint16_t word) { SDfile.write(word & 0xFF); SDfile.write((word >> 8) & 0xFF); } // Write four bytes, least significant byte first void writeFour (uint32_t word) { SDfile.write(word & 0xFF); SDfile.write((word >> 8) & 0xFF); SDfile.write((word >> 16) & 0xFF); SDfile.write((word >> 24) & 0xFF); } void bmpSave () { uint16_t width = xsize, height = ysize; char filename[] = "IMAGEA.BMP"; SD.begin(SD_CS); while (SD.exists(filename)) { filename[5]++; } SDfile = SD.open(filename, FILE_WRITE); if (!SDfile) { MessageDialog(PSTR("BMP save error")); SD.end(); return; } // // File header: 14 bytes SDfile.write('B'); SDfile.write('M'); writeFour(14+40+width*height*2); // File size in bytes writeFour(0); writeFour(14+40); // Offset to image data from start // // Image header: 40 bytes writeFour(40); // Header size writeFour(width); // Image width writeFour(height); // Image height writeTwo(1); // Planes writeTwo(16); // Bits per pixel writeFour(0); // Compression (none) writeFour(0); // Image size (0 for uncompressed) writeFour(0); // Preferred X resolution (ignore) writeFour(0); // Preferred Y resolution (ignore) writeFour(0); // Colour map entries (ignore) writeFour(0); // Important colours (ignore) // // Image data: width * height * 2 bytes for (uint16_t y=0; y>1 | (rgb & 0x1F)); // Convert to 555 format } } SDfile.close(); SD.end(); } #endif // Setup ********************************************** void ResetPack () { for (int p=0; p<=Cards; p++) Pack.StartPtr[p] = 0; Pack.Card = 0; } void DimBacklight () { for (uint8_t b=MaxBrightness; b>0; b--) { analogWrite(Backlight, b); delay(4); } } void SetupSleep () { set_sleep_mode(SLEEP_MODE_PWR_DOWN); } void GoToSleep () { MessageDialog(PSTR("Sleeping...")); DimBacklight(); SPI.end(); digitalWrite(Enable, HIGH); // Disable display, keyboard, and SD card // TFT display signals pinMode(TFT_MOSI, INPUT); pinMode(TFT_SCLK, INPUT); pinMode(TFT_CS, INPUT); pinMode(TFT_DC, INPUT); // SD card signals pinMode(SD_MISO, INPUT); pinMode(SD_CS, INPUT); // Keyboard signals PORTA.PIN5CTRL = 0; // Turn off DATA pullup PORTA.PIN6CTRL = 0; // Turn off CLOCK interrupt and pullup PORTA.DIRSET = DataMask | ClockMask; // Clock and DATA low outputs // Buttons ADC0.CTRLA = 0; // Turn off ADC PORTB.PIN1CTRL = PORT_ISC_LEVEL_gc; // Add pin change interrupt to Buttons pin PB1 sleep_enable(); sleep_cpu(); while (digitalRead(ButtonPin) == LOW); // Wait for ON/OFF button release PORTB.PIN1CTRL = 0; // Turn off pin change interrupt on Buttons pin PB1 ADC0.CTRLA = ADC0.CTRLA | 1; // Turn on ADC SetupKeyboard(); digitalWrite(Enable, LOW); // Enable display and keyboard delay(500); // Let 5V supply settle down ScanCode = 0, ScanBit = 1; // Reset keyboard scan PORTA.PIN6CTRL = PORT_ISC_FALLING_gc | PORT_PULLUPEN_bm; // Add pin change interrupt to CLOCK InitDisplay(); ClearDisplay(); DisplayOn(); pinMode(SD_CS, OUTPUT); digitalWrite(SD_CS, HIGH); // SD Card analogWrite(Backlight, MaxBrightness); // Turn on backlight } // Just used to bring us out of sleep ISR (PORTB_PORT_vect) { PORTB.INTFLAGS = PIN1_bm; // Clear interrupt flag } // Setup ********************************************** void setup() { pinMode(Backlight, OUTPUT); digitalWrite(Backlight, LOW); SetupKeyboard(); pinMode(Enable, OUTPUT); digitalWrite(Enable, LOW); // Enable display and keyboard delay(500); // Let 5V supply settle down PORTA.PIN6CTRL = PORT_ISC_FALLING_gc | PORT_PULLUPEN_bm; // Add pin change interrupt to CLOCK pinMode(SD_CS, OUTPUT); digitalWrite(SD_CS, HIGH); // SD Card SetupSleep(); InitDisplay(); ClearDisplay(); DisplayOn(); Scale = 2; // Big text ResetPack(); SplitBuf(Pack.Card); Mode = Edit; analogWrite(Backlight, MaxBrightness); // Turn on backlight MessageDialog(PSTR("TinyCard v4")); ShowCard(Edit); } uint16_t WaitForInput () { unsigned long timer = millis(); do { // Is there a key waiting to be processed? uint16_t c = KeyBuf; if (c) { timer = millis(); // Stay awake KeyBuf = 0; return c; // character > 5 } // Is there a button press? uint16_t button = ButtonRead(); if (button && (button <= LastButton)) { while (ButtonRead()); // Wait for button release timer = millis(); // Stay awake return button; // Button = 1 to 5 } } while (millis()-timer < Timeout && ButtonRead() != 5); GoToSleep(); return 0; } // Index of a random '1' bit in n, which must be non=zero; uses GCC builtin functions int RandomBitIndex (uint16_t n) { if (n == 0) return 12; // Shouldn't be possible uint8_t count = __builtin_popcount(n); // Number of '1' bits uint8_t r = random(count); // Random 0 to count-1 while (r--) n = n & ~(1 << __builtin_ctz(n)); // Unset lowest '1' bit return __builtin_ctz(n); // Index of lowest '1' bit } void loop () { uint16_t c = WaitForInput(); if (c == 0) { ShowCard(Mode); // Return from sleep } else if (c > 5) { // Character key ProcessKey(c, c>>15 & 1, c>>14 & 1); } else { // Button uint8_t go = RandomBitIndex(Button[c-1]); // Index of a random '1' bit JoinBuf(Pack.Card); Pack.Card = go; SplitBuf(Pack.Card); ShowCard(Play); } }